Repository navigation
Start: launch the /factory session for a claimed issue - #47
Merged
Merged
Conversation
The server has the project store, the launch and management services and git in scope where the factory is merged in, but not the projection store. `layerLive` is `layer` with that store provided, for the factory's root layer. Issue: #16
Fails until the launch lands: start() has no sessions, launch message, text re-check or started outcome yet (type errors and failing assertions in start, messages, pass and index tests). Issue: #16
Fails: the pass never calls start for a run that is already confirmed in triage. Issue: #16
Fails: the error is recorded as kind ledger. Issue: #16
Two review nits: the no-note test now asserts the stuck outcome and its reason, and the failing-unassign test asserts the exact ledger lines. Both pass already; no behaviour changes. Issue: #16
rawphp
marked this pull request as ready for review
October 16, 2026 01:25
Author
|
Factory gate: passed (attempt 1, run by the factory in a fresh checkout of the pull request's head
|
The pass's run and untracked-claim steps now call `resume`, which goes on with a claim already made and never takes the fresh-claim branch: an issue put back to factory:ready is left to the claim step, which checks the pause, the limit and the blockers (f1, f2). `resume` also finishes a stuck path cut short after its label moved: an issue labelled factory:stuck whose newest run is unfinished in triage with a stuck line in its ledger. Before, start skipped it every pass and the run held a slot forever (f10). A pass that runs past 30 minutes is stopped and logged, so one hung call (an in-process T3 launch, say) no longer stops every later pass. The bound is above any single call's own limit (gh 60 s, worktree removal 10 min); a launch cut short resumes on the next pass under the same command id (f11). Tests for the stop path's worktree-remove failure and a thread T3 no longer knows (f5). Drops imports the move to run.ts left unused.
This was referenced Oct 16, 2026
…k by its own run Since the run steps only resume, a run whose issue an instructor labelled factory:ready again stayed in triage and held a slot; with every slot taken the claim step never reached the issue, so its claim never finished the old run (f10). The claim step now leaves an issue's own runs out of the limit, since a claim finishes them first. Another issue's run still holds its slot. PASS_TIMEOUT's comment no longer claims only a hung pass is stopped (f13). A test pins that resume leaves a stuck issue alone when its run never went stuck (f14).
…eeps its slot Round 3 left an issue's own runs out of the claim limit, so an issue put back to factory:ready while its old session still ran could start a second session past max_concurrent_runs: finishOlderRuns ends the old run in the ledger only until #20 ports the stop (f15). That exemption is gone. As softfac's `stalled` does, a confirmed triage run that `resume` will not take (a person relabelled, reassigned or closed its issue) now prints an ATTENTION line saying it is not progressing and holds a slot, and how to let the start finish, instead of a quiet "left to a later pass" (f10). The run keeps its slot, as in softfac; abandoning a closed issue's run is #20's.
Author
|
Factory gate: passed again (attempt 2, run by the factory in a fresh checkout of the pull request's head
|
…is logged The stalled ATTENTION line now carries resume's reason, and a skip says what a person did: a closed issue's line says to reopen it (nothing finishes a closed issue's run until #20), and an issue put back to ready with nobody assigned gets a plain note instead, as the claim step takes it when a slot is free (f10, f16). Each attention line also writes the machine log's `attention` event with `not-progressing`, as softfac does (f18). Tests pin a re-readied issue with and without a free slot (f19); comments say its run holds a slot until a claim finishes it (f17). A claim_repaired line the machine log cannot take is logged as a warning, not dropped (f9). `mention` and the sole-assignee check have one definition each, still case-folded with toLowerCase: a Linear user can be a display name (f8).
…says so An issue put back to factory:ready while its own unfinished run holds the last slot can never be claimed: only that claim would finish the run. The claim step now raises an ATTENTION line and a `not-progressing` attention event for it, naming the run and how to free it, instead of only a limit line (f10). The limit still counts the run, so no second session starts past max_concurrent_runs. pass.ts matches repos with runId.ts's asciiLower, as run ids do (f8). A test pins that resume says nothing moved for a ready issue someone is assigned to, which stalls its run (f19).
…nless run An issue put back to factory:ready while its own triage run held the last slot waited forever: only that claim would finish the run (f10). Runs now say whether a session may be running (a launched session, or a launch that was sent). The claim step leaves the issue's own confirmed triage runs with no session out of the limit, since finishOlderRuns ends them and nothing is left running. One whose session may be running keeps its slot, so no second session starts past max_concurrent_runs, and only when such runs fill the limit by themselves does the claim raise its ATTENTION line (f20).
… issue's run Two pass tests: a re-readied issue whose own run has no session still waits when other runs fill the limit, and once claimed past its own run it takes the slot the next ready issue would have had (f21). workRuns picks its runs with the claim step's `inTriage`, so the two cannot drift, and the docs no longer say the run holds its slot until the claim (f22).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #16
Contract
start()takes a claimed issue to a launched/factorysession as softfac'srunner/start.rsdoes:findProject(repo.path)before the claim; thelaunch-sendingstep once; the launch undersoftfac:<run>:launchwith softfac's title, base, first message and the machine's model; thesession_launchedline; the issue text checked again once the session exists (a change archives the session, removes its worktree and goesstuck); theapproval-recheckedstep; thebuildinglabel, the status comment and thestate_changedline; outcome{ started, run, threadId }. Every resume path replays from the ledger, and the pass callsstartagain for a confirmed run still intriage. A refused launch goesstuckwith the detail and the session-may-exist note in the ledger only.finishOlderRuns(Building: follow the session to its result line #17–End of a run: merge, close, abandon and stop by hand #20), Linear.vp test run apps/server/src/factory→ pass;vp run --filter t3 typecheck→ passAcceptance criteria
factory:triagefinds the T3 project by the repo'spath; a missing project makes the run stuck with softfac's reason, and no project is created —start.test.ts"a repo with no T3 project goes stuck before any launch is sent" (reasonno T3 project for this repo on this machine, public commentthe factory could not launch a session) and "a T3 that cannot be reached when the project is looked up claims nothing".softfac:<run>:launch, the base from the factory block, softfac's thread title and first message, and the model instance and model from the machine config —start.test.ts"claims, launches the session and moves the issue to building" (asserts the wholeLaunchRequest), "records the model the launch call sent";messages.test.tspins the first message byte for byte against softfac'slaunch_message.start.test.ts"a text that changed before the second look archives the session and goes stuck" (changedandunjudged), "the second look comes after the launch is recorded and before the label", "an archive that fails is an error before anything is written, and the next start retries".factory:building, upserts the status comment and writessession_launchedwith the thread id — the happy-path test's tracker calls and ledger lines (startedEvents()).start.test.ts"writes the launch-sending step once and sends under the same command id", "with no session launches and then looks at the text once";pass.test.ts"a confirmed triage run gets start on the next pass, and the line says the session started".start.test.tsdescribes "start: a fresh ready issue", "start: a claim call that fails", "start: a launch that an earlier pass may have sent", "start: a resume", "start: older runs of the issue".Decisions
building, and never before the launch (a resume that skipped the launch for a changed text would leave an untracked thread running). The port follows softfac and the ported tests.FactorySessions.launchis T3's answer after the request went out, so it is final and recorded with the session-may-exist note. AfindProjectfailure still fails the start with nothing claimed, as in softfac.FactorySessions.layerLive(adapter) provides the projection store the server does not have in scope where the factory is merged in; nothing outsidefactory/changed.e4bcce9f6); it was fast-forwarded toorigin/main(a8a24e261) before provisioning. Nofactory:*labels were created; all seven existed.Gate
vp test run apps/server/src/factory→ exit 0 (31 files, 921 tests)vp run --filter t3 typecheck→ exit 0Models
Review
startagain for a confirmedtriagerun, so a start cut short after the launch left its session untracked (blocker,pass.tsworkRuns); a failed T3 call was logged as kindledgerinstead oft3; the stuck path, session stop andfinishBuildingmoved torunner/run.tswith the step onProgressso Building: follow the session to its result line #17/Gating: a passing gate opens the PR for review, a failing one goes back to the session #19/End of a run: merge, close, abandon and stop by hand #20 reuse them; the ledger step names are spelled once. Cycle 2 (testing, mutation): 12/12 named mutants killed; two assertions tightened.Lessons
gh auth tokeninside a heredoc that wrote the launch message, which forbids that very command. Cause: the guard scans command lines, and the contract text carried the phrase as data. Default: when a packet's byte-exact text contains words a command guard watches, say in the packet to write those files through the Write/Edit tools and never put the phrase on a command line.files. Symptom: correctness approved the runner, and reliability found that nothing in the pass ever called it again, so the crash-safety criterion was unreachable in production. Cause: the port split the step (start.rs) from its dispatcher (tick.rs) and the contract named only the step. Default: for each criterion phrased as "the next pass/run/tick does X", list the dispatcher infiles:and add a test that drives X through it.Commits
24d132e34feat(factory): a sessions layer with its projection store provided— Make slice844104c4atest: port softfac's start tests for the launch— Make slice (red)cb7c29f67feat(factory): launch the /factory session for a claimed issue— Make sliced38718702test(factory): a confirmed triage run is started again by the next pass— Check cycle 1 (red)76c9b3659fix(factory): the pass starts a confirmed triage run again— Check cycle 1c20e7a48atest(factory): a start that fails at T3 is logged as a t3 error— Check cycle 1 (red)13207bed2fix(factory): a failed T3 call is logged as kind t3— Check cycle 18e61acc4crefactor(factory): move the run's stop and stuck path to runner/run.ts— Check cycle 1 (no red phase: a move)9f46619cdtest(factory): pin the whole outcome in two stuck-path start tests— Check cycle 2 (no red phase: tightened assertions)Files
building;findProjectbefore the claim; resume from the ledgerProgress,goStuck(with the session stop and worktree removal) andfinishBuilding, shared by the steps to comeLAUNCH_FAILED,TEXT_CHANGED, the session-may-exist noteSeenreadslaunch-sending,approval-rechecked, the worktree and its removalworkRunscallsstartfor confirmedtriageruns;startedoutcome; T3 errors are kindt3FactorySessionsintostart;FactoryLayerprovideslayerLivelayerLive