Repository navigation
Building: follow the session to its result line - #51
Conversation
|
Factory gate: passed (attempt 1, run by the factory in a fresh checkout of the pull request's head
|
|
Factory gate: passed again (attempt 2, run by the factory in a fresh checkout of the pull request's head
|
…on a set read A run whose session reported its pull request is no longer limited until it next enters building: the wait for the gate is not the build's time. A triage run whose launch was sent with no session recorded is left to resume, which names its session, so the limit never finishes a run whose session may be running untracked. The events wake checks an in-memory set of owned threads, read at start and after every pass, instead of reading every run file per status change. The time-limit comment no longer promises that the next attempt continues from the old branch. sanitize is imported from text.ts everywhere, and advance's reminder send and ended-badly statuses each have one home. Tests: an unconfirmed run is never time-limited, watched or advanced; a hostile branch name reaches the comment and ledger only as prepared text; a run back in building from pr-open is limited again.
|
Factory gate: passed again (attempt 3, run by the factory in a fresh checkout of the pull request's head
|
Until the gate is wired into the pass, a building run whose session reported its pull request stays in building. It has no session at work, so like a pr-open run it no longer takes one of max_concurrent_runs, and new claims are not starved by finished builds. The listing carries prReported from the ledger, cleared when the run enters building again.
|
Factory gate: passed again (attempt 4, run by the factory in a fresh checkout of the pull request's head
|
…ad again Once the session has reported its pull request, the build is done and the run waits for the gate (#19). The pass no longer watches its text or calls advance for it, so each pass makes no tracker or T3 call for a finished build and only says it is ready for the gate.
|
Factory gate: passed again (attempt 5, run by the factory in a fresh checkout of the pull request's head
|
Closes #17
Contract
buildingrun is followed on every pass and within 5 s of an events wake: the thread's status is read.FACTORY-DONE→ the pull request is checked (same repo, not a fork, base =project.base, head = the session's branch; a ready pull request goes back to draft with apr_set_draftline, a draft call that fails is stuck unless rate-limited) →pr_openedonce → outcome ready-for-gate, label staysbuilding.FACTORY-STOP: needs-info→ questions comment,factory:needs-info, unassign, status comment,state_changed, archive + worktree removed, finishedneeds-info.FACTORY-STOP: stuck→ archive, comment (session text fenced),factory:stuck, unassign, status, finished. No result line → one reminder per session (result-line-reminding <k>/result-line-reminded), a second miss is stuck.failed/cancelled/rolled_back→ stuck "the session run ended as";interrupted→ paused (session_pausedline only); thread gone or archived → stuck "the T3 thread no longer exists"; wrong gh account →session-wrong-account. Issue text changed during the build (watch) → stuck. Worked time intriage+buildingpastlimits.minutes→ stuck "the run passed its time limit", branch named when the thread has one.pr_adopted/branch_bound;commits_aheadcounting (always null, so the comment's sentence has no parenthesis, as softfac prints when its clone cannot count).vp test run apps/server/src/factory→ pass (33 files, 1087 tests);vp run --filter t3 typecheck→ passAcceptance criteria
pass.test.ts"following a building run" (time_limit→watch→advanceper run;needs-info/stuckruns advanced too);index.test.ts"waking the pass when a factory thread's run changes status" (ownsThread, a non-owned thread never wakes, an owned thread starts the next pass and a failed stream is re-run).FactoryEvents.layerLiveshares the server'sEventSinkV2instance viaRuntimeLayer.layerEventSink(verified by the correctness reviewer fromruntimeLayer.ts/server.ts).FACTORY-DONE: <url>: same repo, not a fork, configured base, session's branch as head; ready → draft;pr_openedwritten; moves on to the gate —building.test.ts"advance: done" (23 tests: fork, base, branch, no branch, not a draft → drafted, draft call fails → stuck, rate limit → error,pr_openedonce).FACTORY-STOP: needs-info: questions posted,factory:needs-info, unassigned, archived, worktree removed, run ends — "advance: questions" and "advance: a run left in needs-info".FACTORY-STOP: stuck: archived, comment with the reason,factory:stuck— "advance: stuck" (session text fenced, wrong account, ended badly, thread gone/archived, decision already in the ledger).softfac:<run>:remind:<k>).limits.minutesis stuck with softfac's reason — "timeLimit" (boundary at the last millisecond, branch named without a count, no branch, status read fails, never-launched run with the session-may-exist detail).building.test.tsnames the skipped ones in its header comment (merged/closed ends, closed-issue abandon, interruptions, gate-pass skips, the real-ghwatch cases,applyinternals).Gate
vp test run apps/server/src/factory→ exit 0 (.factory/gate-0.log)vp run --filter t3 typecheck→ exit 0 (.factory/gate-1.log)Models
.dw/config.ymlhas nomodelsblock, so Tom's roster applied)Review
needs-info/stuckafter a failed archive, leaking the session and its slot (correctness-1 = reliability-1, blocker); a reminder T3 refuses looped every pass until the time limit (reliability-2) — now stuck at once with softfac's "no result line" reason and T3's refusal in the ledger detail; four hand-written lifecycle action loops → oneapplyActionsported from softfac'sapply(maintainability-1); a second "unsafe text" character class → onesanitizeintext.ts(maintainability-2);progressFromshared withstart.ts,Progress.prrequired (nit); the pass passes the ledgerCauseStepexplicitly (nit); a thread the factory does not own is proven never to wake the loop (testing-1, the survivor); bounded wait in the wake test (nit); dead!endedBadlyterm dropped (nit).e4bcce9f6, behindorigin/main; fast-forwarded to9e8095616before provisioning (field lesson 18). All sevenfactory:*labels already existed on the repo; none created. Red-then-green verified at the pairb4e6422a6(6 failures) →c4ac52d58(202 pass) in a detached worktree.Lessons
node_modules/.bin/vp. Symptom:npx vitestandapps/server/../../node_modules/.bin/vitestboth print nothing or "no such file" in the detached worktree, so the red check looks like it ran and found nothing. Cause: pnpm does not hoist vitest's bin to the root.bin, andnpxresolves against the wrong tree. Default: symlink the rootnode_modulesand every workspace package'snode_modulesinto the detached worktree, then call./node_modules/.bin/vp test run <files>from its root; grep the output forTest Filesto prove the run happened.messages.tswhilegate.tsalready hadsanitize, and a reviewer had to find it. Cause: the packet described the helper's behaviour but did not grep for an existing one. Default: for every utility a packet asks for (sanitiser, fence, formatter, id builder), grep the repo first and write "use<path>:<name>" or "none exists" into the packet.buildingruns, so a run left inneeds-info/stuckwas never finished; two reviewers flagged it as the one blocker. Cause: the packet said "building runs get watch → advance" and "other states are left alone", while the reference'smatch info.statesends every non-gate state toadvance. Default: when porting a step the reference dispatches by state, paste that match arm by arm into the packet and mark each arm in or out of scope.Commits
46d9e3e9btest(factory): session text, result-line reminder and progress of a building run— Make slice 1 (red)09915e03ffeat(factory): prepare session text and read a building run's progress— Make slice 1e0687d155test(factory): advance a building run to its result line— Make slice 1 (red)8d6ef7830feat(factory): advance a building run to its result line— Make slice 1a36408d07test(factory): watch a building run's text and stop it at its time limit— Make slice 1 (red)fec84c408feat(factory): watch a building run's text and stop it at its time limit— Make slice 1a2f42fe31test(factory): the pass works a building run and the loop wakes on its thread— Make slice 2 (red)cd2b0089afeat(factory): the pass follows a building run and wakes on its thread's run changes— Make slice 2b4e6422a6test(factory): the pass advances a run left in needs-info or stuck, and a refused reminder is final— Check cycle 1 (red)a56eabc03fix(factory): the pass advances a run left in needs-info or stuck— Check cycle 1c4ac52d58fix(factory): a reminder T3 refuses ends the run as stuck— Check cycle 1732955429refactor(factory): one interpreter for lifecycle actions, one text sanitizer— Check cycle 1098c9e363test(factory): a thread the factory does not own never wakes the pass— Check cycle 2 (red)71fb342a4refactor(factory): the events wake is testable over a finite stream— Check cycle 2Files
advance,watch,timeLimitand their outcomes, ported from softfac'sbuilding.rsStopwith session text and branch,applyActions(softfac'sapply),progressFrom,stopSessionexportedSeengains the pull request, questions, reminder, wait marker, pause and worked-time fieldsprepareSessionText,fenced,questionsComment,stuckCommentwith session text and work left, the reminder and the fixed reasonsprogressFrom;finishBuildingreads the thread fromProgresssanitize, moved fromgate.ts(which re-exports it)sanitizefromtext.tsBuildingContextin the live world;ownsThread,wakeOn, the events consumer with restart;FactoryLayerprovidesEvents.layerLivelayerLiveover the server's sharedEventSinkV2