Kernel: a Run that ran and left no outcome is an attempt; one cancelled, or never started, is none (#286)
A failed Run that left no outcome is now an attempt when it ran (failure or timed_out), and no attempt when it was cancelled. This follows the working ruling on #286, which takes section 3.10's stricter reading over decision 0029's.
What changes
kernel/brakes.py::_attempts. It also counts a completed Run with no outcome whose conclusion is interms.RAN_AND_DIED(failure,timed_out). AcancelledRun with none counts toward the Run ceiling alone.adapters/records.py. A job that never started (startup_failure) is read ascancelled, notfailure, so a broken workflow file never counts against an item.controller/feed.py. The feed counts attempts the way the brakes do.- Simulator. A killed Run in
sim/world.pyandsim/issues.pymay now leave no outcome, as section 3.10 says, and the approval invariant counts such Runs. - Decision record. A new record replaces 0029 and keeps all of 0029 except this rule and its row. 0029's Status names the new record.
- Design §3.10. The killed-Run sentence is rewritten to match.
Boundaries. Each new boundary has a test and a mutant:
- A review or an approval that failed or timed out with no outcome is held at
max_attempts. - An issue's Run that ran and left no outcome counts the same way.
- A cancelled Run with no outcome is admitted again.
- A Run still in flight, with the item one attempt short, holds nothing.
- The port reads
startup_failureascancelled. - The feed's count matches the brakes'.
Not verified. That a job killed at its timeout concludes timed_out is still a_killed_job_times_out, waiting on P-18. The record and the design both say so.
Known and accepted. An outcome GitHub has not listed yet, or one past its retention, reads as none, so for that pass a failed Run counts as an attempt. The effect is a Hold, never an action. The record says so.
Reviews, all on c6fed5e, with the fix round after.
- Claude review (fresh context). It found one major:
startup_failurereached the Kernel asfailure, so it counted as an attempt. The minors were tests that used conclusions a run record cannot hold, no issue-side test, a vacuous in-flight assertion, the simulator never producing a null outcome, and stale references to 0029. All are answered in 83a58df. - Muse (Muse Wonder Full). It found the same
startup_failuremajor, and a minor: the feed still counted attempts by the old rule. Answered in 83a58df and 045b347. - Security pass. Not run as a separate review. The change only makes the Factory hold more, never act, and Muse looked for a path to approve, merge or admit and found none.
- AGY (Gemini 4 Argon). No findings on c6fed5e, and none when it re-read the Kernel fix on 83a58df.
- Verify.
make verifypassed on a fresh export of 1cad718, and both simulators pass at CI's 300 seeds. After the rename to 0049 and the merge of main, only the context check's gap fails. - Simulator. A killed Run that may leave no outcome changes what each seed draws. Seed 69, the first to lose an approval while queued, now runs in a short run too.
Closes #286
🤖 Generated with Claude Code