Grouped because each is a one-line fix. Full reasoning in docs/notes/2026-08-20-plan-3a-review-triage.md under "MEDIUM". Split out any one that turns out to be bigger.
Found 2026-08-21, not in the original 57:
- A run thread that finishes before its handle is stored leaves a dead
Thread in @threads forever — supervisor.rb:90. running? still answers correctly via &.alive?, so it leaks rather than misreports. Rare on MRI, ordinary on JRuby.
(The interrupt Status-write race, also found that day, has its own issue.)
From the original review:
- A stage that already succeeded is charged an interruption and re-run —
supervisor.rb:190
clear_stale_locks deletes locks belonging to a live git process — supervisor.rb:283. Also Dir[] treats [, {, * in the path as glob syntax, so such a clone clears nothing, silently.
- A failed teardown wedges the branch with nothing to retry it —
supervisor.rb:104
- The second half of a two-part answer is silently discarded —
poller.rb:107
interference? has no production callers — board.rb:51. The design doc, the failure taxonomy and the runbook all say mill reports a Status it did not write. It does not.
- An unrecoverable Status failure retries forever with no log line —
board.rb:80
- An event skipped by
running? is skipped forever with nothing recorded — poller.rb:108
INSERT OR IGNORE swallows every constraint violation while the cursor advances — poller.rb:171
@rate_limit_waits is a per-run budget spent by every stage, and reset by any restart — runner.rb:162
- Nothing can raise from the
rescue clause without killing the loop for good — workers.rb:101
- A hung
gh parks a loop forever and health calls it alive — workers.rb:72
health cannot distinguish "workers off" from "both threads died" — workers.rb:53
stop is dead code, and would not stop the run threads if called — config.ru:3
base_branch truncates any default branch containing a slash — repo.rb:93
- A missing
origin/HEAD silently becomes main — repo.rb:94
missing_secrets accepts a key with an empty value — repo.rb:121
- A leftover directory at the clone target wedges a repo permanently —
repo.rb:41
- Doctor never checks
Ready, the one Status the queue depends on — doctor.rb:254
read_config ignores whether the fetch worked, and caches the result forever — repo.rb:101
Grouped because each is a one-line fix. Full reasoning in
docs/notes/2026-08-20-plan-3a-review-triage.mdunder "MEDIUM". Split out any one that turns out to be bigger.Found 2026-08-21, not in the original 57:
Threadin@threadsforever —supervisor.rb:90.running?still answers correctly via&.alive?, so it leaks rather than misreports. Rare on MRI, ordinary on JRuby.(The
interruptStatus-write race, also found that day, has its own issue.)From the original review:
supervisor.rb:190clear_stale_locksdeletes locks belonging to a live git process —supervisor.rb:283. AlsoDir[]treats[,{,*in the path as glob syntax, so such a clone clears nothing, silently.supervisor.rb:104poller.rb:107interference?has no production callers —board.rb:51. The design doc, the failure taxonomy and the runbook all say mill reports a Status it did not write. It does not.board.rb:80running?is skipped forever with nothing recorded —poller.rb:108INSERT OR IGNOREswallows every constraint violation while the cursor advances —poller.rb:171@rate_limit_waitsis a per-run budget spent by every stage, and reset by any restart —runner.rb:162rescueclause without killing the loop for good —workers.rb:101ghparks a loop forever andhealthcalls it alive —workers.rb:72healthcannot distinguish "workers off" from "both threads died" —workers.rb:53stopis dead code, and would not stop the run threads if called —config.ru:3base_branchtruncates any default branch containing a slash —repo.rb:93origin/HEADsilently becomesmain—repo.rb:94missing_secretsaccepts a key with an empty value —repo.rb:121repo.rb:41Ready, the one Status the queue depends on —doctor.rb:254read_configignores whether the fetch worked, and caches the result forever —repo.rb:101