Kernel: an act is recorded and bounded
Closes the eighth gap of the epic "Epic: nine gaps in the Kernel, found in review": nothing counted acts. An approval that never landed was issued again on every pass, and the Run ceiling did not see it.
Based on main. It was built on "Kernel: the facts say whether the list of Runs was read in full" (#75), and rebased onto main after #75 and "Pin the order: under a Pause, the approver Hold shows first" (#76) merged.
What changes
Following the architect's working ruling on the issue (option 1):
-
Approvals. An approval is a Run with no Engine, Intent
issue-approval, because onlyapplyholds the machine user's token.kernel/decide.pynow makes every act through a new_act().- Failed approval Runs whose fault is the item's are attempts. At
max_attemptssince a writer acted, the item is held, reasonattempts_exhausted, and a writer's action renews them (I-28). - One helper,
_attempts(), now counts attempts for Runs and approvals alike. A failed model Run is never an approval attempt. - A landed approval is an act and does not count toward the Run ceiling, which stays the cost ceiling (FR-39).
- An approval that does not land, whatever its fault, counts toward the Run ceiling, and the Kernel checks that ceiling before issuing one. One failing for the environment, such as an expired token, is not an attempt and is never on the timeline; without this it would be issued for ever.
- Failed approval Runs whose fault is the item's are attempts. At
-
Every act that lands is counted from GitHub's timeline as the new fact
acts_since_writer, held to the new limitmax_acts_per_item(6).- At the limit, an approval or an enqueue is held: reason
act_ceiling, attentionneeds_you. - A count that is missing, negative or not a whole number defers, reason
acts_unreadable. - The ceiling bounds acts only. A Run is still admitted at the ceiling, and a Hold is never refused.
- At the limit, an approval or an enqueue is held: reason
-
The timeline is a list like any other.
complete.timelineis required and inREAD_IN_FULL. A timeline read short would undercount and let acts past the ceiling, so the item defers (I-13, I-20). -
The new facts are in
schemas/facts.schema.json,examples/facts.json,facts()intests/support.pyandsim/world.py.examples/invalid/facts.no-count-of-acts.jsonandexamples/invalid/facts.no-completeness-for-timeline.jsonare rejected.- The other counter-examples gain both, so each stays wrong in one way only.
-
The new limit is in
config/defaults.jsonandschemas/config.schema.json. It is also a boundschemas/lifecycle.schema.jsonaccepts, so "Lifecycle check: bounds in the data for every cycle with no person in it" (#38) can bound the merge-queue cycles. -
tests/test_decide.pygains:test_an_approval_that_does_not_land_is_boundedtest_an_approval_that_keeps_failing_for_the_environment_is_boundedtest_an_approval_that_fails_for_capacity_or_environment_is_not_an_attempttest_a_failed_model_run_is_not_an_approval_attempttest_an_approval_that_landed_is_enqueued_whatever_failed_beforetest_an_approval_run_does_not_count_toward_the_run_ceilingtest_acts_are_boundedtest_an_unreadable_count_of_acts_makes_no_acttest_the_act_ceiling_stops_acts_and_nothing_else@pending test_a_dequeue_is_never_refused_at_the_act_ceiling, which waits on the dequeue itself
The three incompleteness tests also cover
timeline. -
tests/test_tools.pygainstest_the_act_ceiling_can_bound_a_transition. -
sim/world.py:- An act is made only if the Kernel, asked again on fresh facts, still wants it.
- The approval is recorded as a Run with no Engine.
- New faults: an approval that fails to land (fault
item,environmentorcapacity), and a queue that ejects. - Three worlds that never end on their own: an approval failing every time for the item, one failing every time for the environment, and a queue that ejects every time. Every seed must rest on its own bound or on the Run ceiling.
- The invariants assert I-8: acts within
max_acts_per_item, approvals failing for the item withinmax_attempts, and every approval that did not land within the Run ceiling. sim/grounding.pylists three new rules and widensa_short_list_says_soto the timeline.
-
tools/mutants.pygains sixteen mutants and updates one. 53 of 53 killed.- "an approval that does not land is issued for ever"
- "an approval that keeps failing for the environment is issued for ever"
- "an approval that did not land is free of the Run ceiling"
- "a landed approval counts toward the Run ceiling"
- "a writer's action does not renew an approval's allowance"
- "a failed model Run counts as an approval attempt"
- "an approval's failed attempts stop an enqueue"
- "acts are unlimited"
- "the act ceiling is reached one act late"
- "an enqueue is not held to the act ceiling"
- "an unreadable count of acts is read as none"
- "a count of acts that is not a whole number is trusted"
- "a negative count of acts is trusted"
- "the act ceiling is not the Maintainer's to see"
- "the act ceiling stops a Run"
- "a timeline read short is trusted"
- Updated: "the run ceiling is ignored", for the line it mutates.
-
Documents:
- Design §3.3:
actors_since_humanbecomesacts_since_writer;completenamestimeline; a paragraph on the ceiling. - Design §3.10 gains "The act Run", including how an approval that does not land is bounded.
- Design §4.2: the approval is re-issued "with no model Run".
- Design §4.1 and §9.9 drop the open question about counting the Controller's acts.
- Design §4.5 lists the new faults, and §4.6 gains two boundary rows.
docs/glossary.mdgains "Act".- Decision 0003 gains eight boundary rows; one says "not held yet", one is waiting on
adapters/. - Decision 0002 names the timeline's counter-example.
- L-03's rule covers acts. L-53 names the timeline.
- Design §3.3:
Acceptance criteria (from "Kernel: how an act is recorded and bounded")
-
test_an_approval_that_does_not_land_is_bounded:max_attemptsfailedissue-approvalRuns whose fault is the item's givehold, reasonattempts_exhausted. A writer's action grants a fresh allowance. An approval failing for any other fault is bounded by the Run ceiling. -
test_acts_are_bounded: at the limit,hold, reasonact_ceiling, attentionneeds_you; one below, the act is made.- The fact, and its completeness flag, are in the schema, the example,
tests/support.pyandsim/world.py, with counter-examples. max_acts_per_itemis 6 in the defaults and the schema.- An act that stops work is counted and never refused. The Hold half is held now; the dequeue half is pinned
@pending.
- The fact, and its completeness flag, are in the schema, the example,
- A mutant for each rule. The simulator has an approval that never lands (for the item, and for the environment) and a queue that ejects every time, and asserts I-8.
- Documents: design §3.3, §3.10, §4.2, §4.1 and §9.9; the glossary's "Act"; decision 0003; L-03.
Left open, on purpose
- The Kernel does not dequeue yet. "A dequeue is never refused at the act ceiling" is a pending test for "What takes a pull request out of the queue" to take up.
- The run record has no marker for a Run with no Engine. A Run is free of the Run ceiling only if its intent is
issue-approvaland it succeeded. Run records count only from the Factory's own workflow (I-34). A marker would be a new run-record field. - A timeline read short defers the whole item, Runs included, as every list does under the fail-closed reading of #75.
- Counting acts from the timeline, by the Factory's identities and since a writer acted, and saying whether it was read whole, is the port's job, and waits on
adapters/. Its cost is measured by P-8; the ruling says it would be overturned if that cost is too high. acts_unreadabledefers, and is not shown as needing the Maintainer.- The run record's role for an approval Run is not read by the Kernel. The simulator uses the owner Role.
max_acts_per_itemis accepted as a Lifecycle bound but not yet written intolifecycle/pull_request.json. That is #38's.
Checks and reviews
make verifyandpython3 -m tools.mutantspass on agit archivecopy: 218 tests, OK (3 expected failures); 53 of 53 mutants killed.- The tests were written first.
- These failed until the code changed:
- the approval, act-ceiling, unreadable-count, Run-ceiling and "nothing else" boundary cases;
- in the fix, the environment-failing approval and the three incompleteness tests for
timeline; - the Lifecycle bound test, until the schema accepted the bound.
- These passed throughout, because they pin existing behaviour; their mutants show they now hold it: the capacity case, the enqueue after failures, and a failed model Run not being an approval attempt.
- These failed until the code changed:
- A first review approved every tested path. Its replay of 2,148 recorded
decide()calls matched exactly. It found the two gaps fixed in the second commit: an approval failing for the environment issued for ever, and a count of acts with no completeness flag. - The second model family's read is the Gemini code review and the other bot reviewers on this pull request, as the Maintainer directed. It is queued only once clean and green at the reviewed head, under the Maintainer's standing approval of 2026-10-15.
Closes #37
🤖 Generated with Claude Code
Summary by CodeRabbit
- New Features
- Added configurable per-item limits for automated actions.
- Automated approvals and other actions are now bounded, with holds and dequeues still allowed at the limit.
- Incomplete or invalid activity history now prevents decisions that depend on its count.