Skip to content

Cut-short scan stops re-reading label history for issues it never acts on - #53

Merged
tomkaczocha merged 6 commits into
mainfrom
t3/factory-issue-48
Oct 16, 2026
Merged

tomkaczocha merged 6 commits into
mainfrom
t3/factory-issue-48

Conversation

@rawphp

@rawphp rawphp commented Oct 16, 2026 •

Copy link
Copy Markdown

Closes #48

Contract

  • done: unassignedClaims skips issues the pass knows runs for and issues it already judged not the factory's at the same updatedAt; a history read that fails for that issue alone is reported for that issue and the scan goes on; the pass prints one error line per such issue; an issue that is the factory's is still moved to factory:proposed as before.
  • out: no memo of "ours" judgements; no change to CLAIM_GRACE_MS or releaseClaim; no new files.
  • risk: none
  • test: vp test run apps/server/src/factory/tracker/github.test.ts apps/server/src/factory/pass.test.ts apps/server/src/factory/index.test.ts → pass (325 tests)

Design decisions settled in this run:

  • The memo key is repo, issue, state label and updatedAt (the state label is added to the issue's key so an issue carrying both labels is judged per label).
  • One ClaimMemo is built in liveWorld, which is built once per process, so a judgement lasts for the life of the factory process.
  • Review finding: a GitHubRateLimited, GitHubTimedOut or GitHubSpawnError on a history read would fail every following call too, so it ends the repo's scan as a failing list call does (one "cannot list the unassigned claims" line), instead of spending one refused call per remaining issue every pass. Only GitHubFailed, GitHubParse and GitHubOutputTooLarge are isolated per issue. This narrows "a history read that fails" in criterion 3 to the failures that are the issue's own; the budget protection the issue asks for is why.

Acceptance criteria

  • No history read for a known issue — github.test.ts "unassignedClaims reads no history for an issue the pass knows a run for"; pass.test.ts "the issues with runs are handed to the scan so their history is not read"
  • Judgement remembered while updatedAt is unchanged, one history call per issue over two scans — github.test.ts "unassignedClaims remembers an issue judged not the factory's while updatedAt is unchanged"; index.test.ts "a judgement that an issue is not the factory's lasts across passes of one world"
  • A failing history read is that issue's error only; the pass prints one error line naming it — github.test.ts "unassignedClaims: a history read that fails is an error for that issue only" and "… an unparseable history is that issue's error and the next is still read"; index.test.ts "a history read that fails is reported to the pass with the issue and the message"; pass.test.ts "an issue whose label history cannot be read is one error line and the others are still moved"
  • Behaviour for the factory's own cut-short claim unchanged — existing pass.test.ts "a triage or building issue with no run and no assignee is moved to proposed" and index.test.ts "a cut-short claim is commented on, then moved to proposed" still pass; all 952 tests under apps/server/src/factory pass

Gate

  • vp test run apps/server/src/factory → exit 0 (31 files, 952 tests)
  • vp run --filter t3 typecheck → exit 0

Models

  • maker: claude-sonnet-5-5 (no models block in .dw/config.yml; roster default)
  • checker: claude-opus-5-5

Review

  • cycles: 2
  • fixed: cycle 1 (correctness + reliability, one shared action): a rate-limited or timed-out history read no longer continues to the next issue; it ends the scan. Cycle 2 (testing, 12 mutants, 2 survivors): added index.test.ts cases proving the memo lives for the world and that unread reaches the pass; also the two nits (an unparseable history case, a run in another repo left out of known).
  • leftovers: none

Lessons

  • Read the branch name from git, not from the launch snapshot. Symptom: the first push failed with "src refspec does not match any". Cause: the harness's git status snapshot named a different branch from the one the worktree was on. Default: run git branch --show-current at Isolate and use that name on every push.

  • Never cd into the skill directory to read a reference. Symptom: the session's working directory flipped to the skill checkout and back, and a later compound command ran from the wrong place. Default: read reference files by absolute path with cat; every product command starts with cd <worktree>.

  • Pass --repo <owner/repo> to gh pr create in a fork. Symptom: the draft PR opened on the upstream parent (pingdotgg/t3code#20137, closed within a minute) instead of the watched repo. Cause: a checkout with an upstream remote makes gh default to the parent repository. Default: always pass --repo with the issue's repo and --head with the worktree's own branch name, then check the URL's owner before linking.

Commits

  • aa444d356 test(factory): cut-short scan skips known issues, remembers judgements, isolates failures — Make slice (red)
  • a8fc4b268 fix(factory): cut-short scan reads no history for known or remembered issues — Make slice (green)
  • bc58f39a6 test(factory): a rate-limited or timed-out history read ends the cut-short scan — Check cycle 1 (red)
  • ba10b6f22 fix(factory): only a call's own failure is isolated in the cut-short scan — Check cycle 1 (green)
  • ac95256fd test(factory): cover the cut-short scan's wiring and the call-specific failures — Check cycle 2 (test-only, no red phase: behaviour already worked)

Files

  • apps/server/src/factory/tracker/github.ts — ClaimMemo, ClaimScan, isCallSpecific; unassignedClaims takes known and memo, isolates an issue's own failure
  • apps/server/src/factory/pass.ts — ClaimScanResult, Known.issues; the pass hands known issues to the scan and prints one error line per unread issue
  • apps/server/src/factory/index.ts — one ClaimMemo per liveWorld; maps tracker errors to messages for the pass
  • apps/server/src/factory/tracker/github.test.ts, pass.test.ts, index.test.ts — the cases above

Labels: all seven factory:* labels already existed; none created. The worktree was fast-forwarded from e4bcce9 to origin/main (9e80956) at Isolate.

…s, isolates failures

Red: the tests call unassignedClaims with a known set and a ClaimMemo and expect a
{ claims, unread } result, none of which exist yet (compile errors count as red).

Issue: #48
… issues

The scan took one serial GraphQL history call per unassigned triage/building issue on
every pass, including issues the pass then dropped for having a run, and one failed
read failed the scan for the whole repo. It now takes the issue numbers the pass has
runs for and reads nothing for them, remembers issues judged not the factory's while
their updatedAt is unchanged (for the life of the process), and reports a failed read
for that issue only, which the pass prints as one error line.

Issue: #48
…short scan

The two new tests fail: such a failure is still swallowed into unread and the
scan goes on to the next issue.

Issue: #48
…scan

A rate limit, a timeout or a gh spawn failure would fail every remaining
history read, so it now ends the scan like a failing issue list does. Only
GitHubFailed, GitHubParse and GitHubOutputTooLarge are kept per issue.

Issue: #48
…c failures

Four cases a mutation review showed were missing. They pass against the
current code (test-only cycle):

- index: a judgement that an issue is not the factory's lasts across passes
  of one world (one history read over two scans)
- index: a failed history read reaches the pass as an unread entry with the
  issue and a non-empty message
- github: an unparseable history is that issue's GitHubParse error and the
  next issue is still read
- pass: a run in another repo is left out of the known set handed to the scan

Issue: #48
@rawphp
rawphp marked this pull request as ready for review October 16, 2026 03:44
@rawphp

rawphp commented Oct 16, 2026

Copy link
Copy Markdown
Author

Factory gate: passed (attempt 1, run by the factory in a fresh checkout of the pull request's head ac95256 merged with the base branch tip 9e80956)

Command Exit Time
factory/scripts/check.sh 0 37.9 s

…ssue and label

The memo now keys by login, repo, issue and state label and stores the
updatedAt it judged at, so a re-authenticated gh judges again and a newer
judgement replaces the old one instead of piling up. Tests pin each part
of the key, a re-read after a failed read, an oversized history as that
issue's error, and a gh that cannot start ending the scan.
@rawphp

rawphp commented Oct 16, 2026

Copy link
Copy Markdown
Author

Factory gate: passed again (attempt 2, run by the factory in a fresh checkout of the pull request's head 8e81ea2 merged with the base branch tip 11492a7)

Command Exit Time
factory/scripts/check.sh 0 44.7 s

@tomkaczocha
tomkaczocha merged commit 7db5cfe into main Oct 16, 2026
@rawphp
rawphp deleted the t3/factory-issue-48 branch October 28, 2026 01:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Cut-short scan stops re-reading label history for issues it never acts on

2 participants