Repository navigation
Pass loop: claim ready issues fairly - #46
Merged
Merged
Conversation
…file Red: duration is not exported, and listRuns.ts and claims.ts do not exist. Issue: #15
…west factory comment Red: makeGitHub has none of login, claim, unassignedClaims or newestFactoryComment. Issue: #15
…west factory comment Issue: #15
…fusals, older runs, and the untracked claim Red: runner/start.ts and runner/messages.ts do not exist. Also silences the global Date diagnostic in claims.test.ts, which sets an mtime. Issue: #15
…fusals, older runs, and the untracked claim Issue: #15
…int rule asks Issue: #15
…ockers, pause and untracked claims Red: pass.ts does not exist. Issue: #15
… unassigned claims, claim fairly Issue: #15
…ke, and never overlaps Red: loop.ts does not exist, and FactoryRuntime does not yet need a FactoryProcess or run a pass. Issue: #15
…d exposes wake() Issue: #15
…or goes to its run, a gating issue is skipped first Red: loadHandPause blocks on a FIFO (the test hangs), PassWorld has no recordError or gateRunning, and tracker failures from start are recorded as kind github. Issue: #15
…d start's error goes to its run ledger, tracker kinds, gating skip, one bound tracker - claims: open the pause file with O_NONBLOCK, as softfac does, so a FIFO is no pause - pass: an error of a start that made a run goes to that run's ledger (step-failed), and to the machine log with the run only when the ledger cannot take it; a ready issue whose older run's gate is running is skipped first with "gate running"; every call start makes is a tracker call, so its errors are kind tracker, as is the tracker login - github: BoundGitHub and bindProcess, so the claim's Tracker is a Pick and index.ts binds every method at once; ledgerCall is exported once from ledger/errors and isState from domain/lifecycle; one noteStart puts a start's outcome on the line for both callers Issue: #15
rawphp
marked this pull request as ready for review
October 16, 2026 00:01
Author
|
Factory gate: passed (attempt 1, run by the factory in a fresh checkout of the pull request's head
|
…scan a pass, stranded stuck runs, fail-closed pause, live pass lines - With no slot free the pass reads no blockers; every issue left counts toward the limit line. - The ledger is listed once a pass; after each start only that issue's run files are read again. - A run with no approval and no session is not confirmed: it holds no slot, the untracked-claims step starts its issue again, and start goes on from it to stuck in the same run. - A pause file that exists but cannot be opened is a pause, since its mtime. - Pass lines are logged as they are decided, so a pass that dies has said what it did. - Tests for liveWorld's grace window, assignee filters and base-branch rule, and for repairClaim's half-moved and someone-else-assigned re-reads.
…before it moves to proposed A label call that fails after the comment leaves the issue where a later pass finds it again, and the comment is not posted twice while it is the issue's newest factory comment.
Author
|
Factory gate: passed again (attempt 2, run by the factory in a fresh checkout of the pull request's head
|
This was referenced Oct 16, 2026
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 #15
Contract
FactoryLayerruns a pass at start and everypoll_seconds, never overlapping, withwake(); a pass recordspass_started, loads each repo's factory block from its default branch and theghlogin, lists unfinished runs, handles untracked claims (triage, ours, no run: stuck "the factory could not confirm the issue text was approved", as softfac) and unassigned claims (triage/building, nobody, quiet 15 min, our label: proposed with the cut-short comment,claim_repairedreconciled), then claims ready issues oldest first, fewest-held repo first, recounting after each claim, undermax_concurrent_runs, with blocked/limit/unapproved/pausedclaim_skippedevents, the limit and pause lines, andpass_finished. A claim applies the approval rule, assigns and swaps ready→triage in one edit (repairing a half-applied edit), and writes approved/claimed/state_changed to a new run file; a refusal posts softfac's comment once and moves the issue to proposed.GH_CONFIG_DIRcheck, the repo clone and project checks, Linear.vp test run apps/server/src/factory→ pass (30 files, 867 tests)Acceptance criteria
FactoryLayerruns a pass at start and everypoll_seconds; passes never overlap;wake()starts the next pass now —loop.test.ts(the pass loop: 5 tests underTestClock),index.test.ts"logs factory started once for a valid machine file, then runs the first pass". A wake during a pass makes the next pass follow it at once, however many wakes arrived.max_concurrent_runsslots; a slot is held from claim until pr-open, needs-info or stuck; fewest-runs repo first —pass.test.ts"claiming new issues", "which repo gets a free slot", "issues blocked by an open issue", "issues whose Depends on text names an open issue", "the limit, said out loud";ledger/listRuns.test.ts"holdsSlot";index.tsprojectFromBase(verified live: see below).factory:readyforfactory:triagein one edit, and writesclaimedto a new run file; a refusal moves the issue tofactory:proposedwith softfac's comment —runner/start.test.ts"checks the approval, claims, and records the approval, the claim and the state", "a refusal posts the comment, moves the issue to proposed and makes no run", "a refusal already explained since the label is not commented again", the claim-repair tests;tracker/github.test.ts"claim views the assignees, then assigns and swaps the labels in one edit".runner/start.test.ts"with no run goes stuck: the factory cannot confirm what it was approved on" (softfac sends a triage issue of ours with no run to stuck at once; the 15-minute grace is softfac's rule for the claims with no assignee, ported intracker/github.test.ts"unassignedClaims keeps only unassigned issues that are old enough" andpass.test.ts"claims with no run and no assignee"). The issue text pairs the 15 minutes with the assigned case; the port follows softfac's code and docs (docs/lifecycle.md, "claims that were cut short").<data>/claims-pausedexists no claims are made, logged once per pass —pass.test.ts"the hand pause";claims.test.ts.pass.test.ts"what the pass records" (pass_started,pass_finished,repo_skipped,claim_skippedlimit/blocked/unapproved/paused,claim_repaired,errorin the run ledger withstep-failedand in the machine log withrunonly when the ledger cannot take it); the ledger's event tables are unchanged.pass.test.tsholds 64 tests ported fromsrc/tick/tests.rs(claiming new issues, which repo gets a free slot, claims that have no run, claims with no run and no assignee, blocked, Depends on, the limit, the hand pause, what the pass records, text that is printed). The dry-run tests are not ported: there is no dry run.Verified live before Ship, reads only, through
liveWorldwith the realghasrawphpagainst this repo: the factory block parsed frommain, loginrawphp, ready issues[16, 17, 19, 20, 21, 22, 23], no triage or unassigned claims, and #16 blocked by #15 (open) both by link and by its Depends on text. No write was made.Deliberate deviations from softfac, each marked in the code for the next issue: a fresh claim ends at
{ kind: "claimed" }and the pass line saysclaimed in run <id>(the launch is #16);finishOlderRunsfinishes older runs in the ledger only (archive and PR draft are #16/#20); a closed issue is skipped, not abandoned (#20).Gate
factory/scripts/check.sh→ exit 0 (seam check;vp test run apps/server/src/factory867 passed;vp run --filter t3 typecheck;vp run --filter @t3tools/web typecheck). Log:.factory/gate-1.log.vp test run apps/server/src/factoryandvp run --filter t3 typecheck; both also exit 0.Models
Review
O_NONBLOCK, so a FIFO cannot hang the pass), correctness-2 (an error of a start that made a run goes to that run's ledger, withmadeSinceto find it), correctness-3 (every callstartmakes is a tracker call: kindtracker, as is the tracker login), correctness-4 (a ready issue whose older run is gating is skipped first with "gate running"), maintainability-1 (BoundGitHubandbindProcess; the claim'sTrackeris aPick), maintainability-2 (oneledgerCallinledger/errors.ts, used by gate, start and index), maintainability-3 (isStateexported once), maintainability-4 (onenoteStartfor both callers).releaseClaim): the port matches softfac'srelease_claim, which fails on either call; maintainability-5 (splitgithub.tsinto transport and operations): the reviewer defers it to the PR that adds the pull request operations.test:commit was run before itsfeat:/fix:commit and failed as its body says (a missing module, a missing method, or the asserted value); the whole suite is green at the tip.Lessons
gh pr createwithout--repoopens the pull request on the parent repository. Symptom: the draft PR for this fork's branch appeared aspingdotgg/t3code#20083(closed at once with a one-line apology; no other write was made upstream). Cause:ghresolves a fork's default base to its parent. Default: always pass--repo <owner/repo>and--head <branch>togh pr createandgh pr edit, and read the URLghprints before reporting it..factory/is collected by the test runner. Symptom: the gate run reported twice the test files and 16 failures, every one under.factory/testing/. Cause: vitest globs the whole worktree and.factory/.gitignoredoes not hide it from the runner. Default: on a JS repo, remove the mutating reviewer's copy before running the gate (or tell the reviewer to remove it at the end), and read the gate log's file count against the suite's..dw/config.ymlafter Isolate's fast-forward. Symptom: the two gate commands from the launch-time config passed, but the base tip's config had moved to one script that also typechecks the web app. Cause: the config was read beforegit merge --ff-only origin/<base>. Default: read the factory block again after the fast-forward, and run that gate.open. Default: run such a test file alone with a per-test timeout, commit it red, and expect the first run to hang rather than fail.Commits
7061a137ctest(factory): duration, unfinished run listing and the claims pause file— Make slice 131812bedbfeat(factory): duration, unfinished run listing and the claims pause file— Make slice 1d31b95ab3test(factory): gh login, the claim edit, unassigned claims and the newest factory comment— Make slice 2491322480feat(factory): gh login, the claim edit, unassigned claims and the newest factory comment— Make slice 2768a8d53dtest(factory): the claim: approval, the claim edit and its repair, refusals, older runs, and the untracked claim— Make slice 399efe706cfeat(factory): the claim: approval, the claim edit and its repair, refusals, older runs, and the untracked claim— Make slice 374a39f315style(factory): catch the missing-ledger tag with catchTags, as the lint rule asks— Make slice 31863af803test(factory): softfac's tick tests for ordering, fairness, slots, blockers, pause and untracked claims— Make slice 40a0686743feat(factory): one pass: load repos, count runs, handle untracked and unassigned claims, claim fairly— Make slice 4cf3532aeetest(factory): the pass loop runs at start, every poll_seconds, on wake, and never overlaps— Make slice 51c66f2d36feat(factory): FactoryLayer runs the pass loop over the live world and exposes wake()— Make slice 5dd396c29btest(factory): a FIFO pause file does not block, a failed start's error goes to its run, a gating issue is skipped first— Check cycle 1641145bfcfix(factory): review cycle 1: ...— Check cycle 1Files
apps/server/src/factory/index.ts—FactoryLayerbuilds the live world overghand the ledger, runs the loop, exposesFactoryLoop(wake,awaitPass)apps/server/src/factory/loop.ts— the pass loop: start, poll, wake, no overlapapps/server/src/factory/pass.ts— one pass overPassWorld: repos, runs, untracked and unassigned claims, the claimsapps/server/src/factory/claims.ts— theclaims-pausedfile: load, pause, resumeapps/server/src/factory/runner/start.ts— the claim: approval, the edit and its repair, refusals, older runs, the untracked claim's stuck pathapps/server/src/factory/runner/progress.ts— what a run's ledger already recordsapps/server/src/factory/runner/messages.ts— softfac's fixed words: the stuck comment, the status comment, the cut-short commentapps/server/src/factory/ledger/listRuns.ts— unfinished runs oldest first,holdsSlotapps/server/src/factory/ledger/time.ts—durationapps/server/src/factory/ledger/errors.ts—ledgerCall, shared by gate, start and indexapps/server/src/factory/domain/lifecycle.ts—isStateapps/server/src/factory/gate.ts— uses the sharedledgerCallapps/server/src/factory/tracker/github.ts—login,claim,unassignedClaims,newestFactoryComment,BoundGitHub,bindProcessindex.test.ts,loop.test.ts,pass.test.ts,claims.test.ts,runner/start.test.ts,ledger/listRuns.test.ts,ledger/time.test.ts,tracker/github.test.ts