Agent roles and sessions¶
The workflow has three agent roles, each spawned by a different set of stage handlers. Roles are independent: each can
use codex or claude and each carries its own optional CLI args, parsed from its own env var by the grammar in
command-specs.md.
Stage and label names are spelled apart here as they are in
../state-machine/labels-and-state.md#workflow-labels: a bare tag names the stage — the
handler, the subpackage under orchestrator/workflow/stages/ holding it, and the identifier a session's analytics row
is attributed to — while workflow:<tag> is the wire label the GitHub issue carries. in_review, question,
discussion, and the done / rejected terminals were never namespaced, so those read the same on both sides.
The three roles¶
- Decomposer (
DECOMPOSE_AGENT, defaultclaude) — spawned by_handle_decomposing(and itsawaiting_humanresume);_handle_question(and itsawaiting_humanresume),_handle_discussion, and the late adjudication an oversized committed candidate earns all reuse the same backend. Session: locked per issue after first spawn (decomposing →decomposer_agent; question →question_agent; discussion →discussion_agent; late adjudication →late_agent, each a separate pin). Thedecomposeskill holds the sizing and decomposition policy for the two of those runs that split work: the initial decomposition and the late adjudication.questionanddiscussionshare the backend and nothing else — neither sizes or splits an issue, so selecting it for them does not call for the skill. - Implementer / dev (
DEV_AGENT, defaultclaude) — spawned by_handle_implementing,_handle_documenting,_handle_validating(awaiting-human resume; theCHANGES_REQUESTEDdev fix is dispatched here but relabels toworkflow:fixingBEFORE the spawn and recordsstage="fixing"analytics, so the dev-fix subphase reads as fixing rather than validating on both the wire label and the analytics row),_handle_fixing(in_review-route PR-feedback resume + validating-route awaiting-human rescan),_handle_resolving_conflict(conflict resume + awaiting-human resume). Session: locked per issue after first spawn. - Reviewer (
REVIEW_AGENT, defaultcodex) — spawned by_handle_validating(fresh every round). Session: fresh per round; current config always wins.
The defaults (claude decomposes, claude implements, codex reviews) require those two CLIs; both need to be
authenticated on the host before the orchestrator starts.
Where a role is spawned from¶
Every stage handler lives on responsibility-named owners under orchestrator/workflow/stages/, one subpackage per
stage — decomposition (the decomposing / ready / blocked / umbrella handlers), implementing, documenting,
validating, in_review, fixing, conflicts, question, and discussion — which own entry checks, session
execution, drift handling, persistence, and terminal routing. Nothing answers for a stage beside those owners, so each
handler is reached on the one module that holds it, the dispatcher and the same-tick pickup start that module
directly, and a patch meant to intercept a handler has to land on it. The orchestrator.workflow initializer is a
marker. Callers read labels from state.py, write guards from transition_guard.py, and the per-repo tick from
engine/tick.py.
Everything a stage borrows is named the same way. A cross-stage call names the owner it borrows from rather than a
facade; the worktree, HEAD, fetch, push, and PR-title helpers live on owners under orchestrator/git/; and the
tracked spawn every role goes through dispatches on orchestrator/agents/runner.py. Which owner defines which helper
and which stage borrows it is the module map in
../architecture/platform-modules.md for the git and agent owners and
../architecture/workflow-modules.md for the stage tree; the
per-stage behavior is in
../state-machine.md#stage-handlers. What follows is the role-specific glue.
Session lifecycles¶
- Dev session reuse. The implementer session is spawned once in
_handle_implementingand then resumed by_handle_documenting,_handle_validating,_handle_fixing, and_handle_resolving_conflictwhenever they need the dev to make a change — with one park excepted, on the same terms as the decomposer's below: animplementingissue stopped on its spent spawn budget (retry_cap) resumes on no reply at all, and the trusted/orchestrator continuethat renews the budget retires the session as it buys the attempt (dev_agentkept), so what a human pays for is a fresh conversation rather than a replay of the one that ran out. The locked(backend, args)spec is re-parsed on every resume from pinneddev_agentso a config flip mid-flight cannot retarget the session. A conversation a run showed to have FAILED is retired for good rather than rotated:dev_session_retirementrecords its id with a fresh conversation owed, the pins are cleared withdev_agentkept, and every one of those resumes opens a fresh conversation over the same worktree, branch, and issue context under the pinned command — whatever the silent streak or resume budget read, and whatever a publication resets — until one with an id of its own is pinned. A fresh conversation there, or an unparked tick's first spawn, that returns no id or hands a retired id back pins nothing and leaves that recovery owed, and no retired id is resumed again — not by the one continuation an AGY run that stopped mid-command earns either, and not where the record is misspelled or will not read at all, which owes a fresh conversation and keeps every id it still names, while the pin it was owed over — in either field, a legacycodex_session_idthe pinneddev_agenthides from the session read included — is retired by name as it is cleared. A resume charges the resume budget only of the conversation it ran while that is still the one pinned, so neither a conversation retired meanwhile nor the fresh one a recovery pinned in its place is charged for it. An authorized late restart drops the pins anddev_agentwith the cycle but keeps the record, so the fresh cycle's first spawn is refused a retired id too. The retirement spends nothing of the retry budget or the run ledger, and authorizes no run: the next one still waits on whatever gate stopped the issue. The record does not decide THAT a conversation failed; its lifecycle is in the pinned-state reference._resume_dev_with_textonworkflow/stages/implementing/resume.pyis the one module every one of those resumes goes through; it declares the call signature its callers were written against, then binds those arguments into a typed request/context before executing the resume. - Reviewer freshness.
_handle_validatingspawns a fresh reviewer subprocess every round with no resume, soREVIEW_AGENTchanges take effect on the next validating tick. The current value is recorded inreview_agentfor traceability only. Each reviewer is handed the developer report its pull request carries — re-read from where it settled and quoted whole beside the issue and the inspection commands, never fetched by the reviewer or left to the bounded thread excerpt — and what it reviewed is recorded beside the spec asreview_subject, both committed before the reviewer spawns over the comment the subject was bound to, so a record moved there, or a commit nobody confirmed, spawns no reviewer: the pull request, its head, the requirements revision, and the report revision. The same subject is recorded again asreview_returned_subjectin the write a reviewer that RETURNS makes, since the launch's record goes down before the run budget is asked and a refused launch leaves it with no reviewer invoked; that returned record is what tells a hand-back still owed its review from one a reviewer has read. A pull request with no report recorded, a report about another commit than that head, and one written against requirements the issue has moved past are refused rather than handed over — save that a report of the head this orchestrator's own rewrite replaced (a rebase, a conflict resolution, or commits an earlier tick left unpushed), where a recordeddeveloper_report_rewrite_debtexplains exactly that gap, holds the reviewer while the developer is resumed for a fresh report of the rewritten head, which is the report handed over once it settles. So does a report of the approved commit this orchestrator's approval squash collapsed into that replaced head, once the squash's recorded lineage proves it. A report that changes on an unchanged head is a new subject, so it always reaches a fresh reviewer — one settling before the spawn holds the round, and one settling while a reviewer is out, while its approval is verified or squashed, or whilein_reviewdecides its ready ping voids what that approval would have earned, and either stays current — and nothing an earlier approval left keyed on the head alone stands in for that review (seereview_approved_subject). Nor does an approval of requirements the issue has moved past, or of a head a push has replaced: the squash handoff reads the issue and the pull request's head afresh against what the approval was given before it moves the label toworkflow:documenting, andin_reviewhands such an approval back and reads both afresh before its ready ping. Beside the report, a reviewer is handed the workflow verification evidence current for exactly its subject, quoted whole under the revision a reuse names, and nothing about another head, report, or requirements revision; it declares the verification it relies on (see the reviewer verification contract), and what it returns is persisted asreview_returned_verdictbefore that evidence is published or the verdict acted on, so a publication or handoff retry finishes the verdict without a second reviewer, usage fold, run charge, round, or developer run. A reviewer run that timed out, crashed, or ran out of provider quota is answered by the next fresh reviewer, never by the developer session: a timeout or crash (reviewer_timeout/reviewer_failed) is retried on a quiet tick or by any reply, while areviewer_usage_limitpark — the one a Codex reviewer takes when its last turn failed on the account's usage limit, its notice naming the limit and the reset the provider gave — waits for the operator to say the quota reset with a trusted/orchestrator continue, and holds every other tick with nothing run or consumed (see state-machine/delivery-stages.md). - Decomposer reuse.
_handle_decomposingspawns the decomposer once and resumes it on every awaiting-human reply — with one park excepted. An issue stopped on its spent spawn budget (retry_cap) is waiting on a human deciding to spend more of this issue's day on it rather than on words for the agent, so a reply resumes nothing there: the tick holds until a trusted/orchestrator continuerenews the budget, and what that buys is the fresh spawn the budget refused. The locked session is retired as it is bought,decomposer_agentkept, so the run it pays for is a new conversation rather than a replay of the one that ran out. What the park holds, what the command lifts, and what one lifts it to are in state-machine/delivery-stages.md. Thequestionstage readsDECOMPOSE_AGENTonly as the fallback on the first-ever question spawn, then pins what it ran under toquestion_agent(a separate key) so a multi-turn Q&A keeps its own lock independent of any decomposing session on the same issue. Thediscussionstage borrows the same role on the same terms and pins todiscussion_agent+discussion_session_id, a third independent pair, and resumes that session on every human reply — so the pin protects both which backend and args a later round runs under and the conversation it continues, since a session id is only meaningful to the CLI that issued it. - Late adjudication. An oversized committed candidate is adjudicated by the same role under the same
workflow:decomposinglabel, by_adjudicate_late_generationonworkflow/stages/decomposition/late_coordinator.py. It is an additive mode rather than a stage: an analytics row readsagent_role=decomposerandstage=decomposingexactly as the initial decomposer's does, the spawn spends the same per-issue retry budget and folds its usage into the same counters, and the initial decomposer's prompt, fence, and missing-manifest handling are untouched. What it does not share is the pin or the conversation:late_agent+late_session_idare a fourth independent pair, seeded fromDECOMPOSE_AGENTon an issue's first late run and locked from then on. One late run in three resumes it: a human answering the categorized question the adjudicator asked is answering the agent that ASKED it, so that run continues the pinned session rather than opening a conversation which would have to be told the question before it could be told the answer. Every other late run is fresh — a first adjudication has none to continue, and a candidate the developer revised is a different question, so a session opened against the commit it replaced would hand the agent a transcript about work nobody is adjudicating. Both halves are proved rather than assumed: the caller says it is carrying an answer, and the record says its session really ran against this cycle, generation, and commit. The id is pinned at the two exits that persist, a timeout and a completed reply. A spent spawn budget parks this road too, on the same terms as the two above and under the sameretry_capreason — a reply resumes nothing there, and only a trusted/orchestrator continuebuys another adjudication. It is the one of the three that retires no session as it buys one: the pre-spawn record already dropslate_session_idfor every run that is not continuing a question a human has answered, so what the command pays for is a fresh conversation before the agent starts rather than a retirement taken beside the grant. The run happens in the issue's OWN worktree rather than a scratch checkout of the base branch, because the diff it is asked about is between two commits nothing has pushed. What reaches the coordinator is the first thing adecomposingtick asks (_late_adjudication_owns_the_tickonstages/decomposition/run.py): an issue whose record carries a live generation belongs to it entire, and no step of the initial decomposition runs for it. It is asked on every tick of that label rather than only the ones that look late, because the reconciliations it opens with are owed by exactly the records the gates below it route past. What PUTS an issue there is the size gate below, and the two refusals beside it keep anything else from deciding a live generation: one stopsDECOMPOSE=offfrom routing an unadjudicated candidate to implementation, and one stops the dispatcher handing a hand-relabelled issue to whichever stage the new label named. - Late developer revision. Guidance a human writes about an oversized candidate is not a decomposition question,
so it does not go to the late adjudicator: the work itself has to change, and the session that wrote it is resumed
against the guidance in the worktree the candidate already lives in
(
workflow/stages/decomposition/late_revision.py). It runs underagent_role=developerandstage=decomposing, because that is what it is and where it happened — the issue never leavesworkflow:decomposing. The prompt quotes the issue's CURRENT title and body beside the guidance, because a resume is exactly the case that cannot see them: the replayed transcript holds the issue as it read when the work started, and the commonest reason to be here is that a human edited it since. The budgets are the ones that already exist: the resume budget and the session rotation behind it belong to the shared developer resume this goes through rather than around, and the per-issue daily retry cap counts fresh spawns, so a resume driven by a human's reply is an unblock signal rather than a retry exactly as it is in every other stage.
What a resume re-parses, and why the pin is the full spec rather than the backend alone, is in
command-specs.md#in-flight-session-lock. The two conversation stages'
prompts and round contracts are in conversations.md.
The lifetime agent-run ledger every role spends from¶
Every role above reaches an agent through one boundary, and the boundary charges the issue before a process exists
(../state-machine/labels-and-state.md#the-agent-run-circuit). What it counts is
processes, not ticks and not rounds: a reviewer round that requests changes and resumes the developer inside the
same tick pays for two, a developer resume that lands on a transcript the backend has lost pays for the fresh spawn
it buys itself, and a run the shutdown sweep killed or an operator paused mid-flight pays like one that finished —
each cost the same minutes of somebody's compute, and only the disposition is thrown away.
The ceiling is MAX_AGENT_RUNS_PER_ISSUE (default 50, 0 = unlimited), or the issue's own agent_run_allowance
where a human has bought it one. It is a lifetime total, so nothing gives a run back — and in particular none of
the counters a role does reset is this one:
review_roundgoes back to zero on a pushed base rebase and on a recovered conflict, soMAX_REVIEW_ROUNDScan be spent over and over on one issue;- the 24h
retry_countwindow empties on the clock, soMAX_RETRIES_PER_DAYis spendable again tomorrow; dev_resume_countreachingDEV_SESSION_MAX_RESUMESretires the transcript and the next round spawns fresh;- an authorized restart of a cancelled late cycle projects a whole fresh cycle over the pinned comment, dropping
every counter it is not told to keep —
conflict_roundamong them, which nothing short of a restart resets — and the allowance and the spend are two of the few it keeps.
So an issue can move between workflow:implementing and workflow:decomposing, be adjudicated once per committed
candidate, rotate sessions, and answer review round after review round without any of those loops ending — and the
ledger is the one thing counting them. When it is spent, the issue parks on agent_run_limit and the dispatcher
holds it ahead of every handler above; only a trusted, bounded /orchestrator add-agent-runs N widens what it may
still spend (../state-machine.md#per-tick-flow-workflowengineticktick).
The caps each role already had still refuse first — a cap that fired only after the charge would spend a run on work nothing ran — so a spent review cap, conflict cap, or daily spawn budget parks its own way with the lifetime count untouched.
The size gate a committed candidate passes¶
No agent runs here. This is the seam that decides whether an adjudication happens at all, and it sits at the one
place every clean committed developer outcome publishes through (_publish_committed_work on
stages/implementing/candidate_recovery.py, measured by stages/implementing/late_gate.py) — a run that
finished, a
timeout that had committed before it was killed, and a branch a crash stranded all reach it, which is what makes the
measurement a contract rather than a check.
Only a CLEAN tree is measured, and clean is proved rather than inferred from an empty answer. A candidate
measured beside uncommitted changes is not the candidate a push would publish, and the diff it would be adjudicated
on is not the one a human would read, so a dirty tree parks exactly as it always did and never reaches the gate. A
tree git status could not report on is refused there too, under its own unreadable_worktree park: the list form
of that read maps its own failure to "no paths", which is the answer a clean tree gives, and a seam whose next step
is a push may not rest on a probe that never ran.
The order is the failure contract. The candidate is proved to be a commit this host holds (resolved and peeled, so a
revision this repository has never seen is work made somewhere else rather than a head to stand in for it), the base
is frozen from what the remote says the branch is at (not refs/remotes/..., which lives in the object store the
agent's own worktree shares), and both are persisted with late_phase=measuring BEFORE a line is counted. A tick that
dies over the count comes back to the pair this one froze rather than to one re-derived from a branch and a remote
that have both moved, and the same freeze holds the branch out of the ordinary base refresh for as long as the
generation lives.
A resumed run is judged against what it started on, not only against what the branch inherited. The floor says
which tip the branch was already carrying — a certified handoff, a frozen candidate, or a commit an approval owes a
push for — and before_sha says which tip this run began at, and those are different commits whenever a human's
guidance resumed a developer on top of inherited work. A head that has not moved since the run began is a run that
committed nothing, whatever else is on the branch: published on the ahead-of-base reading alone, the developer's
clarifying question would be dropped and the very commit the human was still deciding about sent to review.
Both are comparisons, so both ends have to have been read, and a run either end cannot be established for
publishes nothing. _head_sha reports its own failure as "" — the one value that cannot be a commit — so an
unread end differs from every commit there is, and the difference belongs to the probe rather than to the run. On a
branch that arrived already ahead of base (one a read-only relabel certified, one a size-gate park left a candidate
on, one guidance resumed a developer over) that difference publishes work the run never made, or hands the gate a
candidate nobody wrote. The refusal parks a finished run's commits behind a reading nobody got, which is a bounded
cost — the commit is still in the worktree and the park says so — where the other way round pushes somebody else's
work under this issue's name.
What the gate hands back is the commit, not merely its permission, and the push is named against it. The gate
reads the checkout and the publication writes it, and HEAD between those two moments is not necessarily the commit
that passed — another tick, an operator, or a descendant the timeout cleanup raced can move it — so a push that named
nothing would publish work no measurement ever saw while the record named the commit that did.
The checkout has to be on that commit too, and this is the one refusal here a human answers without writing
anything. The push would be safe on its own, but every stage past the handoff works from the worktree — the reviewer
reads a head ahead of the pushed branch as unpushed work, the squash rewrites what is on it, the docs pass commits on
top — so a checkout that has moved is parked as late_candidate_moved rather than handed to review. It is asked
twice, before the push and again once the pull request is open, because those three requests are where the window
is: the worktree is writable while they run, and a descendant the timeout cleanup raced is the commonest thing to
move it. The second refusal does not take the publication back — the commit is on the remote and its pull request
carries it, which the record says — it stops the handoff, so review never reads the descendant.
Both boundaries ask about the tree as well as the head, and for the same reason one step over. Loose work can
appear beside a commit without moving it, so every proof about the commit passes while the checkout stops being what
the gate measured — and the stage the handoff passes it to takes no reading of its own, so uncommitted work that
slipped in reaches the squash and the docs pass, which commit it or destroy it. A tree that is dirty, or that could
not be read at all, takes the same late_candidate_moved park on the same terms: nothing published before the push,
and the publication left standing with only the label withheld after it.
One commit is decided on before any of it, and everything downstream is named against that: the push carries it,
the receipt the handoff leaves says it, and the post-push proof asks the checkout for it. Where the gate proved a
commit, that is the one; where it did not — a candidate the switch kept out of the gate — the checkout names it,
because a push named against nothing publishes whatever the branch has become by the time git runs it and leaves
nothing on the issue afterwards saying which commit that was. And a checkout that cannot name one is refused rather
than pushed as it stands: with no name there is no receipt and nothing for either proof around the push to hold it
to, so it parks as late_candidate_moved and waits for a repository somebody can read.
The intent is made durable before the push and only
where the record does not already carry it, so the roads an approval or an adjudication already wrote pay nothing
and the rest pay one write: a tick that died between the push and the handoff would otherwise leave a published
branch and a record saying nothing was ever owed. The approved
commit is on the record as late_approved_sha before any of this, because by then nothing else on the issue names
it: the generation is retired ahead of the effects it licenses, on purpose. So the park has a way back that costs
nothing. Every tick
asks the checkout one local question against that record and says nothing until the answer changes, and a worktree put
back on the approved commit publishes on the next poll — no reply, no guidance, and no second developer run over work
that is already committed and already measured. It also freezes the branch out of the pre-tick base refresh, because
that is what makes the remedy reachable: a rebase between the operator's git checkout and the tick that would have
noticed moves the head off the approved commit again. A worktree deliberately left on the descendant is measured as the
fresh candidate it is instead.
What a record already names is what a later tick reconciles, and the current head is never a substitute for it.
Where late_candidate_sha is set, that commit is proved first: a host that cannot peel it is one the work was not
made on — a rebuilt checkout, a machine the branch never reached — and it parks rather than measuring, adjudicating,
or publishing whatever the branch points at there. DECOMPOSE=off does not license publishing it either; the switch
decides what is measured, not what may be pushed unproven. Only once both commits are proved present does a head that
differs mean what it usually means: the developer was resumed on a human's guidance and committed again, which is a
fresh candidate under a fresh generation of the same cycle — and, with the switch off, one published while the record
it supersedes is retired rather than left freezing the branch over work nobody will publish. The recorded base is
retried the same way: the object it names is asked for again (fetching once for it), never the remote, because a
remote re-read would answer with wherever the base branch has moved to and measure a different pair under the same
generation. That is why a base the remote named but this host could not read is recorded beside its failure — the
id is the only thing a retry has to ask for. A count already on the record is acted on only once it is a WHOLE
measurement: a base, a ceiling, and a boundary beside it, because the record's own comparison answers "not oversized"
on a missing threshold — which is a damaged record publishing as a small candidate — and because a count whose base
this host cannot show is a number with its evidence missing. The identity carries the same weight and is asked
through the late domain's own record gate, so what a measurement may be acted on under is exactly what a record of it
may be written under: a cycle, a generation, a root, and a late_current_issue naming this issue. Nothing
downstream reads those fields, which is why they are easy to lose and why losing them fails open — a count that
publishes but cannot be correlated is a reading no operator can defend afterwards, and one recorded against another
issue is not this issue's answer at all. Anything short of that parks for repair rather than being read as an answer,
and the refusal is reported under a minted identity rather than under the record it is about, so a damaged pinned
comment cannot take its own refusal down with it. That repair is the reporting identity every late refusal here goes
through, not the damaged-count case alone: the evidence refusals taken before any measurement — a reaped worktree, a
recorded object this host cannot show, a base it no longer holds — would otherwise emit nothing at all for a record
whose root is gone, and would file the failure against the wrong issue for one whose late_current_issue names
another.
The same rule reaches one step before the gate. A tick that recorded the pair and died before counting or parking it leaves a frozen candidate with nothing on the issue saying the workflow is waiting, so the record is reconciled ahead of any spawn: on a host that cannot show that commit — a rebuilt checkout, a machine the branch never reached — the issue parks asking for the worktree rather than paying for a second developer over work the first one finished. The checkout has to be on that commit too, because no developer ran on that path: a head past the record is a resumed developer's new work only where a developer was resumed, and everywhere else it is a checkout somebody moved.
And one step after the verdict, where the same window opens with the record already gone. Every approval — the
retirement a small candidate earns, and the exemption an authorized settlement writes — drops the record that named
the commit and licenses a push that has not run yet, so late_approved_sha goes down in that same write and the
commit it names is proved before anything spawns. What is proved is the head, not the object: holding the object
says only that the store was never pruned, and the store outlives the branch — a worktree rebuilt or reset on the very
host that made the commit still has it sitting in the object store it shares. The proof is taken once the checkout has
been restored, which is what tells the two hosts apart: a commit the branch already carries comes back with it and the
tick proceeds, while a checkout standing anywhere else parks as late_candidate_moved — nothing published, no second
developer, and the same quiet republication settling it once the worktree is back on that commit with a provably
clean tree around it. What that park leaves standing is the whole approval group. Where the seam has to mint one — a
candidate a receipt or an exemption admitted reaches this refusal with nothing recorded yet, since the intent that
would have written it is never reached — it goes down through the same owner the publication mints one with, so the
two of them leave one debt rather than two. The lease inside it is what the side of the gate it was taken on can
promise: an INITIAL publication leaves it empty, since its push is the one that opens the pull request and reads the
remote for itself, while a push onto a pull request the remote already carries writes the commit AND the head to pin
the republication against — both being the commit that just landed, which is where that push left the branch. Half a
pair there is not a smaller debt but a claim the reconciliation refuses as damage, under a reason only a human clears,
so restoring the worktree would never finish the retry this park documents.
Both halves are asked there, or the republication would walk straight back into the refusal
the park was taken on and post a fresh notice every poll for a checkout nobody has touched. The record is spent by the
handoff that pays the debt, and spent durably before the relabel: past that label the issue belongs to
validating and implementing never runs on it again, so an approval still standing there is one nothing will ever
drop — and it goes on freezing the branch out of the base refresh for the rest of the issue's life. The same write
records which commit the push carried, because the label is the one effect that can fail on its own: refused, the
issue is still implementing with its branch pushed and its pull request open, and that record is what stops the next
tick re-deciding a published branch and what has it reuse the pull request and land the label instead. The head has to
be what is asked,
because a branch with no commits ahead of base reads as an issue with nothing to publish: a checkout holding the
object while standing on the base would be handed a second developer run for an implementation already written. An
adjudication takes the record off again, since a candidate being decomposed is one nobody is publishing yet.
Answering a recorded reading is never new work, and that is what keeps DECOMPOSE=off from failing open on it.
A tick answering a reading a previous one recorded — the bare-continue retry, the stranded pair — reaches the gate
with the switch off exactly as it does with the switch on. Publishing the current head there would publish the very
commit whose reading is what somebody asked for. New work is still bypassed; a question the gate already asked is
still answered.
The claim is narrower than no developer ran, and the two are separate facts for that reason. A base rebase, a conflict resolution, a divergence publish, and a recovery push each reach the gate with no agent behind them and with nothing on the record asking for their commit to be read — fresh work, which the switch off publishes unmeasured. Answering the switch with the wider fact would measure them, which is the same failure one direction over: an install that turned the gate off having its branches routed into an adjudication it never opted into.
Every refusal keeps the identity it managed to establish, and one that established none refuses the retry outright. A revision that resolved and would not peel — an object a prune took, work made on a host this one is not — comes back carrying the id it resolved to, and that id is recorded with the park: from there the retry asks for that exact object, the pre-tick base refresh holds the branch still around it, and the reconciliation ahead of the next spawn proves it before anything runs. A revision that would not resolve at all names nothing, and there the park itself is the record. Its bare continue is refused rather than answered, because there is no pair to re-read: what a retry would take is a first reading, of whatever the checkout points at by then, and nothing ties that head to this issue — a rebase, a reset, or a rebuilt worktree each leave one. The way on is the developer, which is what guidance buys. The park holds the branch out of the base refresh for as long as it stands, for the same reason: rebased under it, the frozen commit is gone and the checkout the refusal was protecting is standing on the base, so neither the exact-pair retry nor the refusal has anything left to be answered from.
A reconciliation stays bound to the recorded pair for the whole tick, not only at the door. Both roads prove the head against the record before they start and the gate reads it again a moment later, and the checkout is writable in between — so a head that differs on the second reading is one something moved mid-tick, not a run's output, because no run of this tick exists. Read as fresh work it would be measured and published with the switch on, and pushed unmeasured with it off. So the second reading is refused exactly as the first would have been: nothing is published, the recorded pair stays for the retry, and the ordinary disposition — where a moved head really is a resumed developer's new commit — is deliberately not held to it. The refusal comes before the head is asked whether it is readable, because a head that moved onto a commit this host cannot peel still names one — and a named commit handed on from there is one the park downstream records, minting a generation around it and dropping the very pair the retry exists to re-read.
What the number earns:
- At or below
MAX_ADDED_LINES— the ordinary publication, unchanged. The generation is dropped in the same breath, because a frozen candidate freezes the branch and a record carried into the stages that close the issue reads as a live cycle a close should end;late_retired_cycle_idoutlives the drop, so the next candidate on this issue mints a cycle after it rather than reusing the number. That drop is also the barrier: past it come a pushed branch, an open pull request, and a relabel toworkflow:validating, none of which may happen on an issue a human has closed. A latched close is asked for ahead of the write, and the write is held inside the observations owner's retirement window so a close arriving as the record stops naming its cycle is answered behind it — the generation goes back from the call's own memory and is cancelled from there, with nothing published to take back. - Strictly past it — nothing is pushed, no pull request is opened, and the label moves to
workflow:decomposingwith the measurement durable ahead of it. The commit stays exactly where the developer left it, which is where the adjudicator reads it and where a settlement that publishes it unsplit would publish it from. A tick that dies between the write and the label leaves a live generation underworkflow:implementing, which the dispatcher's relabel guard puts back.
The same three answers stand one seam later, where a pull request already carries the work. Every push onto one is
measured first — the reviewer's requested changes, a human's reply, a body edit, a stranded commit a bounce
republishes, a deferred push or a timeout's commit a recovery finishes, all three conflict publications, the base
sync's own auto-rebase and crash recovery, and the final docs pass —
and measured for what the pull request would come to rather than for what that push adds: the pair is the frozen
remote base and the candidate, so what a reviewer would open and read is the number the ceiling is applied to, and no
branch can be grown past it one small fix at a time. What such a call has to freeze first, and could re-derive from
nothing afterwards, is which publication it was entered on: the stage the gate is taking the issue out of, the pull
request the work already has, and the head that pull request is standing on. A candidate at or below the ceiling is
pushed as it always was — named against the commit that was measured and leased against that frozen head, so neither
a checkout that moved nor a pull request somebody pushed to can turn an approved reading into an unmeasured
publication. That head outlives the record it came from: the write that approves a candidate retires the generation
while the push it licenses has not run, so the head rides on the approval and pins every later push for that commit —
the retry after a failed one, which skips the measurement because the commit is already approved, and the ordinary
publication a settled verdict hands it back to. One past the ceiling is held with the pull request left exactly
where it stood, and the issue is adjudicated from whichever of workflow:validating, workflow:documenting,
in_review, workflow:fixing, or workflow:resolving_conflict that push was reached under — or, where an exemption
nothing authorized names the commit, parked late_unauthorized_exemption on that same stage, since a change already
ruled one change is owed the operator rather than a second adjudicator. A tree that is not
provably clean, a pull request nothing could read, one that is closed or merged, and a head that moved off what a
live record froze are each a refusal rather than a reading: nothing is pushed and a human is asked.
The freeze that precedes the count is a step, not a window. A tick that dies between the two leaves a pair with no number on it, and the next tick answers that reading before its stage runs at all — on the stage the record names, with no developer having run, so a checkout that has left the recorded candidate is refused rather than measured. It is the same contract the implementing recovery keeps, asked one seam later. A checkout that is not on this host at all stops the tick rather than letting the stage carry on: the commit is on a machine this one is not, so the pair keeps the branch and the record exactly as they are and a human is asked once.
None of what follows is what an adjudicator's single earns — that parks — so read it as the road a human's decision
to publish the candidate unsplit would license. A settlement of a candidate taken past publication publishes it itself
and then continues at the stage the record names, rather than sending every one of them back to implementing: that
stage is the only owner of the completion the candidate still owes, and the settlement is the last tick holding the
head the reading was taken over. The one exception is an auto-rebase replay the generation took over, which continues
at workflow:validating as every rebase this orchestrator publishes does, since its report and its review are owed
there. It proves its pull request before either — and a pull request already standing on the
accepted candidate is that proof answering "this settlement's own push landed and the tick died before the label", not
"somebody moved it", but only where a durable record vouches for the push: the approval written in the write ahead of
it, or the receipt the push itself leaves read with the head that receipt replaced. On a fresh pass neither is written
and nothing of this workflow's has reached the remote, so that same head is something ELSE having pushed — an agent
that published its own commit is the plain case — and it refuses with every other moved head. The receipt needs its
head because it is never cleared: an accepted candidate published in an earlier round is one it goes on naming, and a
pull request rewound onto that commit would otherwise agree with every local fact there is. It needs the pull request
it went onto for the same reason one step further out: a branch pushed from that head onto a publication since closed
and replaced by another on the same ref dates a receipt to this settlement while the push it records went somewhere
else, so the number it names has to be the one the verdict was frozen on. Vouched for, the retry
finishes what it never reached instead of refusing the publication this settlement made. A pre-publication one
searches for the pull request its commit is on and drops a recorded pointer that turns out settled, because losing it
costs nothing — the publication opens the pull request the work needs. This one already knows which pull request the
reading was about, so it checks instead: still open, and still standing where the reading found it. A check that fails
parks. Dropping the number there would push onto a branch whose pull request a human settled and open a second one for
a change adjudicated against the first, and publishing over a head that moved would publish on a reading the branch
has already overtaken.
- Answered small after a revision — the one record that wears
workflow:decomposingwith nothing left to decompose. A developer revision guidance bought comes back re-frozen and re-measured, and a candidate now at or below the ceiling has had its question answered: the label goes back toworkflow:implementingand the ordinary publication reconciles the exact commit already on the branch. Handing it to the initial decomposer instead would re-plan an implementation that is already written. It owes the pull requests the same two reconciliations an accepted verdict does, and for the same reasons: the "do not merge" notice comes off the held PR before anything else moves — a refusal parks underdecomposingwith the record untouched — andpr_numberis moved onto the pull request the measured commit is on, or dropped where the recorded one is settled, since a merged plan PR carried across ends the issue asdoneon a design. The record is kept across that handoff and retired by the gate on the other side, which is what makes it recoverable — it is the only thing saying the question was asked and answered, so dropping it before a label write that then failed would leave an issue nothing could tell from one that never entered the gate. - No reading at all — never "small". What a failed
gitinvocation writes to stdout is what a candidate that changes nothing writes, so a typedlate_failurecarryingmeasurement_failedgoes to both sinks — for every refusal, including one taken before a generation existed, which is reported under a minted identity — the issue parkslate_measurement_failed, and the pair that was frozen stays on the record. The record says the same three things the notice does: the family, themeasurement_failurestep it stopped at, and thedetailline that step wrote, so an operator reading only the stream can tell these apart the way the human on the thread can (../observability/event-streams.md). A base the TRANSPORT could not reach is the one exception, and a bounded one:base_unreadableandbase_absentclear themselves, so the first three consecutive misses on a pair count one onlate_measurement_miss_count, emit that same typed failure, log at WARNING and stop with nothing parked and nothing said — the next tick re-reads the pair by itself, spawning nothing — and only the fourth asks a human. Every park here asks once per thing there is to say, whatever its cause: a reading retaken with nobody asked and stopping where that park's own notice already said it stops — which is whatlate_measurement_failurerecords, written by the roads that tell somebody and by no other — is held silently, while one that stops at a different member is announced once and takes its place. What retakes it is the road the gate was entered on: past publication the reconciliation ahead of every handler re-reads the parked pair once a poll, which is what that silence is for, while before it the park owns the tick until the bare continue below arrives — and that reading, being a human's answer, is announced again if it misses. A base this host does reach ends the run of misses in the write that records the pair, and the member beside the count stays, since reaching the base is not the last step a reading can stop at. Both go together, with the park, on the verdict a reading that finally lands settles. Every notice names the member and explains it in a line written for the operator — which of a remote, a token, a checkout, or a planted attribute file they are looking at, and, for the remote read, the fetch and the two diff steps, theorchestrator.git_plumbingchannel their invocation is logged under — with whatever the failing step wrote for itself, scrubbed, carried up beside it. A trusted bare/orchestrator continuere-measures exactly that pair and re-publishes through the same seam; it spawns no agent, since the developer that produced the commit finished long ago — and because no agent ran, a checkout that has left the recorded commit is refused rather than measured or published, under either switch setting. Guidance is the opposite reply and buys the opposite thing: the developer is resumed, and what it leaves is judged against the floor the park left on the branch rather than against the base, so a clarifying question is answered by parking on it rather than by publishing the very work whose size nobody could read. A worktree that is gone is answered by that same reconciliation rather than handed on: the generic parked-continue classifier would refuse the command as carrying no guidance, which is the wrong thing to tell an operator whose command is exactly right, so the park is re-taken saying the recorded commit is not on this host and the next continue retries it once the worktree is back.
Six candidates skip the measurement and none is a bypass. Four are commits this workflow has already decided
about, and each names one commit and only it, so work committed on top of any of them is measured as the fresh
candidate it is. late_exempt_sha names the commit an authorized settlement accepted — never an adjudicator's own
single, which parks for that decision rather than making it — and it is asked as a PAIR with the late_override_*
group beside it, because an exemption alone records a DECISION rather than a decider: the adjudicator that wrote it
is an agent, and an agent proposing to publish past the ceiling is what the ceiling is there for. The pair is proved
rather than parsed, since every term of an authorization but the digest is the pinned comment agreeing with itself:
the contribution between the pair the record names is fingerprinted again here and held to what that record says.
Only the exemption, and the candidate goes to the ordinary cumulative reading —
implementing/late_authority.py is where the two are asked together. Between the settlement and the publication the
exemption also holds the branch out of the base refresh — but on two conditions, since the record is never cleared
and freezing on its presence would take every issue that ever earned a verdict out of the refresh for good. The head
has to still be that commit, and the stage that has to act on it has to still have the issue: past the handoff the
branch is review's, and keeping a pushed branch in step with base is the PR-aware sync's job.
late_approved_sha names the commit the gate itself approved and has still to push, which a crash
in that window brings back here with its generation already retired: re-deciding it there would measure a settled
question against a base that has moved since, and route work a human may already have adjudicated back into
adjudication. A debt the EXEMPTION left is the one approval that is not the gate's own and is read as that exemption
one field over, which late_authority decides off late_approved_basis. implementing_published_sha is that same
window one step further on and the one that matters most,
because the effects are already out: past the push a pull request carries the work and only the relabel is owed, so a
reading that came back oversized there would hold nothing back and route a published branch to adjudication. The
fourth is the one an unauthorized exemption still earns: a commit the pull request this call FROZE is already
standing on, where the push would move nothing and what would be held back is the bookkeeping behind a publication
that has happened.
That window is real and nothing closes it, but the receipt does not prove the tick is still in it. The note says
what this stage last pushed and nothing about where it went or whether it is still there, and it is never
cleared, so a branch published rounds ago carries one for the rest of the issue's life. So the same proof stands
behind it on every road, and which reading takes it differs by seam. A call taken past a publication hands over the
pull request it froze and the head that pull request is standing on, and the receipt is held to BOTH: the head says
the work is there, and the number says it is there because of the push this record is about. A receipt naming some
other publication — an earlier one of this issue's, a replacement somebody opened after closing the original, a
record a hand edit pointed elsewhere — vouches for nothing, and the candidate goes back through the ordinary
reading, which is the safe road: measured, then pushed leased against the head that call froze. The implementing
seam froze neither — its push is what opens a pull request
— so there the remote is read for the number the record names, and it answers only for an open pull request whose
head is in this repository, standing on this exact commit, and open on the branch that seam would push. Every
half is required, the LEASE included: a lease names the head a push replaced and only a call that froze a publication
writes one, so a receipt carrying one at this seam was left by some other call and its commit matching the candidate
is a coincidence — waved through, the fresh receipt behind the push clears the field that said which attempt the
record was about. Without the receipt, any head a remote happened to agree with would do; without the remote reading,
a note over a pull request the branch has moved off would do; without the repository, a fork — which carries this
repository's ref names over its commits — would agree on everything else while naming a publication this issue never
made; and without the branch, which the seam resolves from the record for itself, a pr_number left over from a
cycle on another ref would license a push where nothing has published and a second pull request opened over the same
work.
A proof that fails parks rather than handing the commit back to the ordinary reading, because that reading is not a neutral answer: a count under the ceiling publishes, which force-pushes a branch nothing could confirm and opens the second pull request by another road. The park writes nothing else — the receipt, the recorded number and whatever push is still owed stand exactly as they were, for the terminal that drains finished work or for the retry behind a repair. Nothing is outside it — an exemption or an approval naming the same commit least of all. Each answers whether the candidate needs a fresh reading and says nothing about where the work went, which is the question that failed; and the road that admits a delivered candidate records the commit as a debt before it pushes, so a tick dying there leaves an approval with no lease beside it. Waved past on one, the retry publishes with nothing to lease against and reuses whatever pull request a branch lookup finds.
Which pull request is the receipt's own, written with it by the push that landed rather than by the relabel behind
it, and it is never searched for. pr_number is the relabel's write, so it is missing for exactly the window this
proof exists for — a push that landed and a process that died before it — and the only other way to name one is a
lookup by branch, which answers with whatever is open on that ref: a replacement somebody opened after closing the
original satisfies every other term, and the relabel, the debt and the receipt would all be spent against a
publication this stage never made. So a group that cannot produce a readable one is damage rather than a question
the proof can answer, and what that costs is a park a human repairs. It is refused one step earlier, with every other
shape a group this build cannot read whole takes, and told apart from a group that claims nothing at all: every late
field reads fail-closed, and published over, the push would write a fresh group across the damage. The
three are one record — written in one call, cleared in one call — so the check is over all three, and it stands at the
gate's own door rather than beside the candidate question: every road out of a gate call ends in the write that puts a
fresh group down, and the one an install with DECOMPOSE=off takes answers before that question is reached. The
accepted settlement, which reaches the transport by another door entirely, is refused by its own reconciliation on the
same terms. It asks presence as well as value: a member whose key has gone while its siblings are there is a hand
edit rather than the null an initial publication writes for the head it froze none of, a member carrying a value
nothing can read is damage, and so is a group that names no publication at all — a commit with no readable number
beside it, or a lease or number with no readable commit. That last pair is the one a commit-only check walks past,
since a receipt naming some other object id is never compared against the candidate and the next push completes the
partial group rather than leaving it for a human.
And because a reading is a moment rather than a guarantee, the answer carries the pull request number rather than a bare permission. The push behind it is leased against that exact commit, so a branch somebody moved in the window rejects it instead of being force-overwritten, and the bookkeeping is bound to that pull request by number, so one somebody closed in the window holds the tick instead of earning a second pull request over work the first already carries — and that last reading re-takes the whole identity rather than the open state, off one fetch and against the object it hands on, because what it writes is a receipt naming this commit and a relabel handing a reviewer this pull request. One somebody merely MOVED in that window is open and is not the publication the proof was about. A call taken past a publication is held to the same repository and branch one step earlier: the entry freeze refuses a pull request whose head is in somebody else's copy, and one open anywhere but on the branch its push will name.
The publication that window is about is re-read immediately before the push, on every road that makes one. The
ordinary implementing push proves no publication — it reuses whatever open pull request is on the branch, which is
how a discussion plan PR on that ref, or this stage's own from a round that crashed before its relabel, gets
carried forward — so the pull request it would join is the one the record names. One that merged, closed, or stopped
being readable between the tick's first reading and there answers nothing to the branch lookup behind the push: a
second pull request is opened over the work and pr_number overwritten with it, losing the pointer to whatever a
human just decided. The road the gate PROVED a publication for hands that number in instead, and that one is read.
What that record fails to say is read three ways rather than two, and the third is what keeps the barrier closed.
An absent pr_number is an issue that has published nothing: the implementing seam's own push is what opens a
pull request, so there is none to hold it to, and the window before the relabel that records one leaves the same
shape. A field the comment carries and no reader here will type — the payload is JSON, so a hand edit or an older
write can leave a zero, a flag, a string, a list — is the record disagreeing with itself. Every identity in this
domain is read fail-closed, so an unusable one comes back as no identity: taken for an absence, the barrier spends
no request and the push goes out as though the issue never had a pull request, opening a second one over work a human
may have just closed. So it refuses, and on the published-side roads an absence refuses with it, since every one of
them publishes onto a pull request the remote already carries.
One ending is deliberately not one here. A discussion plan PR the humans have settled — merged or closed — is an
agreement rather than a delivery, and it is the same carve-out the stage's own terminals make: finalizing on a
merged plan would close the issue done with no developer ever having run, so the tick carries on and what it
licenses is an implementation with a pull request of its own. That plan is therefore not a pull request this push
joins, and never one it may be held back by. It is told from an implementation by the two records those terminals
use — a live discussion_plan_path, or a discussion_plan_sha the PR's head is still standing on — read off the
reading already in hand, so a plan this stage has since pushed over reads as the implementation it has become and is
held like any other.
Two things bound that. It has to be settled, which only a reading establishes: a pull request this host could not read is refused ahead of the carve-out, because the record says which one is the design and never what anybody did with it — carved out on the record alone, the push would go out onto whatever the failed request was hiding. And it has to be a number the record named: one that came from a proof is never offered the carve-out at all, since what a proof establishes is that the branch is standing on the candidate, which no plan publication produces.
And DECOMPOSE=off keeps every new candidate out of the gate — but not one this issue already has a
recorded generation for, nor one it owes a push for, because the switch decides what ENTERS the gate and nothing
about what is already in it or already through it. Bypassing an approved commit would be the sharpest of those: the
publication is handed a candidate the gate never looked at while the record beside it names a different commit as the
one still owed a push.
The sixth is the only one no record names in advance, and the only one that has to earn its way past the reading: a
workflow rewrite of the exact commit an authorized settlement accepted. Three of them replace that commit with an
object carrying the identical contribution: a squash on approval, on the last push before the merge button; the clean
base rebase the per-tick refresh force-pushes once the stage that had to act on the exempt commit has handed the issue
on; and the replay workflow:resolving_conflict runs when a branch has stopped merging cleanly, which is the one
rebase that refresh never drives because it does not own that label. The one-commit rule that makes the exemption safe
is exactly what stops it answering for any of the three — so the same change would be measured past the same ceiling
with a pull request already open over it. Only the owner that RAN a rewrite may describe one: a resolution an agent
wrote over conflicted files, a developer's fix, a reviewer-fix round, the documentation pass, and whatever an earlier
tick left for the conflict stage's ahead-only recovery to find are all commits made by somebody other than the caller
pushing them, and no reading off the branch tells those from a replay. So the conflict rebase writes down what it is
about to replace -- that head, its fork point, and the pull request it is being made against, recorded before the
replay, and the commit it produced, stamped on before the size gate is entered -- and the recovery is answered from
that record or measured, never from a probe. late_transfer.py is what it is earned on: a whole semantic record whose
exempt commit is the one the rewrite came from, evidence naming a bounded rewrite kind from a stage that really makes
that kind — the two are one claim, and a conflict_rebase offered from validating types in both halves while naming
a rewrite that stage does not make — and both pre- and post-rewrite pairs, no authorization this build cannot read
already standing for that exemption, the publication this call itself froze and the one the issue still records, a
provably clean checkout standing on the rewritten commit, a leased head that peels to a commit this host holds — the
one end nothing else here reads as an object, since the lease may name a different commit from the accepted one — an
issue re-read and found unchanged — open, carrying no paused or backlog, and still on the stage the rewrite
recorded, since the entry read that stage off the issue the tick opened with and a relabel during the rewrite is
invisible to every other reading — a rewritten base the base branch really carries, frozen from what the remote
says that branch is at and held to reachability rather than to equality, since the branch advances on its own while a
base carrying work no remote has is one only this host ever saw — and canonical fingerprints that agree. Those
fingerprints are what the base proof is for: their equality says the rewrite contributes what was adjudicated over
the base it names, and choosing that base is what a rebase does, so a fork point taken against
refs/remotes/<remote>/<base> — a ref the issue's own worktree can repoint — would let a replay onto work no remote
has fingerprint as exactly the adjudicated change. The accepted one is re-taken over the pair
the record names rather than the pair the caller claims — so the record proves itself, base included, instead of
having its digest read back against an end nothing checked — and the caller's own claim about what it replaced is held
to that same digest, as is the digest any permission already standing there recorded: carried forward unchecked, a
grant would write its own reading over it. Granted, one durable write records the permission and the debt the push
is still owed, before anything is pushed — the exemption itself does not move there. That rotation belongs to
late_rotation.py, on the write that receipts the landed push, where the exemption, the identity it carries, and the
account of what the remote holds go down together or not at all, so a verdict is never left on a commit no remote
carries; a force-push the remote refuses puts the branch back onto the head the rewrite found it on — the accepted
commit where a squash collapsed it, the leased anchor where a rebase read it for itself — and what the rollback owes
is dropping the permission it will never spend, since the exemption never moved. Refused, nothing moves and the
ordinary cumulative gate measures it like any other candidate.
A crash between that grant and the push it licenses leaves the one approval that was never a reading this gate
took, and it is read as exactly that: an approval standing beside an outstanding permission defers back to the
permit, which is re-asked in full over the record the grant left — the recovery has no plan behind it, so both
pairs, the publication, and the lease come off the permission itself. Asked of the permission rather than of the
commit it names, because the two go down in one write for one commit and a hand-edited target would otherwise make
the permit invisible and leave the approval looking ordinary. It is also what tells a lost receipt from a moved
remote: a remote already standing on the rewritten commit, while the permission is still outstanding, is this
permit's own push having landed rather than somebody else's move, and remeasuring there would re-adjudicate a
squash the pull request already carries. The recovery republishes as the leased no-op it is, and the receipt behind
it settles the transfer the first tick could not — recorded as already_published rather than pushed, since which
reading proved the publication attributable is the one fact a local note can never supply. What ENDS the deferral is
that same receipt: past it the permission is spent, and a later approval on the same issue is the ordinary one
again. Until then the deferral holds, and where the permit refuses on the re-ask the ordinary gate measures the
rewrite like any other candidate — the same rule failing conservatively, at the cost of a reading rather than a
decision.
What the settlement turns on is the permit's own answer, carried down the push tail beside the commit it was given for. The permission on the comment is not a substitute: a refusal is not a hold, so the rewritten commit falls through to the ordinary cumulative gate and a count under the ceiling pushes the same commit — and a settlement that read the record alone would rotate a human's verdict onto a rewrite nothing revalidated, under the very digest the permit declined. A permission this tick's permit did not vouch for is left exactly where it stands: not spent, because nothing licensed the move; not dropped either, because the remote is now on a head the permit accounts for and a later tick whose refusal has cleared can settle it in full.
Given that permit, the settlement distinguishes three remotes and settles on two. Standing where the permit was
granted, the
leased force-push moves the publication and the receipt behind it carries the verdict over. Standing on the
rewritten commit already, the leased no-op proves the pull request still has this permit's own push and the same
receipt settles it. Moved anywhere else, the permit is refused before any of that, so nothing is spent and
nothing is reported — a reading nobody could take refuses the same way, and neither is ever read as equivalence.
A publication that went past an outstanding permission — some other commit reached the pull request — drops it
instead of spending it, on the rollback's own terms, since the head it was granted against is gone and no later tick
can be granted it. What a settled transfer leaves on both observability streams is one bounded late_transfer
record — the issue, the pull request, both pairs, the rewrite kind, and which reading proved the push — and
deliberately no second late_verdict, which would read as a second adjudication of work nobody was asked about
twice. That record is late_transfer_telemetry.py's rather than the rotation's, and the push tail asks for it on
the far side of the write the rotation stages into: a receipt GitHub refuses ends the tick having reported nothing,
and a permission left standing or dropped moved no verdict and so says nothing either. A process lost between that
write and the record leaves the proof the settlement kept, and the reconciliation ahead of the next handler reports
from it — best effort, since a sink that refuses the record loses it and a proof drop that does not land lets a later
tick report it again.
The base refresh's rebase of an accepted commit is recoverable at every one of those writes, and its recovery of an
unchanged contribution never falls back on a reading. A replay git produced before anything recorded it is vouched for
by what it contributes over the terms the attempt pinned before git ran; a push that never went out is reissued on the
permit alone, entered into the gate whatever DECOMPOSE says -- save a replay the attempt's record names, with no
grant made before the crash, whose re-derived evidence the permit refuses: its contribution changed, and it is
measured as the publication would have measured it; a push that landed with its receipt lost is settled through the
leased no-op; and a pull request that merged or closed over the attempt ends the attempt, its debt, and its permission
in one write, settling the permission where the pull request ended on the rewrite it names — on the record's own
binding, since that push is behind it and there is no permit left to re-ask. Every road over an unchanged
contribution finishes without an agent, a measurement, or a second adjudication, or parks where the record cannot
account for what it finds
(../state-machine/labels-and-state.md#base-refresh).
None of this widens the exemption. A rotation moves it from one exact commit onto one other, and only where the owner
that ran the rewrite proved two canonical fingerprints equal; equivalence is never inferred from a matching addition
count, a commit subject, a timestamp, tree or rename similarity, or a git patch-id, each of which can agree over a
different change
(../architecture.md).
A descendant of the exempt commit, a developer's or reviewer's fix, the documentation pass, an agent's conflict
resolution, a child a split creates, and a replay that changed one covered byte are each measured as the fresh
candidate they are, and an oversized one is adjudicated afresh. How the exact commit, the semantic identity, and the
rotation relate, which phases a rotation passes through, what each crash leaves behind, and what a legacy or damaged
record reads back as are mapped in
../state-machine/labels-and-state.md#exemption-identity-and-rotation.
The approval holds the switch back for the commit it names and no other, which is why the switch is asked twice — once at the door, cheaply, and once past the proof. An approval is a claim about one object id, and nothing can say whether the head is that id until the head is proved; past that proof and not it, the approval describes work this branch has moved past and the candidate in hand is new work, which is exactly what the switch keeps out. Asked only at the door, a stale approval would drag a resumed developer's fresh commit into a gate the operator switched off and route an oversized one to adjudication. The stale record goes with it: a publication naming a different commit drops the approval, because a debt recorded for a commit nothing is going to push freezes the branch out of the base refresh for the rest of the issue's life and parks every later tick asking for it back.
What a late adjudication is asked, and what it may answer¶
The late prompt (late_prompt.py) carries the original issue and its trust-filtered thread, the declared scope this
generation owns, the two frozen commits with the git diff <base>...<candidate> that reads them, the measured additions
against the configured ceiling, the lineage, and the fact that committed work already exists and is not to be rewritten.
Three dots, not two: that is the prospective pull-request range the measurement was taken over
(git/measurement/additions.py), and on a diverged history the two-dot range would put the agent on changes nobody
measured — deciding a split over work this candidate does not add. The child cap, the lineage bound, and the category
vocabulary are read back off the owners that enforce them, so a bound the agent is told cannot drift from the bound it
is judged against, and the two field names the prompt states — the explanation a single owes, and the addition budget
every proposed child declares — are read off the owners that read the reply back, so a key nothing reads is not one the
agent can be asked for. The per-child ceiling is this generation's own measurement rather than the configured knob, so
an operator retuning that knob while an agent is running cannot leave it sizing children under a bound nothing checks.
The reply ends in exactly one fenced orchestrator-late-manifest block — a different fence from the initial
orchestrator-manifest, read by late_reply.py — declaring one of three outcomes:
single— the committed work is one coherent change despite its size, and no safe split of it is available. It publishes nothing, and the prompt says so in those terms: the verdict REQUIRES a human decision, so it must say what that human is being handed. A diff dominated by legitimate generated or data artifacts is the named false positive and gets this verdict with"category": "generated_artifacts". The"split_blocker"beside it says what makes a safe split unavailable — the prerequisite that cannot be landed dormant, the artifact that cannot land apart from what generates it, the invariant a half-landed slice would break — and the pinned comment keeps it whole or not at all. It is REQUIRED: the prompt states it as an obligation and the parser enforces it, so a fresh reply that declares the verdict and names no obstacle is refused whatever its"rationale"says, because what would be recorded otherwise is the one answer a human has to act on with the one thing it turns on missing. The refusal is about a REPLY and never about a record — results predating the key are on live issues, still decide their candidate, and are read back with a fixed stand-in sentence rather than sent round for a second run to recover prose.split— a child manifest that partitions the declared scope completely, held to the same rules the initial mode uses: the child cap, each child's shape, and the acyclicity of the graph they declare. The prompt requires dependency-ordered implementation slices to be considered before asingleis answered, because that is the way out an agent otherwise talks itself out of: work that will not cut across features almost always cuts along its dependencies, and a prerequisite may land dormant — built, tested directly, reached by nothing in production — with its activation waiting for the last consumer that needs it, the recovery paths included, so no half-wired state is what a user or a later tick meets. Every child body owns its slice end to end, its own tests and documentation included, since a slice whose proof or description belongs to a sibling is not one. Beside that each child declares an"estimated_added_lines"budget covering ALL of its paths, and that one is enforced: on a fresh reply it has to be a real count of at least one line, strictly below the ceiling this candidate was measured against, and a manifest that does not clear it is refused — a split whose children are each still oversized is not a split. What is judged is whether the proposal is actionable, never whether a later measurement may disagree with it: what decides that a child is oversized is the cumulative measurement of that child's own diff, which is why the prompt asks for headroom under the ceiling for the review fixes that land on the same pull request rather than for a number that only just fits, and why the figure its JSON template shows is scaled to that ceiling rather than fixed — a template is copied verbatim, so a standing figure would be a refused child on every repository configured under it. The number itself travels: it is recorded with the manifest and stated on the child issue created from the slice, so a tick that crashed between the verdict and the transaction still opens children that say what they were sized at. The requirement is what stays on the late parser rather than moving to the shared split validator, which also reads recorded manifests back off pinned comments written before any budget was asked for.question— a categorized question for a human, which is also where artifacts that look like they should NOT have been committed go. The category is mapped onto the closed vocabulary, so an agent's own spelling records asunknownrather than widening the field.
The "rationale" — at most two sentences on why the work is one change, or why it divides the way the children do —
is optional on a single and a split and retained with either. The prompt says so, and says it is a separate
field from the "split_blocker" rather than part of it: the two answer different questions under different rules, and
an agent told its argument is thrown away would leave it out or fold it into the explanation, where it restates the
verdict instead of naming an obstacle. It decides nothing, so no reply is refused over it. It is written as
late_result_rationale in the same result write as the verdict it argued for and dropped with that result; a
question keeps none, and neither does a split refused at the lineage bound, which is recorded as the question it
became. One longer than MAX_RATIONALE (2,048 characters of the value, a bound the prompt reads off the record's
owner like its others) is stored as a prefix ending in the visible marker [... rationale truncated by the
orchestrator], inside the bound, instead of costing the verdict — while the explanation, the question, and the
manifest still go in whole, or the result is refused (late_result_unrecordable) with nothing of it written. A record
with no rationale a reader can use — one written before the key existed, a blank or non-string value, one past the
bound — is still this candidate's answer: no second run is paid for to recover the argument, and nothing writes a
stand-in, or anything derived from the explanation or the category, into the pinned comment. Where it is shown is
the notice an authorized publication posts (below), which quotes
the record rather than a reply, so a process that dies after the result write or after the authorization settles with
the same words and no agent run. It stays issue prose: no late event and no analytics record carries it
(../observability/event-streams.md), and the
field-by-field contract is in
../state-machine/labels-and-state.md.
Unlike the initial mode, prose alone is not an outcome: a reply with no block, or with more than one, parks for a
human rather than being read as the agent asking a question, because a late question has its own structured decision
to travel in. Automatic splitting stops at MAX_LINEAGE_DEPTH (3), and a split proposed at the bound is recorded as
the categorized question the workflow actually owes a human (lineage_bound) rather than acted on — so the next tick
asks the human instead of paying for the same forbidden split again.
A completed run is recorded whole so a crashed tick does not pay for a second one — a second run is not free, and it
is free to decide differently. What "whole" means is per verdict: a single carries the explanation of what stopped a
split, a question carries its category and the sentence it asked, and a split carries the ordered child manifest
that is its decision — rewritten from the fields a child issue is created out of, the declared budget among them,
since the issue created from a slice states the size that slice was proposed at. So a result this binary writes
persists every budget its reply declared, and a tick that crashed between the verdict and the transaction still opens
children that say what they were sized at. A manifest a live issue already carries was recorded before any budget was
asked for and reads back without one, which is no bar to answering: the rules a record is read through ask for none,
those children are created exactly as they always were, and their issues state no size, because nothing here invents a
number an agent did not give. The compatibility rule is the same for the explanation — results predating that key are
on live issues and still decide their candidate — and so is the other half of it: a fresh reply that omits either
field never becomes a record at all, because the reply contract refuses one.
Whether it fits is measured on the whole comment the write would produce — the preserved held-PR body and every other
stage's keys included — because a result small on its own can still be the one that pushes the comment past what
GitHub accepts, and learning that from the failed write means the agent has already been paid for. The budget is
one, and every verdict is held to it. What a verdict goes on to owe the thread is not taken out of what it may
record: a single earns the park a human's decision is owed on, and the refusal a smaller budget would produce is one
the next attempt supersedes — so charging it for the sentence it owes would buy another decomposer run against a
candidate already adjudicated and leave that single short of its own park. The obligation that makes the sentence
retryable is written into the headroom this budget leaves under GitHub's own limit, afterwards, and a fixed figure
covers it because that sentence names the recorded explanation rather than copying it. An outcome past the budget is
refused entire rather
than shortened, and the issue parks for a human; an incomplete one read back later is not an answer at all and the
adjudicator runs again. The record goes out before the question it announces does, so the narrow crash window between
them costs a repeated comment rather than a repeated agent run, and the next tick posts the recorded question instead
of earning it again. Which keys carry that, why a recorded answer names its cycle as well as its generation and
commit, and why the pre-spawn write leaves the retry counter alone are in
../state-machine/labels-and-state.md#the-late-run.
The read-only promise the prompt makes is proved rather than trusted. The late adjudicator runs in the developer's own worktree — the frozen candidate is not on any remote yet — and the CLI it runs under can write there whatever the prompt says, so before the reply is read at all the candidate is proved unmoved (HEAD still IS the frozen commit, not merely contains it) and the tree proved clean. An agent that committed over the evidence or left changes beside it has contaminated the one artifact every later step acts on: the issue parks for a human and the verdict is not used. That check sits ahead of the interruption refusal for the same reason the initial decomposer's dirty check does — a run the shutdown sweep killed can have written before it died.
None of it starts on a generation that cannot be acted on. The prompt names both frozen commits and tells the agent to
diff between them, the hold marks a pull request in the generation's name, and the verdict is reported under its
identities — so the identities and both SHAs are proved before a pull request is touched or an agent is started. That
includes the generation naming THIS issue, which a positive late_current_issue on its own does not say. A candidate
whose base was never recorded would otherwise produce a git diff against nothing and a record two sinks refuse
afterwards, with the run already paid for; one carrying somebody else's number would show the agent a prompt naming two
issues and file the verdict against the one it names. Either parks instead, saying which field is wrong.
Before any of that runs, the reusable open pull request the candidate stands on is put under a cycle-marked hold
(late_hold.py). Which one that is, the owner decides from the record and in one order. A hold this generation
already took names its own, and outranks the rest: the preserved body beside that identity is the only copy of a
description there is, so a tick that went looking for a target instead could take a second hold and overwrite it —
which is exactly what an issue re-pointed at another change would otherwise arrange. It outranks them only up to a
record that has moved, though. A generation re-measured past its own push is adjudicating the change on the
published pull request, and a "do not merge" left standing on the plan one marks nothing while the change a human
could merge carries no notice at all — so a hold whose publication entry names a different pull request is settled
first: the old one released, the slot freed, the new one taken, never both at once, and a release that could not be
made on a reusable pull request parks instead of spawning. Failing that, a generation carrying a publication entry
names the implementation pull request the work is already on: the gate proved it open and froze it there, so nothing
is looked up and no provenance is derived. Failing both, there is only pr_number — whichever pull request the issue
currently records, which is an implementation as often as a plan — so the hold reads the discussion provenance through
the implementing stage's own _recorded_pr_is_the_plan, about the one snapshot it read, since past the handoff a plan
is told from an implementation by the commit its head is on and two reads would leave a window for a human push. That
snapshot is read whole where the fetch is guarded, because a PyGithub pull request is lazy and the request that can
fail is the first attribute access rather than the fetch itself; anything unreadable parks rather than escaping. An
implementation PR that no entry says the candidate was measured against is left alone — rewriting its description
would replace a human's account of a change under review with a notice about a different one. A provenance that could
not be established is not the same answer and fails closed. Past that gate, the identity, the head the pull request is
standing on, and the original body are written to pinned state in one write BEFORE the pull request is edited, so a
crash can lose the edit — which the next tick re-applies, since every branch is idempotent — but never the only copy
of the description. Two of three is not that record: a head no reading could name refuses the hold while nothing has
been touched, because the write drops an empty one and a notice already on somebody's change would be left with
nothing on the issue saying which change it was written over. The head is a reading rather than a claim: it says which
change wore the notice, so a pull request somebody pushes to while the adjudication runs is reported and left wearing
the same notice against the same recorded reading, and the late_published_sha a settlement pins its push to is never
restamped from it.
What the retry re-applies over is decided by the WHOLE body, not by the hidden marker inside it: exactly two bodies
are this issue's to replace — the hold it wrote, verbatim, and the description recorded beside the identity, which
is what a crash between the persist and the edit leaves and what the first application starts from. Anything else is a
human writing over the notice, the marker they happened to leave in place included: a sentence changed inside the hold
is their edit as surely as a wholesale rewrite is, and calling that held would have the release put the preserved copy
back over their words a step later. That comparison is only affordable because the hold body is exactly
reconstructible, which is why the marker is scoped to the cycle and the notice quotes nothing that moves inside
one — the generation counter advances on every reconciliation that lands, so a body keyed to it would leave every
re-measured candidate wearing a notice its own record no longer recognized, and the measurement belongs on the issue
thread, where each reading is announced anyway. What the notice says is decided by the side of publication and by
nothing else: a pull request nothing has pushed to is being adjudicated before anything is published, while the one
the work is already on was published long before the hold — so telling its author their change is held "before
anything is published" would describe a change that is not theirs, and what that notice names instead is the push the
adjudication stands in front of. Being reconstructible is a property of a spelling, and a hold outlives the binary
that wrote it, so the spelling before this one — marked by generation as well as cycle, and quoting the measurement —
is kept as something to recognize and never to write. All three are recognized and only ever one is written: a
body found in the older spelling is ours, and so is one found in the side of publication the record has since crossed,
which a developer resumed on guidance and re-measured past their own push is exactly how a cycle reaches. The same
edit that would have applied a fresh hold rewrites either in the current spelling, so every later comparison has one
answer to make. A hold a binary cannot reconstruct is a "do not merge" notice nothing can ever take back off, on a
pull request nothing can start an agent under. Leaving it alone is
not the same as being held, though, and the answer says so: an open pull request whose notice a human removed is a
change they can merge with nothing on it saying an adjudication is running, so the reconciliation reports it
displaced and no late decomposer is started under it. An answer already recorded may still be settled — settling
releases a hold that is already gone, and only a NEW run would leave a human free to merge under one. A write
that refuses is the same rule read the other way: with no preserved copy there is no hold to take, so nothing is
edited and the issue parks. How long a description may be preserved is decided the same way, before it is replaced: the
whole prospective comment is rendered with the run's record already in it — the spec this issue is locked to, an
operator's command line bounded by nothing here, included — because the write that starts the run has no safe failure of
its own. A body too long to hold beside that is refused while nothing has been touched. A hold that cannot be reconciled
parks WITHOUT spawning — once, since the retry that changes nothing repeats no notice — and the park it leaves is
retired the moment a later attempt reconciles it — a stale awaiting_human would otherwise silence a question, whether
the retry recorded it or a crashed run had already recorded one whose announcement never landed. A pull request a human
merged or closed meanwhile is simply not held, and nothing re-anchors the frozen candidate SHA off it: the recorded
commit is the evidence every later step acts on, never the pull request's current head. What the run itself records —
role, locked spec, session, cycle, source commit, generation, and the whole of what the verdict decided — is in
../state-machine/labels-and-state.md#the-late-run.
The owner read a finished run has to pass¶
Everything above is about the candidate. This is about the issue the candidate belongs to, and it exists because of
how long a late run takes: the issue was fetched when the tick began, an agent then ran for minutes to hours, and
everything a completed run leads to — publishing an accepted candidate, taking a snapshot, superseding the pull
request its work is on, activating children, even announcing a question — lands on an issue somebody may have closed
in between. So
after every completed late run, the adjudicator's and the developer revision's alike, the result is persisted and
then the owner is read again, once, before any of that happens (late_owner.py).
Every completion, not only the ones that decided something. A question, a timeout, an unusable reply, a candidate
the adjudicator moved, an outcome too large to record, and a developer reconciliation that could not be made are all
runs the issue paid for, and a closure during any of them strands the same generation and the same hold as a
closure during a single would. The one exception is a
run the tick declined — an operator's paused label, a shutdown sweep — which is not a completion at all and must
leave durable state exactly as the prior tick left it.
The ordering is the same at every one of them: persist, then read, then speak. What the run produced — a recorded verdict, a re-measured candidate, the park a failed reconciliation earns — is durable before the read; the read decides whether anything is said at all; and the sentence goes out only past an open answer. So a closed or unreadable owner receives no completion notice, and no comment failure can cost a run that already finished.
Three answers, and each is a different obligation.
- Open lets the tick carry on, and is also where a check owed from an earlier tick is retired.
- Closed ends the cycle rather than the tick. The generation is marked cancelled — irreversibly within the cycle,
so a human who reopens the issue gets a fresh cycle rather than this one resumed — the mark is durable before the
late_cancellationrecord of it is emitted, and nothing is reclaimed here: what the remote is owed stays on the generation's ledgers for the cleanup path to settle. Nothing is announced either: a question recorded on an issue whose owner has just closed it is not asked. - Unreadable leaves the check owed on the generation itself (
late_owner_check_pending), and fails closed twice over: an exception is unreadable, and so is a state that is neither of the two GitHub reports, since defaulting a read that established nothing to "open" would act on the strength of it.
That marker is the load-bearing half, because nothing else would bring a tick back to the read. A revision that came back under the ceiling is not adjudicable and an issue parked for a human is not adjudicating, so a retry hanging off either would never run — which is why reconciling a pending check is the first thing a tick does, ahead of the size gate, the hold, and any spawn. The retry costs no agent: the run has already been paid for and its result is already recorded, so the recorded answer is what settles the candidate once the read succeeds.
It is written before the read rather than derived from its failure, because a read that fails is not the only one that does not come back: a process killed mid-read would leave nothing at all behind, on exactly the two routes above that carry the next tick past the point a retry could hang off.
What the claim does not do is move the phase backwards, and neither does anything else above the transaction.
A boundary before the split — measuring, holding_plan_pr, adjudicating, owner_check — is never written over
snapshotting, splitting, or superseding, and the record refuses that move itself rather than each writer
remembering to: a transaction re-entered after a crash comes back through the whole coordinator, so the hold it
reconciles before anything spawns, the spawn that names its own boundary, and the claim each completion
writes would each erase it in turn. The window that matters most has nothing recorded at all — a child is created
before the write that records it — so the phase is the only thing left that says a loop was in flight, and a
cancellation observed there keeps exactly what it found. The guard reads a kept boundary back as the standing claim
it is, so a re-entered transaction pays for no second write of the claim it already made.
And it is written by the completion, not by the guard — in the one write that records what the run left. That is
the same rule for a verdict, a re-measured candidate, a timeout, an unusable reply, an outcome too large to record, a
worktree the read-only agent moved, and a developer reconciliation nobody could make: each is a finished run the
issue has already paid for, and each is durable, park and claim together, before the tick does anything that might
not come back. Taking the claim on the way into the read instead would leave a tick that died in between with no
park, no claim, and a generation still reading as adjudicating — so the next tick would pay for another agent
against a candidate this one had answered, and a human who closed the issue in that window would never be found out
about. The guard asks whether the claim is standing and reads only past it; a caller whose own write did not carry
one gets it there rather than a read nothing would bring a tick back to. Everything a completion staged rides that
same write — which is what puts the durable half of a
park out ahead of any comment, so a comment GitHub refuses can no longer take a finished run's result with it and buy
a second run of an agent that had already answered.
The park beside the marker is the visible half, and it is taken only when the issue is not already stopped on something a human has to answer. Replacing a question, a timeout, or a stalled revision with "the owner could not be read" would swap out the thing the human is actually being asked for one they cannot answer; on those the marker alone carries the retry.
What happens to the sentence that park staged turns on whether anything will ever say it instead. Holding one back is
a deferral, and only where a later attempt supersedes the park: that attempt re-takes it and announces the reason it
fails for then, which is the current one. A park no attempt supersedes — a stalled revision waiting to be told what
a dirty checkout now means — has no such tick coming, and its sentence is the only thing that will ever say what the
human has to do. So an unreadable owner releases that one anyway, on a thread it could not prove is open: a stray
comment costs less than an awaiting_human standing unexplained for as long as the read keeps failing.
A park still stuck repeats no notice; a park that clears itself posts one
follow-up saying so, at most once per episode. The follow-up goes out before the write that clears the park, so
the window a crash can land in loses the write and not the sentence, and "at most once" is answered from the thread
past last_action_comment_id rather than from a receipt — the comment and the clear cannot be made one operation.
A park is not delivered until its comment is. Persisting the flag ahead of the notice leaves one gap: GitHub
refuses the comment, and every later tick reads an awaiting_human it cannot tell from one whose comment landed, so
it takes the human as told and says nothing. For a park a fresh attempt supersedes that costs one tick; for a
question, a content-drift hold, a stalled revision, or a spent spawn budget it is unbounded, since those parks are
what the issue is waiting on. So the sentence is written down beside the flag (late_park_notice) and dropped only by
the post that discharges it: a park whose sentence is still owed never counts as a repeat, is re-said at the top of
the next eligible tick — ahead of every gate a parked issue routes past — and, on the guard's own park, rides out on
whichever answer the owed read gets. A notice too long for the pinned comment is refused whole and loudly, since a
record that broke the write would take the park it explains down with it. Which is why a sentence explaining a
recorded outcome names that record rather than copying it: the explanation a single gave is already in this
comment, so a notice repeating it would put the same agent prose there twice and an explanation an outcome could be
recorded with would be one its own obligation could not be written beside. The obligation holds a marker where the
explanation goes — sized by this orchestrator's wording rather than by anything an agent wrote — and what reaches
the thread has it put back in a fenced block at the end of the sentence, identically on the first post and on every
redelivery, which is what lets the already-posted reconciliation recognize its own comment. Fenced because a thread
is markdown: an explanation opening an HTML comment would swallow the rest of the sentence, and a human would read as
far as the quote and be told neither what the agent said nor what to do about it. Only a LINE that is a run of the
fence character and nothing else can close a block, and a line of backticks cannot close a tilde fence — so the fence
is built out of whichever character the quote's own lines leave cheap, and an explanation that is itself a long run of
fences reaches the thread whole at three characters an end. The quote carrying a long line of EACH is blocked off in
pieces: a fence is two characters per character of width and a further block is a fixed handful, so the long lines
land in blocks of their own and every HTML-comment opener between them stays inside a block, where it is shown rather
than obeyed. How wide to let a fence grow before paying for a block instead is answered by trying every width from the
shortest up to the widest line, doubling, and taking the smallest rendering.
What ends a line is markdown's answer rather than this orchestrator's: a newline, a carriage return, or the pair. The quote is split on all three and rejoined by the terminators it arrived with, so an explanation written on a machine that ends its lines with a carriage return reaches the thread exactly as its author wrote it — and its fence lines are recognized as fence lines, rather than blocked off at three characters they could close.
Past what any pieces can hold the quote goes in unblocked, with its openers escaped a backslash apiece — unblocked is
exactly where an opener is obeyed again, and GitHub renders nothing from it onwards. Past what that holds it is cut
and said to be cut, which nothing this orchestrator records can reach and which exists because the alternative has no
recovery: a comment GitHub refuses is rebuilt identically on every poll, so the park would stand with its sentence
owed and the human told nothing at all. No verdict is refused for how its explanation renders, and no single is
charged for the sentence it owes — that refusal is superseded, so it would buy another decomposer run against a
candidate already adjudicated and leave the single short of its own park.
What a verdict the read cleared earns¶
A question earns exactly one thing: the announcement. It is posted here rather than where the question was recorded, and that is the whole reason it moved — the record has to go out before anything is said and the owner read has to go out between them, so a question is not posted to a thread somebody closed while the agent was still answering it.
A split is handed on rather than acted on. Creating the children, taking the snapshot they are cut from, and superseding the pull request its work is on are one transaction; what the guard owes it is the guarantee it cannot check for itself, that the outcome was re-checked against an owner read taken after the agent finished. Nothing is created here. What that transaction then does is the section below.
A single earns a PARK (late_unsplit.py, late_single_decision, with the sentence it owes the thread held by
late_notice.py). It is the adjudicator answering that this change stays one change, and that is the one answer this
mode may not act on: the ceiling exists so unreviewed bulk does not reach a pull request, so an agent proposing to
publish past it is the thing being guarded against rather than grounds for waiving the guard. What ends the park is a
human, either of the two ways they can answer: guidance saying what to change, which resumes the developer and
re-measures whatever comes back, or the authorization that publishes this candidate as it stands
(the section below). Publishing an oversized candidate unsplit is a
human's decision, and the issue waits for one. Every category lands there, unsafe_split included — what decides it
is the verdict rather than the reason offered for it.
The notice names what a human deciding cannot get anywhere else once the run is over: the frozen candidate, the additions counted and the ceiling they were counted against, and the recorded explanation of what stopped a split — the stand-in sentence where a result kept none. The explanation is named rather than copied — the durable obligation holds a marker the delivery replaces with a fenced block at the end of the sentence, so what a human reads is the whole of it however long it is and whatever it says, and what the comment has to hold twice is nothing. Nothing is taken back, and the only thing written is the park: the durable claim that this issue is waiting on a human, with the sentence it still owes the thread beside it. The committed candidate, the live generation, the hold on the pull request it stands on, the session, the publication the candidate was measured against, the stage the gate took the issue out of, and the recorded verdict all stay exactly where the adjudication put them, and the exemption, the publication approval, the push, and the handback are all withheld — so the label stays on the adjudication and no other stage reads the candidate as publishable. The park is one no attempt supersedes: a retry cannot decide differently without an agent, and no agent runs against a candidate this issue has already adjudicated, so every later tick reads the recorded verdict back, finds the park it would take already standing, and says nothing. A tick that died between the record and the park, and a notice a refused comment stranded, are both answered by the redelivery every standing park's notice gets — once, and by the next tick.
Authorizing one oversized candidate to publish¶
/orchestrator authorize-oversized <commit> is the operator command that ends that park, and late_authorize.py
owns everything it means THERE. One other park reads the same command, and reads it for itself: the implementing
size gate's late_unauthorized_exemption, where an exemption names the candidate and no authorization stands
beside it, answered by implementing/late_command.py and implementing/late_consent.py, and brought to them on
every poll by implementing/late_recovery.py — the owner that routes every park the size gate takes back through
the publication seam before anything is spawned (see
../state-machine/labels-and-state.md). What CREATES that park
is the gate's own ordinary reading, on either side of publication: a candidate implementing/late_authority.py calls
exempt on a record nobody authorized, counted strictly past MAX_ADDED_LINES. Its SYNTAX is
engine/messages.py's beside /orchestrator continue, because the drift
hash has to leave both out — the tick that reads one answers it and hands the same issue on to a stage handler, so a
hash counting it would meet that handler as a body edit nobody made.
Every term of the command follows from what it licenses. It names the exact commit, because a bare "yes" would authorize
whatever the worktree ends on next. It has to be the WHOLE comment: a line of it under a paragraph is prose that
mentions the command, and prose about an oversized candidate is guidance, which resumes the developer against it.
/orchestrator continue is refused rather than absorbed — "do that step again" is not a decision about a change anybody
read. Who may say it is the allowlist every other workflow-driving comment goes through, applied where the thread is
read, so an outsider's comment is not in the reading this owner is handed at all. And WHEN they may say it is bounded
like every other reply: a comment below the park's own notice was written before the question was put, so it goes stale
rather than authorizing the candidate it happens to name. Nor does a valid command publish past guidance written under
that notice: those words were held back from answering the park, no agent has read them, and the stage a publication
hands the issue to reads the thread only through a bounded excerpt — so the developer is resumed with them quoted whole
instead, and nothing publishes.
A command that is all of those things is proved and then recorded. The commit it names has to be the generation's
frozen candidate; the adjudication it publishes has to still be on the record as a single for this cycle,
generation and commit; and the contribution between the frozen pair is recomputed in the developer's own worktree
rather than taken from anything stored — a digest a caller handed in proves only that a caller had one. What that
earns is the late_override_* group (../state-machine/labels-and-state.md#late-generation-state):
the candidate, the frozen base, that digest and the scheme it was taken under, the additions and the ceiling they
were counted against, and the id of the comment the authorization was made in. The record, the park coming down and
the reply being consumed are ONE write — a park cleared without the record would send the candidate straight back
into the adjudication a human just answered, and a record without the consumed watermark would let the same comment
authorize a second candidate later.
Everything the command does not prove is answered on the thread and consumed — the human asked for something this park cannot do and is owed the sentence saying so, with the command that would have worked spelled out in it. Once, under a receipt scoped to the reading it answers: the sentence and the write that consumes it are two operations, so a tick that says it and then fails to record it reads the same command again, and the receipt already on the thread is what stops it saying the same thing twice. A reply written after that failed write moves the scope and earns an answer of its own, because a second request is a second decision.
What those answers leave standing differs. A commit that is not the parked one leaves the park exactly where it is:
the same decision is owed, and the notice explaining it is still the last word above the refusal. An authorization
arriving over an adjudication the record can no longer show retires it, and has to — there is nothing to be owed
until something adjudicates the candidate again, and awaiting_human is the flag that suppresses the announcement a
categorized question earns, so a park left standing over the replacement run would cost a human the one sentence
nothing else will ever say and would have a split create children under a claim that the issue is waiting. The tick
carries straight on to that adjudication.
A contribution this host cannot fingerprint is not answered at all: nothing about it is the operator's doing, so the next tick takes the same reading again rather than asking them to decide twice. And drift outranks the command entirely — an edited title, body or counted comment parks the issue on the edit first, consuming nothing, so the decision is made again against the requirements as they now read.
The same command ends one other park, which implementing/late_authority.py decides is owed and
implementing/late_consent.py takes and answers. A live issue can carry an exemption with no late_override_* group
beside it — an older build wrote one on a single verdict alone — and so can any issue whose authorization was
hand-edited or half-written. The candidate there is measured like every other one: at or below the ceiling it
publishes on its count, exactly MAX_ADDED_LINES included, and past it the issue parks
late_unauthorized_exemption rather than going back to an adjudication that has already answered — routing it there
would pay for a second adjudicator over a settled question and risk a split cutting children out of work somebody
decided ships whole. The park deletes nothing, so the exemption an authorization would be checked against is still
there, and it is answered by the same whole-comment command under the same trust and staleness rules — read where
the reading is, so whichever seam took the park can end it: on workflow:implementing the parked tick reaches it
through the disposition's own recovery, and on the five stages that publish onto an open pull request through the
debt reconciliation the dispatcher runs ahead of every handler. What that command earns there is the same
late_override_* group written from the gate's own reading, and the park itself keeps the pair and not the count,
since a generation answering oversized is what the dispatcher restores workflow:decomposing over.
The publication DEBT such a commit leaves is that question one field over, and it is answered off the debt's own
recorded late_approved_basis rather than inferred from the records beside it. The settlement writes the exemption
and the approval in one breath, so an adjudication or authorization basis is a debt an operator's gesture is
behind and may be spent only while that gesture can still be read; a reading basis is the gate's own count at or
below the ceiling and is spent by the tick after a crash without asking anybody. An approval an older binary wrote
carries no basis at all, and there the exemption is the only evidence left — read conservatively, so a comment that
CLAIMS an exemption and cannot say which commit it is about is the adjudication's debt rather than this workflow's
own. The rewrite TRANSFER asks the same first question before it grants a permit: moving an exemption nothing
authorizes would hand the rewritten commit a permission the accepted one never had, and what follows a granted one is
the authorization, so a squash of an authorized candidate stays authorized and a squash of a legacy one gains nothing.
The one candidate that park never holds is a commit the pull request already stands on, where the push would move nothing and only the bookkeeping behind a publication that has happened is left.
Past that write the tick carries straight on to the answer it already had, and the settlement below runs on the same poll. The question it asks is of the RECORD rather than of the reply. It compares every frozen term — the commit, the base, the additions and the threshold — so a generation that has moved under any of them is a different question from the one that was answered, and then it fingerprints the contribution again and holds it to the digest the record carries. That last comparison is why a digest is recorded at all: the terms above are the pinned comment agreeing with itself, which a hand edit, an older binary and a record half-written by a crash can each arrange, while the digest is the one term answered by the objects. It is re-taken rather than trusted from the tick that wrote it because the two need not be the same tick — the publication can be reached by a later poll, on a later process, and on a host that never held the content between the frozen pair. A reading nobody could take and one that disagrees are refused alike: the candidate stays where it is with the authorization still on the record, so a store somebody repairs publishes what they authorized without asking them to authorize it twice. A process that died between the write and the publication finishes from what the write left, without asking the human or an agent anything.
What no term of the record can say is WHICH answer was authorized, and that is the last thing the road has to hold.
A candidate can be adjudicated more than once — a certificate over edited requirements discards the recorded verdict
and buys a fresh one, and a developer revision re-freezes and buys another — and an acknowledged, unchanged
candidate comes back over the same base at the same size, so every term above still matches. So the record is bound
to its answer by being dropped with it: late_session drops it wherever a recorded result is discarded and
wherever the run replacing that result is recorded — which is the statement of the rule no road gets around, since
every one of them ends at a fresh spawn — and late_revision_reconciliation drops it wherever a re-freeze mints a
fresh generation one step earlier, so the pinned comment never shows a permission for an answer the issue no longer
holds. A record still readable at the settlement is therefore one whose answer nothing has replaced, and an operator
whose decision was overtaken is asked again rather than having the next adjudication publish on their name.
What that decision licenses is the SETTLEMENT below, which
late_settlement.py owns the order of and writes the exemption itself in. Nothing in this mode makes that decision —
a verdict is not one — so it is the only road into what follows. The candidate is already
committed in the developer's own worktree, and the ordinary implementing publication is what pushes it and hands it to
review — so what the settlement owes is a durable record that this exact commit has been adjudicated, or the gate
would measure the same candidate past the same ceiling and adjudicate it again forever. late_exempt_sha names the
measured commit and only it, which is the whole invalidation rule: work committed on top of an accepted candidate is
work nobody adjudicated, and it is measured as the fresh candidate it is
(../state-machine/labels-and-state.md#late-generation-state).
Beside that commit goes the identity of what it contributes: the generation's frozen base, the accepted
candidate, the canonical digest of the contribution between them, and the version of the scheme that digest was taken
under. It is derived from the frozen pair the decomposer inspected and from nothing else — never from the checkout's
head or a base read now, since the worktree is writable for the whole of an adjudication — and it goes down in the
same write as the exemption, because the retirement a few steps later takes that pair off the record. The exemption
says which COMMIT a human ruled on; the identity says which CHANGE they ruled on, which is the only question left
once the commit itself has been rebased, squashed, or made afresh. What lets a candidate publish unmeasured is
still the exact-SHA comparison and only that; what the identity licenses is the exemption MOVING onto the commit an
equivalent workflow rewrite produced, which late_transfer.py grants and records as its own pinned authorization
before the push it licenses — and which late_rotation.py then carries over on the write that receipts that push,
so the verdict never names a commit no remote has
(the size gate's own section).
Where the reading could not be taken, or where any field of the record cannot be
vouched for, there is simply no transferable identity, the exact-SHA exemption stands untouched, and the rewrite is
measured as the fresh candidate it looks like. The record belongs to the
commit on the exemption field, so moving that field to another commit drops it: a later verdict that records the
commit alone writes nothing over those fields, and left there they would match the first commit by name the next
time one put it back.
Beside the exemption goes the exact-commit reconciliation of the pull request the issue records
(late_reconcile.py, with the hold release beside it and the head a published-side verdict is proved against one
owner over in late_proof.py), and it is the half the ordinary publication cannot do: that one searches for an OPEN
pull request on the branch, while pr_number by this point may name a plan PR a human merged, or the commit may
already be sitting on a pull request a crashed publication opened and never recorded. Neither is cosmetic.
implementing asks its recorded pull request first, and
a merged one that is no longer the plan ends the issue as done — with the adjudicated candidate never
published; and a commit already on a pull request nobody records is published a second time, since the reuse looks
for an open one and finds none. So the commit is what the pull request is found by, in any state
(find_pr_for_commit): one that carries it is recorded whatever state it is in, and when nothing carries it the
recorded number is kept only while it is still open — a settled one is dropped rather than handed on. A lookup, or a
recorded pull request, that could not be read parks (late_pr_unreconciled) rather than publishing on an answer
nobody gave.
That is the road for a verdict taken BEFORE anything was published. One taken past a publication searches for
nothing: the entry names the pull request the work is already on, so the same owner proves it instead — the receipt
group first and without asking the remote anything, since this road reaches the transport without the size gate's
own door and the push it makes writes a fresh group over whatever the comment carries; then the pull request open,
its head in this repository rather than in a fork carrying the same ref names, open on the branch this settlement
resolves and pushes, and standing on the head the entry froze. Each of those parks the same way, and none of them
drops what it could not confirm
(../state-machine/delivery-stages.md).
Where the candidate is an auto-rebase replay the generation took over from its attempt
(late_auto_rebase_replay_sha), the settlement's push is a head this orchestrator rebased rather than one a developer
reported on, so it records the report debt that push leaves (late_replay_debt.py) and puts the reviewer's spent
rounds back, and its label hands the head to workflow:validating rather than to the stage the rebase was made under,
as every orchestrator rebase of a pull request's head does.
Two things the reconciliation deliberately does not do. It creates no snapshot — a snapshot exists so children can be cut from a candidate about to be superseded, and an accepted candidate supersedes nothing, so preserving a copy of it would record an obligation with nothing on the other end. And it does not rewrite the held pull request: the description this generation replaced is restored over the hold text, and what happens to that pull request afterwards is the ordinary reconciliation's — the publication that follows reuses it when the push lands on it and judges the restored description as it judges any reused one, never rewriting it, and leaves it alone when it does not. Only a body that IS this cycle's hold, verbatim, in either spelling this orchestrator can reconstruct, is restored — so a description a human rewrote, or edited a sentence of, marker and all, while the hold stood stays theirs, and a settled pull request still wearing an older binary's hold is still put back. That settled case is the one release the retry above cannot have migrated first: a pull request nobody can merge is left exactly as it is by the reconciliation, so what the release meets there is whichever spelling wrote it.
What a failed release may stop is narrower than what a failed hold stops, and for the reason the hold exists: the
danger is a change a human can still merge while it wears a notice saying not to, which is a property of an open
pull request. So only a reusable one parks the candidate (late_plan_pr_hold_failed), with the generation untouched,
which is what makes that retry free. One a human has already merged or closed is tidied where the edit lands and
stepped over where it does not — refusing to publish an adjudicated candidate over the description of a settled pull
request would be a permanent block bought for nothing, and the ordinary exact-commit reconciliation is what the
candidate goes on to.
The order is chosen so every window a crash can land in is one the next tick repairs. The hold is released first,
while nothing else has moved; the exemption and the identity beside it are written next, with the generation still
live behind them; only then is workflow:implementing handed back; and only after that is the generation retired
(late_handback.py owns that half) — behind the one comment naming the accepted commit, the additions measured and
the ceiling they were judged against, the human operator who authorized it, and the exemption's scope (that commit
only; anything committed on top of it is measured again), with the decomposer's rationale quoted beneath under its
own name, posted immediately before the write that drops the generation,
so a crash between them costs at most a repeated comment. That quote is late_result_rationale as the record kept it
rather than a reply, fenced the way a park notice quotes an explanation (late_notice.py), so a fence line or an
HTML-comment opener in it is shown rather than obeyed and a cut one still ends in its marker. The authorization's own
tick and a fresh process finishing the settlement after a crash — whether or not the candidate had been published —
quote the same text, and neither pays for an agent run. Where the record holds no rationale a reader can use, the
comment says Decomposer rationale was not recorded. instead, and that sentence exists only in the comment: the
pinned state keeps the absence it has, and the settlement proceeds exactly as it would with one. The quote changes
nothing around it — the park a single takes, the command that lifts it, this order, and the label handed on are
what they are without one, and no late event or analytics record carries it. What that write keeps is the two
external ledgers: an
obligation the remote is owed does not stop being owed because the adjudication that recorded it ended well. A
decomposing issue with no generation on it is one the INITIAL decomposer would pick up and re-decompose, and an
implementing issue with a live generation is one the relabel guard puts back and the next tick re-settles — so the
ordering is what keeps the first of those from ever existing.
What a cleared split actually does¶
late_transaction.py is the order, and the order is the contract: every step is preceded by the durable fact that
lets the next tick tell "already done" from "never started", and every step is idempotent where that fact turns out
to be ambiguous. Four refusals come first, because no step below could repair any of them. A lineage already at
MAX_LINEAGE_DEPTH creates nothing (the bound is enforced where the children would be born as well as where the
reply was parsed). A recorded ancestry that disagrees with the generation's lineage creates nothing either, and
that is the same bound read from the other side: a child of an earlier split carries the lineage it was created
under, its own generation is minted from that record, and a generation naming a shallower depth or a different root
is one minted without it — which is exactly how a lineage would buy itself a generation past the cap. An
obligation ledger holding an entry this binary cannot type stops the whole transaction, since a split records a
snapshot and one consumer per child on exactly that ledger and merging into one written back verbatim would drop
whatever it did not understand. And a manifest whose declared scope carries one of this orchestrator's own receipt
markers is refused here rather than where the child body is built, because it is a fact about the manifest rather
than about one slice and because this is the last point at which refusing costs a new commit instead of a human —
the children step below says what that receipt would otherwise let a lookup adopt.
The snapshot, before any child (late_snapshot.py, over git/snapshots/). A split ends with the parent's branch
superseded, its pull request closed, and the parent itself an umbrella that implements nothing — so once children
exist, the only thing between them and the work they were told to reuse is one ref. Four properties, each a refusal
rather than a convention:
- One namespace.
refs/orchestrator/late-split/issue-<n>/cycle-<c>/gen-<g>, built from the generation's own identity and validated against the one pattern this orchestrator writes — a custom ref namespace rather than a branch precisely because a branch is listed, protected by rulesets, attached to pull requests, and auto-deleted on merge, and a snapshot has to outlive all of that. A ref assembled from a damaged pinned field is refused rather than pushed. Whether a production token may write the namespace under production rulesets is proved before rollout by the capability check in../configuration/snapshot-capability-check.md. - No blind overwrite. The remote is read first, so a retry finds the ref it already pushed and spends a read rather than a write; every write is lease-pinned to what that read established, a create leasing the ref as absent. A ref already carrying another commit is reported as a mismatch and left exactly as it is, because the automatic alternative is destroying the only copy of somebody else's candidate.
- Exact SHA, proved twice. The remote has to answer with the exact frozen candidate, and then the ref is fetched back into the clone the worktrees share and resolved locally — a namespace a token can write and not read would otherwise pass every check here and fail the first child that tried to use it.
- The obligation is durable on both sides of the push. The intended ref is written to
late_resourcesaspendingbefore anything is pushed (a push that landed and a process that died a statement later look identical from the outside) and moved toretainedonce proved. Every failure writesfailedand parks with the recorded verdict standing, so the retry costs a GitHub read rather than an agent.
The owner, again, before every step the remote keeps. Past the snapshot the transaction re-reads the owner
before each child it creates and before each step of the publication behind them — because a ref is an object a later
pass can reclaim, and a child is a real issue somebody will work, which nothing here ever takes back. It asks
repeatedly rather than once because the steps are not one moment: a push and a fetch stand between the verdict and
the first child, a create, a record, and a seed stand between every child and the next, and publication is three
GitHub round-trips of its own — the announcement, the supersession, and the retirement that hands the parent to
workflow:umbrella and lets its children run. Reading once for all three would let a close observed during the
announcement or the supersession still put an agent on somebody's repository.
Who else could see a close is what makes asking necessary. A poll that observes one while a worker holds the issue
can hand it to nobody — the scheduler admits no second worker for an issue one is already running — so the reading
is latched process-wide (workflow/engine/observations.py) and swept a tick later.
The run in flight reads that latch too, and it is the first thing the owner read asks — ahead of GitHub, because it
holds the one reading GitHub cannot give back: a close and a reopen that both happened inside one of this run's own
steps leaves the issue reporting open, and only the poll ever saw otherwise. So a latched close ends the cycle
wherever the run stands.
Every step nothing takes back is asked past it, and each barrier covers a window of remote work the poll runs
beside. Every child including the first, because the write that forces the parent to be an umbrella stands ahead
of the first create — once more inside the create itself, since the orphan lookup before it walks the whole
repository on a resumed pass — once behind it, and once between the read of the child's own pinned comment and the
write that adds to it: the create is a request too, and so is that read, and what a close landing inside either
leaves is a real GitHub issue, recorded either way and written to never.
Each publication step. Every relabel the activation walk makes, since a close latched after the first child
was released must not release the second — asked on both sides of the publication licence beside it, since that
licence is a lookup and a close observed inside it would reach nothing else before the relabel landed — and the
transaction asks again behind the walk, because the walk holds children but does not own this issue's record.
The spawn, asked last of all, because a worktree probe, a retry-budget write and a hold to reconcile stand
between a tick's own gates and the one step that puts an agent on somebody's repository. Both sides of a
developer revision, which is the same step under another name, with the poisoned-session retry inside the shared
resume guarded alongside it. Each step of an authorized settlement's publication,
which is where the barriers protect the record rather than an effect: the last write drops the generation entirely,
and both the sweep and a receipt adopted from the thread read that generation to decide there is anything to end —
so past that write the answer is a reinstatement rather than a refusal, from the generation still in the call's
own memory. Every obligation a reclamation settles, between every two of the receipts a reclaimed ref owes its
children — each is a comment on somebody else's issue — between the fresh consumer proof and the ref delete it
authorizes — a ref that is gone while the record still reads live is a reclamation nothing afterwards can attribute
to the cancellation that earned it — and again between that delete and the receipts it
authorizes, since those receipts are the one cleanup effect that writes to somebody else's issue and a cancelled
cycle owes its children nothing — and once more inside each of those receipts, since proving a child untold is a
request of its own and the comment it authorizes stands behind it. And the umbrella's own walk, past its child
scan, behind the settlement its terminal waits on, and immediately before the write that records the resolution,
since everything after those acts — it reclaims a remote, hands the issue done, or releases a child, and done
takes the issue off every label the closed-owner sweep queries and closes it. What makes that label safe is the
write ahead of it: one pinned write that stamps the resolution and retires the cycle together, so a close
arriving past it finds nothing left to cancel. One landing inside it is answered behind it, from the generation
still in the call's own memory — a reinstatement rather than a refusal, exactly as an authorized settlement's own
retirement and the size gate's drop of a small candidate both take one: the cycle goes back cancelled, no terminal is
written, and the owner keeps workflow:umbrella, where the ending is reached. Every one of those retirements reads
that answer off the window rather than asking the latch, and the window decides it as it closes, under the lock that
closes it — a barrier taken any earlier leaves an interval in which a poll can still latch a close and receipt it
against the cycle the window is advertising, and the worker would pass on having seen neither. That barrier belongs to
the process that made the write, so the write records which cycle it dropped: a process that dies before reaching it
leaves that correlation and the receipt on the thread, and the closed-owner sweep adopts the two together rather than
finishing a terminal over a close somebody already observed.
The same is true one layer up, of a close no poll ever saw: an issue open when the enumeration listed it and closed by the time its pass refetches it carries a reading that exists only on the object the refetch returned. Both paths that refetch take it there — the sequential loop and the worker — and hold it across the pass, so a mark that could not be written leaves the reading for the next tick instead of losing it to a reopen.
The barriers past a claim-bearing read take the latch ALONE: a claim names owner_check, and writing it over the
boundary a tick actually reached is the rewind the record refuses.
A cancelled cycle is refused under every label it can be wearing, and rejected is written wherever the transition
graph declares the edge — plus ready and blocked, which the cycle's own decomposer writes as its ordinary
outcome and which neither the graph nor the closed sweep would ever bring a tick back to — never from the unlabeled
state, which is the restart handshake itself, and never past a backlog / paused that defers the external half of
the ending while the mark still goes down.
A latch is memory, so the poll that takes one also leaves a cycle-scoped receipt on the issue thread — a comment, because the pinned comment is written whole and the worker holding the issue owns it. A post GitHub refuses is retried by the next poll, since an observation with no durable half is one a restart takes away entirely. After a restart the dispatcher's cancelled-cycle guard scans for that receipt once per owner and cycle, and again once another poller on the host has held the issue since, adopts it, and runs the ending from the mark (state-machine/delivery-stages.md); a walk that could not answer, or whose mark GitHub refused, is taken again.
The latch is scoped to a cycle as well, the one the record named while the issue still read closed behind it — by that receipt's read, by a poller refused the issue's writer claim, which reads the record once more behind the issue and scopes nothing to a cycle the record moved off meanwhile, or by the sweep of the closed owner. Another poller on the host can settle that cycle and start the fresh one an operator authorizes before this process holds the issue again, so a latch that remembered only the issue would end the fresh cycle for a close older than it, and a record read after the issue was found closed proves nothing about which cycle that close ended. A close no read scoped to the cycle a later pass finds ends it only where no other poller has held the issue since the latest moment it was read closed, which the claim's signed holds tell, nor this process restarted the cycle after it: a close read again after a restart is a close of the fresh cycle. A reopened owner whose record names a cycle its close cannot be tied to is swept with nothing marked and its latch let go, and no receipt is posted for a cycle a held close did not end — nor remembered as posted, so the next poll that reads a fresh close still posts one.
The latch is also held past a cleanup pass that RETURNED without finishing the ending — a ref a live consumer keeps,
a delete the remote refused, a terminal GitHub declined — but only where nothing else would come back: an owner
still wearing one of the four swept labels is one the sweep reaches on its own cadence, and holding a reading over
it would cost a pass per tick for as long as the ending is owed. Two of those four are queried for this reason
alone: a decomposition outcome writes ready or blocked and can land after the close its owner was observed on,
so an ending left there has to be discoverable without the latch, which dies with the process. And because it does,
a sweep that leaves an obligation owed under a label outside all four puts a queried one back.
It is the same guard the handoff took, entered again, so the three answers are unchanged: a closed owner is marked cancelled and the cycle ends where it stands, with everything already on the remote left on the ledger for the cleanup path; an unreadable one parks with the read owed on the record, claimed under whatever boundary the transaction reached rather than over it; and only an open one lets the next step run. A loop stopped mid-flight leaves a partial split, which is a state the count and the register already describe — the ref is retained, because a real child exists that the consumer ledger is short of.
Then the children (late_children.py). The umbrella flag and expected_children_count go down before the first
child exists — that pair is what tells a partial split from a finished one — and each child's number is recorded on
the generation's own ordered register (late_split_children), in late_consumers, and as its own child
obligation in one write, before anything else is done with it. That write is the direct-consumer ledger slot the
reclamation rule waits on, which is why it has to be durable before the child can run.
The register is the generation's rather than the stage's shared children list, and that is load-bearing: an issue
that was decomposed, saw its children resolve, flipped back to ready, and implemented an oversized candidate still
carries the earlier decomposition's children and dep_graph, so a walk reading that list would adopt completed
issues by manifest index, reseed them with an ancestry they have nothing to do with, and activate them. The stage's
list and graph are written from the register instead, which is also what replaces the earlier decomposition's
dependency graph rather than leaving a stale one standing over the new children.
A resumed walk therefore adopts every child this generation already records rather than opening a second issue
for the same slice, and the recorded list is monotonic: a resumed pass never writes back fewer children than the
previous one knew about. Past that register there is one more recovery, for the crash between create_child_issue
returning and the write recording it — a window in which nothing outside GitHub knows the number. Every child is
created carrying a hidden marker naming this issue, this cycle, this generation, and its slice index, and a walk
about to create looks for that marker before opening anything. The issue is in the marker because a cycle identity
is minted per issue and repeats across them — two parents adjudicating their first candidate are both cycle 1 — and
the lookup is not scoped to one parent's children, so without it one parent would adopt, reseed, and activate
another's.
That lookup is a walk over the repository's issues in every state and under no label, which is the expensive reading and the only correct one: in the window it exists for nobody has attributed the child, so a human is free to close it as junk or move its label, and a search bounded to open issues on the label it was born with would miss exactly those and open a second issue beside the one they had just acted on. It is taken only on a resumed pass — one where an expected-children count already stood on the parent before this pass wrote its own — so a first split, which has nothing to find, pays nothing for it. An enumeration nobody could take raises and parks, because "could not ask" read as "there is no orphan" is what duplicates. And a candidate a human has since closed or moved off the child label is refused rather than adopted: reopening or re-labelling it would undo a deliberate act on an issue this orchestrator had not even attributed yet, so the transaction parks and lets them say which they meant.
The receipt has to be unambiguous, and that is a second rule, not the same one. The lookup matches a marker as a
substring of a body — all a body search can do — and the body carries the adjudication's own words above the marker
this transaction stamps in. An issue whose body holds two child receipts answers the search for either slice, so
adopting on the strength of the match alone records one issue as two children of the same split: named twice in the
parent's register, once in the consumer ledger, and seeded with whichever scope came last. So the manifest is refused
outright when any declared title or body carries one of this orchestrator's receipt markers — every one of them
starts with the same <!--orchestrator- prefix — and that refusal is taken before the snapshot is pushed, where
it costs a new commit rather than a human: a generation holding a snapshot may no longer be revised. A candidate the
lookup does return is checked for carrying exactly one child receipt, so an issue an older binary created or a human
edited is stranded for a human rather than adopted for a slice it may not belong to.
Each child is born knowing what it needs and nothing more: its declared scope in the words the adjudication used, the
current base branch, the ancestor snapshot ref and exact commit, and the lineage and cycle identity a later record is
correlated by — written to the child's own pinned state as the late_ancestry_* group
(../state-machine/labels-and-state.md#late-generation-state) and read fail-closed like every other
late field. Its body says how the work may be reused: cherry-pick a coherent commit, or copy selected paths,
and never split hunks mechanically to make the change smaller. File and hunk boundaries do not express issue scope, so
a change partitioned along them is one nobody can build or review — the judgment about what belongs to a slice stays
with the developer who implements it.
The body also states the addition budget the adjudication sized that slice at, and says which of the slice's
paths the estimate covers: implementation, tests, documentation, fixtures, generated files — every path the child
commits, with none excluded. That sentence is there because the developer who reads it is the one who has to keep to
the number, and the parent's pinned comment is not somewhere they look. What it is not is a limit: what decides that
a child is oversized is the cumulative measurement of the child's own diff, taken the way the parent's was, so a
slice that lands past the ceiling is adjudicated and split again however it was sized. The budget is added beside
the declared scope rather than folded into it — what the adjudication wrote about a slice is what its developer is
owed in full, and a size stated in the orchestrator's own words is not part of it, which is also why the scope
recorded in late_declared_scope is the adjudication's words alone. A slice that declared no budget states none:
that is what a manifest recorded before this domain kept budgets reads back as, and those still create children.
What a child is not born with is anything that let its parent publish. The exemption an authorized settlement
recorded and the late_override_* terms an operator authorized that publication on are each bound to one commit on
one issue, and a child is neither — so a child's pinned comment carries nothing the size gate reads as already
decided, and the first candidate it commits is measured from the frozen base across every path, over however many
commits the slice took. Inherited, a bypass would publish exactly what the split was made to prevent: bulk nobody
adjudicated, under a permission granted for a different change.
The seed is re-applied on a resume by reading the child's state and adding to it, never by writing a fresh record: by
the time a retry reaches a child that was already created, that child may be implementing. The write that first
attributes a child also takes back the park it may have collected in the meantime — poll order is the repository's,
not the transaction's, so an orphan can reach the stage machine before anything records it and be parked as an
unattributed blocked issue. Leaving that standing would activate a child that then waits for a reply nobody owes it.
A child that already records a parent keeps whatever park it has: that one is its own.
Only then the links and the supersession. The parent says what it became and where its work went, exactly once,
and both halves of "once" are needed. The generation's own late_links_announced flag is the cheap gate and the one
that holds on the ordinary retry — it is scoped to this adjudication, unlike decomposed_at, which an earlier
decomposition of the same issue already wrote and which would therefore suppress the announcement entirely. The
thread is the gate that covers what a flag cannot: a comment that landed and a process that died before the write
are indistinguishable from the outside, so the marker this generation stamps into its own sentence is looked for
among the issue's comments before another is posted. That search is asked only when the flag is unset, so a resume
past the announcement costs nothing.
The pull request this cycle's work is on then gets superseded through the new supersede_pr helper: one marked notice
linking forward to the umbrella, every child, the snapshot ref, and the exact commit, and a close if it is still open.
Which pull request that is is decided by the side of publication the generation was entered on, and by the entry
rather than by the hold beside it — both can name the same pull request, since a generation entered past the first
push holds the one the work is already on. A generation entered BEFORE the first push has only the hold's record to go
on, and that names the plan PR this cycle marked. One entered PAST it names the implementation pull request the
candidate was measured on — and that one is the sharper of the two, because the tail behind this step deletes the
branch and hands the work to children: left unsuperseded it is an open change carrying work nobody will finish,
pointing at a branch that is gone. It is PROVED before it is closed, on the settlement's own reading and for the same
reason — the entry the gate froze names it and the head it was standing on, and neither can be re-derived — so a pull
request nothing could read, one a human merged or closed while the adjudication was open, and one somebody pushed to
are each a park with the children durable, the pull request left where its human put it, and a retry that settles the
same recorded verdict once the disagreement is reconciled. One already CLOSED over this adjudication's own receipt is
none of those: the retirement behind this step is not the last, so a tick that closed the pull request and died before
it comes back to a closed reading it cannot tell from a human's without the thread -- read as a settlement it would
park for good with the children blocked behind a supersession already made. What the receipt answers is the STATE and
only that: the head is proved on that path exactly as on the open one, because a close does not freeze the branch
behind it — somebody pushing between the crash and the retry leaves a commit the snapshot does not hold, and waved
through the retry would settle the split, activate the children, and reclaim that branch. A MERGED one stays a refusal
whatever the thread says, since a human who reopened and landed the work decided the opposite of what the supersession
claims; a reopened one reads open, so the same head proof runs and the close is made again with the receipt keeping
the notice from repeating. Every one of those refusals parks under a notice naming the disagreement rather than the
write-failure sentence, since a supersession this transaction already made is not one that could not be made.
Idempotent through the thread rather than through a receipt, since the comment and the record of it cannot be made
one operation. Both markers are scoped to the exact adjudication — the pull request outlives a cycle and the issue
thread outlives everything, so an unscoped one would read an earlier episode's receipt as this one's — and both are
honored only on a comment this orchestrator authored, since an HTML comment is invisible in the rendered thread
and anybody could otherwise post the marker to suppress the sentence it gates. A merged or closed pull request is told
and left alone; one that could not be read, or a release that failed on a still-open one, parks — and nothing is
activated while a pull request carrying the superseded work is still open. That last part is why the supersession runs
on every pass, including one whose ledger already reads reconciled: that entry records what an earlier pass did,
and a human who reopens the pull request between the write and the resume would otherwise have the resume skip
straight past, report settled, and let the children loose beside a change still carrying the superseded work.
Re-asking costs one fetch and one comment listing, and neither step repeats anything.
On the published road the pull request is asked about between every step of the tail, on the same rule the owner is — in front of the close, in front of the retirement, in front of every child released, and immediately in front of the branch delete. The owner is not the only thing a human moves mid-pass: every one of those steps is licensed by the supersession being on that pull request, and a merge, a reopen, or a push takes the licence away. So no step is run on evidence a step before it took — and "before it" includes a reading the step itself spent. The child scan a release is decided on is a request per child and each relabel is another, so the ask lives inside the activation walk rather than in front of it; the reclamation may spend a read-only probe deciding whether an ordered snapshot ref is already gone, so its ask sits immediately before the delete rather than where the work list is assembled. It is that road's alone: a generation entered before publication froze no head to prove a plan PR against, and what that pull request carries is a design document rather than the superseded work.
The barrier in front of the close is why the close is made against a second reading. The receipt the first
carries costs a comment listing, and that listing is a round-trip standing between the state and head read beside it
and the write those two license. Asked again with no listing behind them, they are the last thing to reach GitHub
before the write — so a change a human merged, closed, or pushed to inside that window is left untouched: no
notice posted onto it, no close, and a park. Discovering it one step later would mean marking and closing a change
nobody adjudicated and only then refusing to finish. A pull request already closed over this adjudication's own
receipt writes nothing at all, since supersede_pr would post no second notice and close nothing already closed.
The receipt that second reading skipped is then handed to the write rather than looked up again. supersede_pr
searches the thread for its own marker before posting, and that search is one more request in exactly the interval
this barrier exists to empty: a human closing the change inside it would have the notice land on their settlement and
the helper report success. Nothing else can move that answer — the marker counts only on a comment of ours, and
this pass has posted none since it looked — so the answer travels with the call and the close is the first thing it
sends.
What a refusal costs depends on which side of the retirement it lands. Before it the pass parks, with the
disagreement named and the obligation written back as owed: the issue stays on workflow:decomposing, the children
stay blocked, the branch stays intact, and the next tick supersedes the same pull request again. Past it there
is no live adjudication left to park, so the last two barriers decline the step in front of them and leave it to the
retry that already owns it: the umbrella's own walk for the children, its terminal for the branch. That is why the
pre-retirement barrier is the very last thing before that write, with the branch resolved ahead of it rather than
between: the window it cannot close is the write, and a reopen inside it costs a label that lands rather than a child
that runs or a branch that goes.
Declining a step is only safe because those retries ask the same question themselves, and that is what the
retirement keeps the publication group for. It is the only thing left on the issue naming which pull request the
split closed and the head it was closed over, so the shared activation walk re-asks it in front of every relabel it
makes — under workflow:umbrella and under workflow:blocked alike, since the walk reads the parent's own record
rather than being told — late_reclamation re-asks it in front of every branch it deletes, and the settlement the
umbrella's terminal waits on asks it once more before done may be written at all. Dropped there, a reopened pull
request would have its work handed to children on the umbrella's very next dependency poll and the ref it points at
reaped by the terminal, with nothing in between having looked. It costs one lookup per release, one per delete, and
one per terminal decision, and a parent that never entered the size gate answers without a request at all.
Keeping the group has one cost, and it is paid a layer above: a whole publication group with no count beside it is
also the shape a tick that died between the freeze and the diff leaves, so the reconciliation the dispatcher runs
ahead of every handler asks the record's own settlement before it reads that shape — a late_phase past the ref the
transaction cuts, or a non-empty late_split_children. Without that question the group would name the stage the
gate was entered from while the issue wears workflow:umbrella, every dispatch would be held for a human as a reading
read off a stage the issue has left, and the walk this section is about would never run
(../state-machine/delivery-stages.md#the-size-gate…).
That last ask is the one no ledger could make, and it happens twice. A reclamation that finished owes
nothing, so a human who restores the branch and reopens the change afterwards leaves every entry settled and the
terminal free — and what it writes is done, a close, and the drop of the publication group, after which nothing
would ever ask again. The answer is deliberately not written back as an obligation: nothing is owed the remote,
and an entry claiming otherwise would send a later pass to delete a branch a human put back on purpose. The label
staying put is the whole of the response.
Once before anything is said, where refusing costs nothing, and once immediately in front of the retirement write, which is the boundary itself. Between the two stands the resolution comment and the latch checks beside it, each a request a reopen can land inside — and a refusal there costs a sentence that has already gone out. So that sentence carries a marker and is gated on the thread as well as on the stamp: the stamp is what a resumed terminal has past the write, and the thread is what covers the window the stamp cannot, so an umbrella held on a reopened pull request does not repeat itself on every poll for as long as a human takes to settle it. A refusal at that second barrier writes nothing at all, so the record the next dependency poll reads is exactly what this one found.
Both halves of that receipt are scoped deliberately. It names the cycle and generation, because an operator restarting a rejected cycle keeps the thread — a marker naming only the issue would silence the sentence the cycle after it owes its humans. And it is stamped only on an umbrella a post-publication split made: nothing refuses the others past their sentence, so the initial decomposer's umbrellas and plan-PR splits keep the stamp as their sole gate, spend no comment listing per completion, and carry no receipt anything would read.
Then the label, the retirement, and the activation, in that order. The generation is retired in the same write
that hands the issue to workflow:umbrella: identity, both commits, both ledgers, and the publication group kept,
the measurement dropped,
and the recorded pr_number cleared. Dropping the measurement is what makes the label stick — a parent that has
become an umbrella has no candidate to measure, and a record still answering "oversized" is exactly what pins
workflow:decomposing and would have the relabel guard put the umbrella label back on every dispatch. Activation
runs after that write for the reason the initial split's does: a crash between them must not leave a runnable child
under a parent still labelled decomposing, and a child this pass could not flip is picked up by the umbrella's own
walk as the retry. What that write does not do is read the thread again. The issue-wide user_content_hash it
carries is the one the last reply this mode consumed already made durable, off the reading that reply came in on
(below) — so the umbrella's first drift check meets
guidance a developer revision already answered as its baseline rather than as an edit, and does not orphan the children
this split just made to decompose the same work again. A crash between the label and the write changes nothing about
that: the relabel guard puts workflow:decomposing back, the transaction is re-entered from the recorded verdict and
adopts the children it already made, and the baseline was durable before any of it.
It runs through that walk rather than through the initial split's one-shot flip, and the difference is the
supersession above it: that step can park for as long as a human takes to settle a pull request, so by the time
activation runs a child may have reached rejected or done on its own. A write that read nothing would put it
back to ready, and the transition guard only warns by default. So each child is read fresh and only the ones still
blocked with their recorded dependencies satisfied are moved; a read that failed leaves every child where it is,
since the umbrella takes the same reading on its next dependency poll.
A child GitHub reports as closed is passed over there too, and that one is not specific to the late split: closing an
issue leaves its label untouched, so a child a human ended while it was still blocked goes on looking startable to
every walk that reads labels alone. The close is read the way GitHub spells it — state, the only spelling a real
issue carries — which is also how the reclamation counts a direct consumer that ended without a terminal label.
Cleanup last, and never in the way. The superseded branch is written to the ledger as pending in that same
retirement write and attempted after activation: an attempt that does not finish records failed, emits
branch_cleanup_failed, and holds no child back. Children waiting on a branch deletion would be work stalled on
tidiness.
"The branch" is every surface it exists on — the remote ref, the checkout holding it, and the local ref — and the
entry reads reconciled only once all three are provably gone. A remote delete that succeeded beside a worktree
that would not come down is not settled: what is left is a checkout on a superseded branch that the per-tick base
refresh treats as a pre-PR tree and goes on merging into. The two halves are attempted independently and the entry
is what both said, because a remote that refuses is a permission or ruleset problem only a human can clear — a local
teardown conditioned on it would leave that checkout accreting merges for as long as the refusal lasts, and the
local half needs nothing from the remote to succeed. The local half is verified rather than trusted, because
git worktree remove and git branch -D are best-effort helpers that report nothing — so the entry is decided by a
read taken afterwards, and that read fails closed. Taking the checkout down at all is safe here for one reason: the
snapshot was created and proved before any of this, so the commit it holds is no longer the only copy.
Only a branch this issue is actually published under is ever deleted. The target comes off a ledger a human can edit
and is spent on a destructive call, so it has to be one of the two names this spec gives this issue — the
slug-namespaced form and the legacy flat one — before the remote is touched. A namespace-and-tail reading is not
enough: orchestrator/other-repository/issue-41 is inside orchestrator/ and ends in this issue's tail while
belonging to another repository entirely, and two specs sharing one target_root is the case slug-namespacing
exists for. The number is taken from the issue being walked rather than from the record, so a hand-edited identity
cannot aim the delete either. Anything that does not match is recorded failed and left for a human, which is the
one answer that neither deletes somebody's branch nor quietly forgets the obligation.
What the obligation does block is the umbrella's own terminal completion, and the retry lives there rather than
in the transaction — an issue that has become an umbrella never reaches the transaction again, so nothing else would
bring a tick back to it. late_cleanup.py is asked at the one boundary where an unsettled obligation still matters:
every umbrella tick that finds every child resolved settles whatever is still owed, and the parent closes only once
nothing is. A refusal keeps the label, which is the retry, and leaves the parent visibly open instead of closed
over a remote nobody will ever reap. A genuine edit can re-decompose that umbrella into a manifest that keeps work
for the parent, which then waits on blocked rather than umbrella; its all-children-resolved tick is the same
boundary — past it the parent goes back to implementation and nothing revisits the ledger — so it runs the same
settlement, and stays blocked while anything it names is still held — and, unlike the umbrella, while its consumer
ledger cannot be read at all, ref or no ref, since what is retired below is kept as history. Once it is settled, the
split's cycle is retired in a write of its own ahead of the flip, inside the window the umbrella's terminal retires in:
its candidate, register, and identity go, its split_attempt with them, while both ledgers and late_retired_cycle_id
stay; a parent with no cycle to retire drops its split_attempt in a write of its own, still under blocked. The
implementation the parent goes back to then measures a candidate of its own, under the next cycle, rather than trying to
recover the superseded one from a worktree long gone, and a later split of it cuts every slice afresh instead of reading
the old register as slices already made. A close latched before that write, or observed during it, ends the cycle
instead, and the parent stays blocked for the ending to run from.
That boundary is also the first at which the snapshot can go, and under the rule that owns it: a ref may be
deleted only once every recorded direct consumer has ended, and all-children-resolved is exactly when that
becomes true for the consumers this split created — for as long as the children the umbrella tracks are those
consumers. Ended is read off the consumer's own closed state and explicitly
not off its label: all three dispositions that end a child close the issue and none of them survives a reopen, while
a label does — a child reopened while still wearing done is live again, and a reading taken off the label would
delete the only copy of the work it came back for. The issues come off the child scan the umbrella already took
wherever that scan was asked about them, so the ordinary proof costs no request of its own, and a closed done
covers a nested split too — a child that reached it has published, so its own descendants are past needing the
ancestor. A recorded consumer the scan was not asked about is read afresh instead: a genuine edit re-decomposes
the umbrella, and from then on its scan is of the replacements while the originals it orphaned are still consumers
the ref was preserved for. The ledger decides who is read, never the manifest beside it, and the orphans are only
read and, once the ref goes, told so — never adopted, relabelled, or reopened. A replacement the re-decomposition
pointed at the ref is on that ledger too (below), so the
ref waits for it exactly as it waits for an original, and a replacement reopened after it resolved keeps the ref the
same way. What the ledger does not change is when the question is asked: only on a tick that finds every tracked
child resolved, or one a child's disposition parks. So the ref goes on the first such tick after the last recorded
consumer — an original, or a replacement pointed at the ref — has ended, not sooner; an original that ends while
the replacements are still running frees nothing until they resolve or one parks the parent, and the terminal waits
behind the same settlement, so nothing closes over the ref in between. Anything that cannot be
proved keeps the ref: a consumer that is open or was reopened, one whose read failed, or a consumer ledger this
binary could not type. All of that
is about the consumers the ledger names, so the prior question is whether it names all of them, and the record's
own phase answers it. A child is created and then recorded in two writes — it must be, since a child on GitHub the
parent does not record is a child nothing would come back to — so while splitting stands the list may be short by
one that already exists, and its length decides nothing: a set of ended consumers says as little about the child it
has not reached as an empty one does. Nothing on the ref is reclaimed in that window. Either side of it the list is
whole — before the split nothing has been created, and past it the loop ran to the end — which is also what makes an
empty list a fact rather than a gap, since the ref is retained ahead of the first child. The same window opens again
when a genuine edit's ordinary re-decomposition points replacements at the ref: each joins the list in the write that
records it, behind its create, so while that split's register is short of the count it wrote — the parent still
carrying its split_attempt — the list may be short by a replacement already on GitHub, whatever phase the late
generation stands at; so it is while the register names a replacement the list has lost. Either way nothing on the
ref is reclaimed, by the umbrella's terminal, a blocked parent's hand-back,
or the closed-owner sweep alike.
The boundary an interrupted transaction stood at is therefore kept, because a phase is otherwise written only
forwards. Every completed run claims the owner read, and a transaction re-entered after a crash comes back through
one — so that claim never writes over snapshotting, splitting, or superseding, and a re-entered split carries
its claim under the boundary it interrupted. That matters most in the window with nothing recorded, which no
ledger can speak to: a child is created before the write that records it, so a loop that died between the two
leaves an empty list beside a real issue on GitHub, and the phase is all that says so.
Beside that, a pre-split phase is believed only as far as the record bears it out: those phases say "nothing has
been cut from this ref" only on a record that shows no split ever started — a consumer or a split child on the
ledgers, or the expected_children_count the transaction writes in the same durable step as splitting, ahead of
its first create. That count is what upgrades a pinned comment an earlier binary already rewound: the guard stops
new rewinds and nothing migrates records already in flight, so what has to answer for one of those is the evidence
no phase write ever touched. That same count is then asked of every boundary, ahead of the phase, because a record
the count proves finished is whole wherever it happens to be standing — and more than one boundary needs it.
splitting is two answers rather than one: the phase goes down before the first create and again beside every child
recorded, the last one included, so a crash between that final write and the announcement leaves a complete ledger
wearing a mid-loop boundary. snapshotting is the same question one retry later: a transaction resumed after a park
rewrites it over whatever boundary it had reached, so a finished split comes back wearing the one it started from.
Reading either as mid-flight retains the ref for good, since nothing revisits a cancelled owner to move the phase on.
So the count is compared against the positional register the loop appends to, and a register that reached it is a
loop that finished. A stale count from an ordinary decomposition of the same issue reads as unfinished, and is
meant to — being wrong that way keeps a ref and holds a terminal, where being wrong the other way deletes the only
copy of a child's work. Past the loop no corroboration is needed: superseding is reached only once every child is
created and recorded.
Deletion is idempotent because an absent ref is a success, so the crash between the push that removed it
and the write that would have recorded it costs one request on the retry — and it is named against the commit the
split preserved rather than against whatever a fresh read observes, so a ref somebody re-pointed is a mismatch left
for a human instead of the one blind write in the whole namespace, aimed at destruction.
The ref that gets deleted is re-derived, never taken on the ledger's word: it has to equal the name the namespace
mints for this issue, this cycle, and this generation. The transport's own checks are the namespace and the commit,
and neither is identity — every generation of every issue in a lineage was cut from the same candidate and so names
the same SHA, which means a hand-edited entry pointing at a sibling's ref would satisfy both and destroy the only
copy of exactly what that sibling was told to reuse. A target that is not this generation's own is recorded failed
and holds the terminal open, the same answer a foreign branch gets.
An opaque ledger is refused per ledger rather than as one flag. The two are preserved and written apart, and they
stop different things: an entry this binary cannot type on late_resources means no reclamation can be recorded at
all, while one on late_consumers means only that no snapshot's proof can be taken — the superseded branch, which
owes no consumer anything, is still deleted and still retried. Folding them together would leave a branch on the
remote because somebody hand-edited a list of issue numbers.
A branch is owed in every state but reconciled, not only in the two this binary writes. The ledger takes any
state the vocabulary defines from any writer, so an entry left retained — a reading that means something for a ref
and nothing for a branch, since no condition keeps one — would otherwise be retried by nothing and reported by
nothing, and the umbrella would close saying it owed nothing over a branch still on the remote.
A ref is owed on exactly the same reading, and for a reason the branch rule only hints at: there is no state under
which an object still on the remote is settled. A ref kept because a consumer could not be proved ended is one
this repository is holding, and an umbrella closed over it is an object nothing would ever come back for — the
parent is done by then, and no pass revisits a done issue. So retained holds the terminal exactly as failed
does, the label staying put is the retry, and the reason it is held is logged on every dependency poll that holds,
since a hold attempts nothing and therefore writes and emits nothing.
"Ended" is the consumer's own issue state rather than its label. All three dispositions that end a child — reaching
done, being rejected, and a human closing it — close the issue, and none of them survives a reopen, while a
label does: a child reopened while still wearing done is live again, and a reading taken off the label would
delete the only copy of the work it came back for.
The delete itself is a small transaction, because the proof above is a reading of live issues and a reading is not
something a retry can reproduce. The entry is written reclaiming before the delete, and every recorded consumer
is read once more past that write and immediately ahead of the delete — the scan the pass qualified the ref on was
taken before the branch half ran and before anything was recorded, and each of those steps is a request a human can
reopen a consumer during. A consumer that came back inside that window keeps the ref, and what is left is the delete
request itself.
A later visit acts on that recorded decision for one thing only: finishing a delete the remote already took. Past
the consumer proof it asks about the ref in one read and qualifies only if the remote no longer has it. A ref still
on the remote is one a reopened child may still be cutting from, and no record of a past decision outranks the
reading in front of it. A delete the transport could not answer at all is caught and reported as the refusal it is,
so no attempt is spent without a typed snapshot_delete_failed behind it.
What follows a delete the remote accepted is the receipt, and it runs before the entry is recorded
reconciled. Every recorded consumer is told once that the snapshot has been reclaimed and that reuse now needs an
explicit new split cycle. The ref is never recreated. The receipt carries a hidden marker naming this owner, cycle,
and generation, and a consumer already holding one of ours is not told twice.
It is a COMMENT and nothing else, and that is the whole design. A pinned comment is written whole by whoever
writes it, so a handler of the child's own that read it before this pass and wrote it after would put the reclaimed
pointer back and take any park off, with nothing left to notice. A label is no proxy for "no writer" either: a
terminal finalize sets done / rejected before its last write, and closed workflow:ready /
workflow:blocked are swept by nothing, so a consumer a human left on one never becomes terminal at all. A comment
is appended rather than rewritten, cannot be lost to a concurrent writer, and reaches a consumer in every state a
consumer can be in. A child the pass could not reach, or whose thread it could not read or post to, leaves the entry
reclaiming rather than reconciling it, because reconciling is what stops anything coming back — and this owner is
the only thing that would.
What ACTS on it is the child's own guard, and it is the child's on purpose. It runs on the child's own dispatch,
ahead of every handler and ahead of the terminal no-op — which is the point, because a consumer that ended wears
done or rejected, reopening one leaves the label exactly where it was, and both are labels nothing below would
touch. Running there also means a relabel straight to another stage cannot route around it.
What it asks first is the receipt, and that answer is authoritative. The marker names the owner, cycle, and generation its own ancestry records, and it is read only off a comment of ours — so neither a later reclamation's receipt nor a third party pasting one can speak for this one. It outranks every reading of the ref because it records what happened: a mirror this host never dropped, or a ref pushed again at the same commit, would both make the world look untouched while the guarantee the child was given — that its candidate provably came from one adjudication — is gone. It costs one walk of the child's own thread per dispatch, paid only by issues a split created. A thread that could not be read is not a thread with no receipt on it: everything asked after this can look untouched while the answer that outranks it sits unseen, so an unreadable thread holds the dispatch rather than falling through to readings a receipt would have overruled.
Where no receipt landed — a crash, a thread it could not post to — the ref itself decides. This host's own mirror
first, so a child of a live split never reaches the wire; only a mirror that is already gone costs one ls-remote
for the exact ref and commit its ancestry records. What makes the mirror worth reading is the order a reclamation
runs in: it drops this host's copy before it touches the remote ref, and a copy that cannot be proved gone refuses
the reclamation rather than being logged past — so a mirror still standing says nothing has been reclaimed, instead
of saying so only when the best-effort half happened to succeed. Standing at the recorded commit, that is: the copy
lives in the object store every agent's worktree shares, so it is resolved and compared against the exact candidate
the ancestry names, and one pointed anywhere else is read as no copy at all.
That is a claim about the orchestrator that would do the reclaiming, so the pointer carries it rather than the
reader assuming it: the split stamps late_ancestry_mirror_first onto every ancestry it seeds, and a pointer
without one — written when the remote ref went first and the mirror came down best-effort behind it — skips the
shortcut and pays the ask. A mirror standing beside a ref that is already gone is exactly the world that ordering
was inverted to rule out, and exactly the world such a pointer may be living in.
The ask itself gives three answers and each is a different verdict. absent is the reclamation nobody told this
child about, and it parks. mismatch is the ref carrying somebody else's commit, which is not the candidate the
child was promised — the reclamation refuses one of those for a human, and so does this, under its own park reason
and its own sentence. unreadable is neither: an outage is evidence of nothing, so the dispatch is held for the
next dispatch with nothing written at all — parking every late-born child through a rate-limit window would be a
self-inflicted stop, and starting one against a ref nobody could vouch for is the failure the guard exists for.
One shape has no recorded ancestry to read at all, and it still has to stop. The transaction records a child on the parent's ledger before it seeds that child's ancestry, because a child on GitHub the parent does not record is a child nothing would ever come back to — so the window between the two is durable, and an ancestry write that failed leaves an issue whose BODY tells it to reuse a snapshot and whose pinned comment says nothing. The reclamation counts it as a consumer all the same and leaves its receipt on it all the same. So the body's own marker is what says whose child it is — free, since the dispatcher already holds the issue, and absent on every issue no split created.
That marker is also the one lineage claim here that comes out of a field the world can write, while everything it competes with is authenticated — a pinned comment only the orchestrator writes, a receipt checked against its author — so it is corroborated rather than believed. The owner's own generation is read fresh and has to name the same cycle and generation and carry this issue's number among the consumers it cut from that ref. A claim it does not vouch for is a claim about nothing and the guard steps aside: parking an issue on the strength of a sentence somebody typed into its body is a denial of service, and an owner cannot have reclaimed a ref while its own ledger may still be short one child. A record nobody could read, one whose consumer list this binary cannot type, and one naming no candidate all leave the claim standing instead — it may be true and this tick cannot tell — so the dispatch is held.
What a vouched claim yields is the whole pointer the failed seed never wrote: the ref the identity mints, and the commit the owner recorded preserving. The ask is then the same one the recorded shape makes — is this candidate still obtainable — and it has to happen, because the receipt is posted after the ref is deleted, so a thread with no receipt on it is what that window looks like, and so is a thread nobody could read this tick. The park then writes back the lineage the body claims — and only that, since the pointer came out of the owner's record rather than out of anything this issue holds: the repair the failed seed owed, and what stops the question being asked again.
A refusal drops the dangling pointer, parks the issue, and returns
before the label's handler is reached, so nothing runs against instructions naming a ref that is gone. Both writes
are taken on the issue's own dispatch, so there is no second writer to lose them to — which is what makes the park
durable where the owner's could not be. The one label it steps aside for is workflow:decomposing, beside the
adjudication guard that shares its read: an issue under adjudication is working from its own candidate rather than
an ancestor's snapshot. That is decided from the read rather than from the label, because the label alone cannot say
which of the two an issue is — a consumer closed while it was being decomposed comes back wearing it with no
generation of its own, and only a live generation on the record makes it this issue's own state.
A ref the remote refused to delete is a permission or ruleset problem an operator has to see, and the parent staying open is how they see it.
Both of those retries are still reached once the umbrella has closed nothing, because a human who closes the owner
mid-cycle takes it out of every other pass. The narrow closed-owner sweep is what brings a tick back to it: on the
existing CLOSED_ISSUE_SWEEP_EVERY_N_TICKS cadence, cleanup-only, cap-exempt, never an activation or a spawn, and
reached even past the backlog / paused filter that parks everything else — dropping a closed owner there would
lose the close itself, so the route is taken and the control label defers only what the pass would DO.
What it does with the cycle that close ended is below;
what it does with the ledger is exactly this, rule for rule (see
../state-machine/delivery-stages.md).
Two more things block it outright, and both are the same rule: nothing that cannot be proved settled lets a
terminal fire. An obligation ledger this orchestrator could not fully type blocks whatever the typed view says — the
entries it could not read are still obligations, and closing on the strength of a projection is the reading the
verbatim copy exists to prevent. So does a ledger holding anything at all on a record whose cycle identity is
damaged — a late_consumers list with no resource entry beside it included, read or not: there is nothing to
correlate a reclamation to and no issue number to prove a branch belongs to this generation, so the umbrella stays
open, or a blocked parent stays blocked, and says so where an operator reads it. An issue that never entered the
late gate carries no ledger and answers without a write, which is every umbrella the initial decomposer made. A record
a retirement left is not a damaged one: it names the cycle it dropped in late_retired_cycle_id, and while both
ledgers it kept are readable they hold either hand-off only on an entry still owed — so a parent handed back to its
own work, and re-decomposed by a later edit, is not held on the history its own settlement already finished. A ledger
this binary cannot read is no such history, and holds the hand-off as it would on any record with no identity.
What an ordinary re-decomposition holds its children to¶
A genuine edit hands the ordinary decomposer an issue that is already inside a late lineage: the umbrella a split made,
rerouted by a body edit or a trusted comment written after the reading the late path consumed, or a child a split made,
rerouted before its implementation started. The children the decomposer answers with are cut inside that lineage
whatever manifest they come from, and children created as ordinary issues would each read as a fresh root at depth 0 —
which is exactly how a lineage buys itself generations past MAX_LINEAGE_DEPTH. So before the split writes anything,
replacement_lineage.py asks late_split/provenance.py which lineage the issue's children inherit, and the answer is
one of three.
None, for an issue no late split charged: its children are seeded with a parent link and a creation stamp and nothing else, like the children of any split outside a lineage.
An inherited lineage: each child is born one level below the parent under the same root, with an ancestry naming the
parent and a cycle and generation to correlate by — the parent's own where its record keeps one, the one its own
ancestry names where it has none, and the retired cycle where neither stands. A parent already at MAX_LINEAGE_DEPTH
has no room for a child, so it is a refusal rather than a child at the bound's far side. The snapshot pointer is the
part that needs protecting: the reclamation deletes a ref once every consumer its owner's ledger records has ended, so a
child pointed at a ref nothing records is a child whose only copy of the work can be taken while it works. A child is
therefore pointed only at the snapshot the parent's own split still holds — the ledger naming it exactly once and
retained — and only once that ledger records it, in the same write that records it in children, ahead of its seed. A
snapshot another issue's split holds is protected by a ledger only that issue writes, and a pinned comment is written
whole by whoever writes it, so this issue may not add a consumer there: its children are born with the lineage and
without that pointer. A child that is pointed at the ref is also told about it where its implementer reads: the same
reuse instructions a late split's own children carry — the ref, its local mirror, the commit, the base it was cut
against, and how to cherry-pick or copy from it — are appended to its declared body when it is created. A ref the
parent's own split no longer holds is settled only where its entry has passed to a reclamation — reclaiming,
reconciled, or a failed delete — and its replacements are then born without it; a ledger that cannot say whether the
ref is held or released is not that answer, and is refused below. What never descends is the parent's own gate — its
measurement, its late_exempt_sha, and the late_override_* terms an operator authorized one of its commits on — so a
replacement's first oversized candidate is measured and adjudicated as the lineage's next generation, one level below
its parent.
A refusal, for a record that cannot say which lineage the children would be born into or which snapshot they may be
pointed at: every shape provenance.py refuses, a parent at the bound, a record naming no cycle at all, or a split of
the parent's own whose ledger cannot say whether its snapshot is held or released — a ledger this binary cannot read, a
held ref no consumer can be recorded against, an entry never proved or recorded twice (released twice included), or none
for its own ref (late_split/entitlement.py) — or a split of the parent's own holding its snapshot with no
late_base_sha, since the reuse instructions name the candidate's change as the range from that base and a range from
nothing is read against whatever the child's checkout holds. A manifest is refused the same way when a slice's own title
or body names a snapshot ref its child would not be kept — anything under refs/orchestrator/late-split other than the
one ref the child is pointed at and this repository's mirror of it. That includes the ref a descendant was itself cut
from, which its parent's ledger keeps for the descendant and for none of the children it creates, and every ref where
the child is pointed at none: the implementer reads the body, so a slice copying reuse instructions from the thread
would otherwise hand a child a snapshot nothing keeps for it. It includes a mirror under another repository's segment
too: a REPOS entry sharing the clone fetches its own split of the same issue, cycle, and generation numbers there,
which may be other work and is on no ledger this parent writes. And each mention is read as the whole ref name it could
be — as far as ref characters run on either side of the namespace, which is anything but an ASCII space or control
character and the few characters git refuses, so a non-breaking space runs a name on as surely as @ does. What is
taken off is only what no ref under refs/ can hold: quotes, backticks, or round, angle, or square brackets that open
the mention and are closed at its end, one leading + forcing the whole refspec, and a trailing full stop or slash. An
opener left unclosed, a second +, a pattern's *, and every other character a ref may contain are part of the name,
so a longer name that merely contains the child's ref (…/gen-1@foreign, `…/gen-1!`, …/gen-1,, …/gen-1*,
'…/gen-1 with no closing quote, ++…/gen-1, refs/heads/refs/…) is the different, unkept ref it is. A refspec names
both its sides, and wrapping opened before its source may close after its destination: the instructions themselves quote
the fetch as '+ref:mirror', so they read back as exactly the ref and its mirror. The split parks
replacement_lineage_unproved before expected_children_count is written, so no child exists and nothing is left for a
recovery to finalize; a reply is an edit, and re-runs the decomposer against the thread as it then stands.
A crash inside the split is repaired to the same answer before anything can start a child. The recovery that finalizes a
split whose every child was recorded asks the parent's record again and holds every recorded child to it, and what a
child is owed is read off that record rather than off the child. While the parent's split still holds its snapshot,
every child it recorded is owed the pointer — the split protected each one in the write that recorded it — so a child
late_consumers no longer names has lost a slot rather than settled one: it is recorded there again, in a write of the
parent's own ahead of its seed and of the finalize, and a child carrying none of the ancestry group, or a pointer at
anything else, is seeded with the pointer, whatever its title or body now says. Once the snapshot has passed to a
reclamation the lineage alone is owed, and a pointer still on a child is dropped, its ordering stamp with it and the
lineage beside it kept. A child carrying exactly the group it was owed is left as it stands. The child's text can only
refuse: the title and body are what the implementer reads, so they are read exactly as a slice is before creation —
every ref named under the namespace, whatever line or line ending it sits on, each as the whole name git would take it
for, a mirror standing for the ref only under this repository's own segment — and a child naming any ref but the one the
split still holds is not one this recovery may finalize. A child that split cannot recognize as its own is refused
rather than overwritten: a pinned comment that would not parse (it reads back empty, exactly like the seed a crash
deferred, and nothing on it can be checked), a parent_number that is not this issue's number — another issue's, or no
positive integer at all, since a float or a bool compares equal to one without being one — text naming a snapshot ref
the split no longer holds or never preserved (or another repository's mirror of one), or any other group — part of it, a
field its reader would drop, another lineage — which a child's own size gate would read as whatever it says. A split of
an issue no late split charged is held to the same recognition: its children are owed no lineage and no snapshot, so a
recorded child of one is left as it stands only where its comment parses, its parent_number is this issue's number or
not carried at all (a null is refused like any other non-number), it carries none of the group, and its text names no
snapshot ref at all — and refused otherwise. Only once every child passes is the parent finalized into the walk that
activates children; a record that no longer proves the lineage, or a child refused, parks the same way instead and
leaves every child unstarted. That walk — which is also how the split itself releases its children with no dependency,
in the tick that created them, over the same fresh read of every child's label and behind the same parks a rejected or
hand-closed child earns, since a human can act on one while its siblings are still being created — asks the same
decision again before it releases any of them, since a child starts off a record that may have changed since its seed,
and, before the first release, holds every child it would release, as each
then reads, to the recognition a recovery applies: its pinned comment has to parse, its parent_number has to be this
issue's number, its ancestry has to be the whole group it was owed, its pointer only one the parent's ledger still keeps
for it (the ref held, the child on late_consumers), and its title and body may name no ref but that one. A child a
recovery would have to refuse or repair — a seed taken off, a pointer at a released ref, a consumer slot lost, another
issue's ref added to its body — releases none of them. A child of an ordinary split is held to the same recognition,
owed no lineage and no snapshot, so every child either walk releases costs one read of its pinned comment. Either
refusal parks the parent, once. What the text is held to is naming no ref the split cannot keep, not keeping the
instructions: a child whose reuse instructions a human took out of its body keeps its pointer and its slot on the ledger
— a recovery puts back either one it finds missing — and is released uninstructed.
A child created and never recorded — the crash between the create and the parent write that records it — is found again
by what the split put in it. Every child's body carries a hidden receipt naming the parent, a split_attempt minted for
that split alone, its slice, and the late lineage the split seeds it with — root, depth, cycle, and generation, or
none — and the attempt goes onto the parent in the same write as the expected count and the
whole dependency graph, before the first child exists. A recovery whose register is short looks for the next slice's
receipt among the issues this orchestrator opened, walking every one of them, and records the one it finds — the only
issue carrying that receipt, not already in the register, open, still workflow:blocked, and ending on that receipt
whole — in a parent write of its own. Only a whole receipt counts, and the last one in a
body governs, since the split stamps its own after the slice and a slice may quote another child's body, receipt and
all, ahead of it; a receipt quoted there is no second claim. That write also records it on late_consumers wherever the
parent's proved lineage points its children at a snapshot, exactly as the write the crash lost would have: every child
of such a split is owed the pointer, so the entitlement is read off that lineage rather than off the child's text, which
may have been edited since. The recovery above then holds it to the same lineage and seeds it before anything finalizes.
A candidate a human closed or relabelled, one ending on another receipt or on none whole, one the register already
names (a sibling whose body was made to end on the receipt is still the slice it was recorded as), a receipt more than
one issue carries (no single child is named, and adopting the first could record a sibling twice while the real orphan
stays outside the register), a register still short once it is recorded (the rest were never created, and the
manifest is not kept to create them from), and a split an older binary prepared with no attempt all park
decomposition_crash instead, naming the candidate where there is one, so nothing points an unrecorded child at a
snapshot or starts it.
Every recorded child a recovery or a release would vouch for is also held to its receipt, where its body carries one on an issue this orchestrator opened: the last whole receipt has to be exactly the one the split stamped for the slot the register names the child in, once — this parent, the recorded attempt, that slice, and the lineage the parent's record owes. The dispatcher holds the child's seed to that receipt once it runs, so one naming another slice, attempt, or lineage would be seeded or released here and held there; it is refused instead, before anything is written, finalized, or released. A child carrying no receipt — every child of an older binary's split — answers the rest alone.
A child that carries a receipt and no parent_number naming that receipt's parent is one whose seed never landed — one
a crash left created and never recorded, the child a short register leaves recorded and unseeded behind its parent's
park, one a crash left between its record and its seed, or one another poller on the host held when its split went to
seed it — and nothing has proved its lineage. A seed that did land can lose its late ancestry, or part of it, or come to
name another place in the lineage, by hand. An edit can still route any of them into its own workflow:decomposing, and
a human can relabel it to workflow:ready or any other stage; every one of them would read the record as an issue no
split made, or as one at another depth, run an agent with no ref kept for it, and start whatever it creates there — a
replacement owed the lineage's last level splitting again past the bound. So the dispatcher holds the child against what
its receipt says it was owed, ahead of every handler but a terminal's, ahead of the step aside a live adjudication of
the child's own candidate takes, and ahead of the reuse guard that holds a late split's child whose ref is gone: a
parent_number that is exactly the receipt's parent, and the late_ancestry_* group exactly as the receipt owes it —
none of it on a child owed none, the whole group at that root, depth, parent, cycle, and generation on one owed a
lineage. A pointer is the one part it may lack, because the reuse guard drops the ref and its commit together once the
ref is gone; one it carries has to be that pair whole, naming the snapshot that split preserved. It is read off the last
whole receipt in the body and the record the dispatcher already holds, so it costs no request, and only a receipt on an
issue this orchestrator opened counts — a body is a field anyone can paste into. A held child parks
replacement_lineage_unproved once — a park already standing holds it silently, and a reply is no seed — until its
split or its parent's recovery seeds it, adding to that record and clearing awaiting_human in the same write, or a
human writes that seed and clears the park by hand. A restart an operator authorizes on the child's own cancelled cycle
keeps the seed — the parent link and the ancestry, pointer included, and nothing of the cycle's candidate — so the
restarted child is not held for a seed its split did write. A child whose pinned comment will not parse is held too,
with nothing written over that comment and nothing said but the log on every tick, since the park's own write would
replace whatever it carries.
What a close mid-cycle ends, and what it still settles¶
A human can close a late-split owner at any of the boundaries above, and every one of them leaves a different amount
of this orchestrator's work standing on somebody's repository. None of them leaves a workflow anybody wants resumed,
so the close ends the cycle rather than the tick, and late_cancellation.py is what the ending consists of. Two
passes reach it: the post-agent owner read above, for a close during a run, and the closed-owner cleanup sweep, for
a close at any boundary no agent was running at.
The mark goes down before anything external happens, and that is what makes everything after it safe to retry. It
says the cycle is over, so no gate below will spawn, adjudicate, relabel, or create another child; it carries the
moment the obligation was taken on; and it is irreversible within the cycle — a human who reopens the issue does not
get this cycle back, so a later visit re-marks the same cancellation and moves neither the stamp nor the boundary it
was taken at. What the reopen gets is the rest of the ending: the cycle settles what it already put on the
repository, the owner reaches rejected, and an operator removes that label to authorize a fresh attempt.
The late_cancellation record rides that same write, which is what bounds it to one per cycle rather than one per
sweep. The mark keeps the boundary as well as the moment, because cancelling is itself a phase and overwrites the
one it interrupted — and the interrupted one is what the retention rule falls back on when the record cannot prove
for itself that the split loop finished.
Then the held pull request, which is the one external thing a cancellation owns that the umbrella's terminal
never sees: every path that reaches an umbrella superseded the pull request its work was on along the way, so a
cancelled cycle is the only shape where one is still open under a "do not merge" notice with its original description
preserved on the issue.
Nobody is going to adjudicate it now, so the hold comes off, one marked notice says why, and the pull request is
closed — in that order, so a change that ends up closed is not also left wearing a hold nothing will ever take back.
A release that failed on a still-open pull request stops the close, since the preserved copy is the only record of
what the hold replaced. The notice is said at most once, proved from the pull request's own thread rather than from
a receipt, and the entry is recorded either way: reconciled, or failed with pr_reconcile_failed behind it.
It is re-asked on every visit, including one whose entry already reads reconciled, for exactly the reason the
ordinary supersession is: that entry records what an earlier visit did, and a human can reopen the pull request
behind it — an owner the sweep is still visiting for a branch it cannot delete would otherwise reach rejected, and
leave the sweep for good, beside a change that is open again under a cancelled cycle. Re-asking costs one fetch and
one comment listing and repeats nothing, while the write and both sinks stay behind a state that actually moved, so
a settled pull request adds no record per cadence.
Everything else is the reclamation owner's, unchanged. The superseded branch and the immutable ref are settled by exactly the rules and in exactly the order the umbrella's terminal settles them in, and a cancellation buys no shortcut through any of them — a consumer that is live again keeps the ref whether or not its owner is closed.
What a cancellation does change is which ledger the rule is read against. The count the split writes before its first create is what tells a partial ledger from a whole one, and a cancelled loop can never reach it: the children it did not make are ones nothing is going to make, so the ref would be held on a proof no pass could ever complete and the terminal with it. So the loop that stops writes down that its register is final — which it may, because every barrier that ends it is asked after the write that records the child in hand, and no further one will be opened. The ref then goes by the ordinary rule, once every child the split actually cut has ended.
The one cancellation that seals nothing is a resumed walk stopped before it reached the first unrecorded index. A create is a request and the write recording it is another, so a pass that died between them left a child on GitHub with nothing naming it; the adoption lookup is what answers that, and until this walk has asked it the register cannot be called complete. There the ref stays held on the count, exactly as before.
One branch is this ending's own to take on, and only one. The transaction settles the held pull request and
records
the branch that PR carried in two writes — the second is the retirement, and retiring ahead of a supersession that
might not land would let the children loose beside a change still carrying their work. A close in that window leaves
a cycle whose candidate is preserved, whose held PR is closed, and whose branch nothing on the record names, so
settling around it would retire the owner over a branch the remote keeps for good. A cancellation whose kept
cycle therefore resolves that branch and records it as owed — off the announcement's own receipt rather than off
the phase, because a park at the supersession is resumed from the top of the transaction, which rewrites the
earlier boundaries while stepping over the announcement it already made; a second failed attempt stands at
splitting with the receipt still set. Not before that receipt, since the snapshot is created and proved ahead
of the first child and the branch stops being the only copy there. Only where the record names no branch already,
in any state. And only once the held PR above is actually settled — that boundary is written before the
supersession is attempted, so it says the attempt was reached and nothing about whether it landed, and inferring
the branch while the pull request is still open would delete, out from under a change a human can still see, the
branch that change is built on. Nothing is lost by waiting: the pull request is re-asked every visit, and the one
that closes it takes the branch on.
Every child that already exists is left entirely alone. A cancellation mid-loop finds real GitHub issues carrying real slices of somebody's work, and what happens to them next is a human's decision rather than this ending's: they are not closed, not relabelled, not written to, and not commented on. The receipt a reclamation leaves is what a live split owes the children it is still responsible for, and a cancelled cycle is responsible for none of them — so even the visit that deletes the ref reaches no child. Each is still read, because proving every consumer ended is what permits the delete at all, and that reading is the whole of what any of them costs.
Their ledger entries are discharged, which is a different thing from touching them. The ledger is not an inventory of what exists but the list of what this orchestrator still owes somebody's repository, and a cancelled cycle owes a child nothing: the entry's obligation was the receipt, and there is no receipt. Marking them reconciled is a local write to the owner's own pinned comment, and it is what lets a settled cycle read as settled — left pending, they would say the ending is unfinished forever, on an obligation no pass is ever going to discharge.
Nothing about the ref goes unsaid by that silence. The transport drops this host's copy of the ref before it touches the remote and refuses the whole reclamation if that copy cannot be proved gone, so a child reopened afterwards finds no mirror, asks the remote once, and is stopped and told by its own guard on its own dispatch — which is where a receipt would only ever have been read.
A reopen resumes nothing, and skips no part of the ending. The mark is irreversible within the cycle, so a human who reopens the issue does not get that cycle back, and both labels an adjudication can be wearing name a handler that would act on the issue rather than settle it. Reopening fast enough does not undo it either: reaching the closed-owner route at all is what says a close was observed, so an issue that pass finds open again — a human who reopened it between the poll and the worker's refetch — is marked cancelled all the same, and stopped there, unless its record has moved on to a cycle restarted after the close, which that close does not end and the pass leaves alone. Nothing external is done to an issue somebody has just reopened and no terminal is written; the mark is what hands it to the guard below from the next tick. The dispatcher's own pinned-state guard catches that window: it runs exactly the reconciliation above, reaches no handler, and writes the same terminal below. It runs the cleanup rather than merely refusing because the closed-owner sweep visits closed issues only, so a refusal with nothing behind it would freeze the issue until somebody closed it again. What it does not do is close the issue: a human just reopened it, and the label is the whole of what this pass has standing to write.
Which labels that ending may be written from is the whole of what the label decides here — whether it is refused
is not, since a cancelled cycle is refused under every label including none at all. The two an adjudication runs
under are the two the terminal is declared out of. From the unlabeled state the record answers instead, because
three different issues wear that same nothing: an operator who took rejected off, a human who stripped a workflow
label mid-cleanup, and an ending whose terminal write GitHub refused. A cycle whose terminal is proved applied is
not handed it back, which would undo the one authorization a restart has; one carrying no such proof is owed the
write and gets it once its obligations settle.
The terminal comes last, and only once nothing is owed — branch, ref, and every unreconciled plan_pr entry on
the ledger, a recorded number with no preserved description beside it included. That last one is the entry no pass can
settle: the description the hold displaced is the only copy there was, so nothing may put it back or close over it,
and the terminal is held until a human repairs the record rather than closing a pull request this cycle cannot show it
ever marked. What the ending does discharge is the child receipts, which are recorded pending when each child is
created and which nothing has ever moved: rejected authorizes a restart, and a restart projects its fresh cycle only
over a ledger with nothing unreconciled on it, so leaving them would retire an owner whose restart then refuses for
good. Saying so changes nothing on the children themselves. That last reading is wider than what the pass acts on,
deliberately: acting takes the hold's own record, since releasing one means knowing which pull request this cycle
marked, while being owed takes the ledger, because an entry left under a number a later write cleared is still an
obligation and a retired owner is one nothing revisits. It is what a restart counts too, so retiring over one would
refuse the fresh cycle that terminal is meant to authorize. An owner that has settled all of them is handed
rejected, which is both the honest end of the cycle and what takes the issue out of the closed-owner sweep for good
— every label the sweep queries is one the owner keeps until this write lands, and this write is the only thing that
takes one off. So a cancellation that finishes
costs a bounded number of passes, and one that cannot — a remote that refuses a delete, a consumer that is live again,
a ledger a human has to settle — keeps the label, keeps being visited, and says on every visit what it is still
holding. An issue that never entered the late gate has no cycle to end and is not touched at all, which is every
umbrella the initial decomposer made.
The restart that ending authorizes¶
rejected is not only how a cancelled cycle ends; it is the thing an operator removes to start a fresh one. That
removal is the whole handshake, and it authorizes rather than merely permits: taking a label off is a write GitHub
grants only to a repository's own people, and the record it is read against is a pinned comment only this
orchestrator writes. So the ALLOWED_ISSUE_AUTHORS allowlist — the guard on the one route a stranger reaches by
filing an issue — is deliberately not asked here, and an outsider's issue an operator has decided to restart is
restarted.
What the record has to prove is a completed cancellation: a cycle that exists, one a close already ended, one that
owes nothing, and one whose rejected was proved applied. That last one is the second half of the ending's
two-phase terminal record, and it is there because the gesture cannot be read off the issue's surface: a human who
strips a workflow label mid-cleanup, and an ending whose terminal write GitHub refused, both leave the same unlabeled
issue an operator's removal does. The decision to write the label goes down before the write; the proof goes down
only for a label that landed, so an attempt never reads as a terminal. The pass that makes the write takes that proof
from the write returning rather than by reading the issue back, since a read proves no more than what the write left
on the object it went through and a closed owner gets no second visit. Where the proof is missing the ending writes
the terminal it still owes instead, and the handshake becomes available from there — which is also how a cancellation
that ended before this record existed is brought into it, since any visit that finds the issue still wearing
rejected writes the proof down.
Everything else stays inert, and inert means undispatched. A cancellation still owing the remote a branch, a ref, a child receipt, or a held pull request belongs to the cleanup above until it does not, unlabeled or otherwise; a closed issue is the sweep's whatever its record says; and an unlabeled issue that already carries a pinned comment at all — a rejection from anywhere else in the workflow included — is left where it is rather than greeted a second time, since a second pinned comment is invisible from the moment it is written while the finished workflow in the first goes on deciding.
"Owes nothing" is the ending's own outstanding list and the domain's settled-ledger reading, because neither contains the other. The pull request a generation names and cannot show it held is carried on no ledger and reported only by the ending — restarting over one would delete the last thing on the issue pointing at a change this orchestrator left marked and open — while a child receipt and an untypeable consumer ledger are counted only by the domain, and a restart that reached its retirement over one of those would be refused there with its marker already down.
The restart itself is a transaction over that same pinned comment, and each step is idempotent so a crash resumes at
the one still owed. The marker naming the cycle it intends, the cycle it succeeds, and the label it means to apply
goes down first — a tick that died between the write and the effects has to finish that cycle rather than mint
a second one and announce it twice. Then one notice on the thread, suppressed by a marker scoped to the cycle being
minted, and the target label, skipped where the issue already wears it and this orchestrator is what applied it —
a name somebody else applied is taken off and put back, since the restart's own application is what separates the
fresh cycle from its predecessor's terminal in the label history the ending reads. Only once both have reconciled is
the marker retired. Which label it is comes from the current DECOMPOSE setting when the marker is written and from
the record thereafter, so a restart begun under one setting and resumed under the other finishes what its own notice
announced.
A refusal from either effect keeps the marker standing and is said out loud on both sinks; backlog / paused defer
the whole of it, since the authorization sits on the issue's own surface and no tick can lose it.
What the fresh cycle inherits is what is true about the issue rather than about the attempt that ended: the
pinned comment's own identity, the bounded list of comment ids this orchestrator posted, the cumulative usage the
issue has already paid for, and the identity joining the new cycle to its predecessor. Every session, the pull
request and branch, the children and the dependency graph, the snapshot it was cut from, the parks, the drift
baseline, the counters, and the timestamps are gone, and the lineage depth and generation counter are back at zero —
a restarted issue is a fresh attempt with room to split, not a cancelled one wearing a new number. The full key list
is in ../state-machine/labels-and-state.md.
What a human can still change once the transaction has started¶
Every transaction park is a moment the humans can speak into, and the coordinator settles what they said before it replays the recorded verdict — so a split that stopped halfway can meet a fresh edit or a fresh instruction on the next tick. Two rules keep that from undoing work that already exists.
A generation whose split has already acted outside this process may not be revised into a new one, and two effects put it past that point.
Children, because a second manifest over the top of real GitHub issues strands every one of them: nothing drives a child the parent stops recording — no walk activates it or waits on it, and the one pass that still reads it is the snapshot cleanup, asking only whether it has ended — they carry an ancestry naming the adjudication that made them, and they are the consumers the snapshot is retained for.
A recorded snapshot obligation, even with no child yet. The ref is named for the generation but the commit under
it is the candidate that generation froze, and the reclamation proves a ref is ours to delete by comparing the two
— so a revision that moves candidate_sha leaves the entry pointing at a ref that no longer matches it, the
reclamation reads a mismatch and refuses, and the umbrella's terminal stays open over a ref nothing can ever settle.
The entry is refused in every state it can hold, because none of them proves the ref is absent: pending is a push
that may have landed, and failed is a create that may have landed beside a verification that did not. An opaque
ledger is refused with them, since an entry this binary could not type may be exactly that obligation.
Either way the issue is handed back naming what stands — their comment stands, whatever the split created stands, and the recorded verdict stands — and which of those to keep is a decision about things that already exist.
A generation that does advance carries none of the previous one's split receipts. late_split_children and
late_links_announced are cleared with the counter, because both are one-shot and positional: a register carried
forward would have the new manifest adopt an old child by index, and a link receipt carried forward would swallow the
announcement the new split owes. What is not cleared is either external ledger — a ref the remote holds is owed
whatever this generation decides next.
What the humans can still change while a candidate is frozen¶
Adjudication takes minutes to hours, and the issue is a live thread the whole time. Two local fingerprints watch it —
late_title_body_hash over the title and body, and late_comment_hash beside the late_comment_watermark_id it
covers from — and they are deliberately separate from the global user_content_hash, which keeps its single baseline
and its meaning so nothing here fires the re-decompose or dev-resume routes that read it. What counts as a human's
words is that hash's own trust filter all the same, so an outsider, a third-party bot, and the orchestrator's own
comments shift nothing. The fields themselves are in
../state-machine/labels-and-state.md#late-generation-state.
Separate is not unrelated, though. Every path below that acts on a reading — a park answered, a candidate certified,
a question reopened, a stalled revision re-read, the developer resumed, an authorization recorded or refused, a spent
budget continued — records the global user_content_hash in the same write as the shared watermark, taken off that very
reading of the title, body, and thread under the global hash's own filter. Without it the stage the issue reaches next
would meet already-answered guidance as an edit: the umbrella a split hands the issue to would orphan the children it
was just given and decompose the same work a second time. Nothing is read again for it, so a comment or an edit that
arrived after the reading — during the developer's run, during the adjudication, before the handoff — is still drift for
that stage, and still reaches whichever agent it routes to. Guidance a park's notice withheld keeps it where it was the
same way, until an agent has been handed that guidance (below). What only observes a reading leaves
user_content_hash where it was: the baseline a generation takes on its first tick records the local fingerprints
alone, and a drift park nobody has answered moves no baseline but the shared watermark, past the notice it posts.
Nothing writes it without a consumption; an issue with none has the reading an adjudication carries on over kept as
observed instead (below). Nor
does a developer run that did not happen move it: the consumption rides the write that reconciles what the run left, so
a pause, a shutdown sweep, a launch the run circuit refused, or a close latched before the resume or while the run was
out leaves user_content_hash, the local watermark, and the replies still owed where the prior tick left them — the
park a refused launch takes may still move the shared one past its own notice. So does a run whose CLI stopped before it
worked — on its account's quota, on any refusal its provider answered with (a 401 or a 429 as much as a 529), or with a
failed exit and nothing said: what it left is reconciled and parks as any unanswered revision does, but over guidance
still unread, so the continue that answers that park hands it to the developer whole. A quota stop counts even when
the run's timeout fired too, and whether the CLI said so in its final message or, as Antigravity does, only in the
failure diagnostics its result keeps.
The first tick of an adjudication takes the baseline — over what the issue-wide user_content_hash covers, which is
what the developer last worked against, and no further (late_issue_baseline.py). Comments are append-only, so that is
the longest prefix of the thread that, beside the title and body as they now read, reproduces the recorded hash. A
comment past it — written while the developer's last run was out, or under a notice since — was read by nobody, so it
stays uncounted and is handed to the developer whole, as fresh or withheld guidance (below), rather than folded into the
baseline. A reading no prefix of which reproduces the recorded hash is one whose title, body, or covered comments moved
since, which is drift: it parks exactly as an edit under a baseline would. It parks on a baseline that counts no
comment, since which of them the recorded hash covered can no longer be told, and holds the title and body to that
recorded hash itself, which keeps the reading drifted until a human answers or the edit is taken back. Every trusted
comment is therefore withheld or fresh guidance meanwhile — one written under the drift park's own notice included — and
whatever answers the park hands them to the developer whole. Where that hash covered a comment, no title and body alone
reproduce it, so a drifted reading of that baseline is compared against the recorded hash once more: a thread that again
has a prefix reproducing it had its edit taken back, the baseline is retaken on that prefix, and the park clears as the
revert below does, with anything past the prefix still handed to the developer whole. An issue with no recorded hash has
recorded nothing as read, so its first baseline counts no comment at all: every trusted comment on the thread is handed
to an agent whole before anything records it read. Nothing records user_content_hash for it, since nothing was
consumed: an adjudication that carries on over a quiet reading keeps that reading as observed_user_content_hash
instead, and the drift check of the stage the issue reaches next — its split's umbrella — compares against it while no
baseline is recorded, so a comment or edit made during the run is an edit there rather than the baseline that first
poll takes. A generation baselined over the whole thread before its baselines were held to the issue-wide one — it
carries no late_baseline_bounded — may count comments no stage consumed, and once the title or body has moved which
ones cannot be told. So it gives up what it counts before anything is compared, drifted or not: baselined again as a
first reading is where a hash is recorded, and holding its own title and body fingerprint where none is. Those
comments then reach the developer before any split, from the reading itself or from whatever answers the drift park,
rather than being certified over. Every tick after that compares.
A spent spawn budget outranks even drift. An adjudication is charged to the issue's shared daily spawn budget,
and a budget with nothing left in it parks the issue as retry_cap before any of the reading below happens. What
that park is waiting on is a human deciding to spend more of the issue's day on this candidate, which an edited body
is not — so the tick ends at the park, the fingerprints are not even read, and the frozen commit, the late session,
the recorded generation, and the hold all stay where they are. The one reply that lifts it is a trusted
/orchestrator continue, and what it buys is one adjudication; the notice explaining the park has to have reached
the thread before any comment on it is read as that answer, since saying it is what moves the response boundary.
The grant spends the batch the command came in, words beside it included, on the adjudicator it buys — and since that
run reads the thread through a bounded excerpt of its tail, the words are also recorded as owed a whole quote (below).
So it consumes that reading whole: the local fingerprints, the shared watermark, and user_content_hash — on a
generation whose first late baseline is still to be taken as well, which takes it here, over what the issue-wide
baseline covers. A reading of such a generation that the recorded hash no longer reproduces moves the shared watermark
alone, owes the adjudicator the words beside the command, and leaves the content read below to park on the move as
drift. A reading that shows an edit under a baseline that exists, which the content read below is about to park on,
moves the shared watermark alone — rebaselining would swallow the edit, and nothing has answered it — and leaves the
words beside the command withheld guidance for whoever answers that park. So does a reading that still withholds
guidance: that is the developer's to be handed, not an adjudication's, and with the park answered the content read below
hands it on.
The budget itself is in ../state-machine/labels-and-state.md#the-retry-budget.
Drift outranks every answer. An edit to the title, the body, or a comment already counted into the baseline
changes what the candidate is supposed to BE, and an answer that arrived in the same window was written about the scope
as it stood before — applying it would adjudicate a reply against requirements it never saw. So the tick that first
sees drift parks (late_content_drift) and consumes nothing: the frozen commit, the late session, the recorded
generation, and any hold are all left exactly as they were, because none of them is wrong, only unadjudicable
until a human says what the edit meant. A comment rewritten in place is drift for the same reason a title edit is —
it moves no comment id, so there is no new comment to read the change out of.
The park is a response boundary, not a one-tick delay. What counts as a reply is read against the higher of the
generation's own comment watermark and the issue-wide last_action_comment_id, which every announced park advances past
the notice it just posted — so an answer written before the human was told anything cannot resolve the park on the next
poll either. Nothing else advances that watermark without consuming what it advances past, and what a notice advances
it past is held back rather than lost (below), so the conservative reading costs no real reply.
What the boundary holds back is not thrown away, though. Trusted guidance past the local watermark and under a notice —
words a human wrote while the issue was being parked, which no agent has read — is reported as withheld. It never
answers the park that withheld it, but it is never folded into a baseline on the way past either. Every developer
revision quotes it ahead of the fresh guidance, since the run folds both. A reverted edit that finds any routes it to
the developer, exactly as guidance beside the revert would: the requirements ended the park, and with nothing left for
those words to answer they are an unread instruction. So does an issue whose park has gone — retired by a fresh attempt,
or lifted by a retry-cap continue — with some still on the thread. So, too, does a certificate or an answered question
that finds any: the adjudication either would buy is consumed for before its spawn gates — the budget, the run limit, a
pause, a close — and a run stopped at one would have spent the words on nobody, while a developer run is consumed for
only once it has run. So, finally, do the two answers that would end a park with no agent at all: an authorization,
which would hand the issue to publication where the next agent reads the thread through a bounded excerpt, and a
continue on a stalled revision, whose re-read could settle the candidate under the ceiling and hand it there too — the
developer is resumed with the words quoted whole instead, and nothing publishes. A continue refused while its park
stands spends its own reply through the shared watermark and nothing else: the local fingerprints and
user_content_hash stay where they were, so the withheld words are still there for the next reading to hand on and
still an edit to the drift check of whatever stage the issue reaches first.
Then the reply resolves it, and the two kinds of reply mean opposite things. A bare /orchestrator continue is a
certificate: the committed work still answers the updated issue, so the fingerprints are re-baselined onto the content
as it now reads and the SAME frozen candidate goes on to be adjudicated — against the updated requirements, which is
why a verdict recorded before the edit is dropped rather than reused. The certificate covers the commit, not an answer
taken against a scope that has since moved: acting on one would be the drift rule refused a step later, with a split
creating children that describe requirements nobody is asking for any more. What it does buy is everything else —
the candidate is not re-derived and no developer is paid for. Substantive guidance is not a certificate — it says the
work has to change, so the developer session is resumed against it (above) and the candidate is re-frozen from what
comes back.
An edit taken back needs no reply at all: the candidate matches the issue again, so the park is cleared and the
adjudication resumes. Leaving it standing would not be harmless — awaiting_human is exactly the flag that
suppresses the announcement a question verdict earns, so a reverted edit would silence a question recorded and never
said out loud. Guidance that came with the revert is still guidance, though, and it is routed before any of that:
taking the edit back decides which requirements the change is asked against, not whether it was asked for. Absorbing
it into the baseline instead would consume a human's instruction without acting on it, and then reuse a verdict
nobody re-earned.
A revised candidate is proved, not trusted. The tree has to be clean before anything is read off it — a candidate measured beside uncommitted changes is not the one a publication would push — and the commit the checkout ends on is frozen and measured again from scratch under the ceiling as it stands now. What is not allowed is skipping the measurement, which is why the generation counter advances on every reconciliation that lands — a recorded verdict answers a cycle, a generation, AND a commit, so an acknowledged candidate is adjudicated against the requirements that changed rather than answered from the record taken before they did.
The resulting SHA is allowed to be the one that went in, but only when the developer said so. The prompt asks for
the same ACK: <justification> marker every other drift resume asks for, and that marker is what an unchanged commit
needs before it is re-measured; the prompt carries the developer report contract beside it and, like theirs, offers the
marker only when nothing the report says has to change either. Without one, an unchanged commit is not an
acknowledgment — it is a run that said
nothing, asked a question, or timed out before it could do either, and all three look identical from the checkout.
Reading any of them as "the work already covers it" would advance a generation and adjudicate a candidate nobody
vouched for, so they park (late_revision_unanswered) with whatever the developer did say quoted, so a question
reaches the human it was meant for. A commit that MOVED needs no marker: work that changed HEAD speaks for itself. The
one path where an unchanged commit passes without one is the human's own /orchestrator continue on a stalled
revision — they have read the park and accepted the commit as it stands, which is the same thing the marker says.
A reconciliation that could not
be completed parks (late_revision_dirty / late_revision_unmeasured) with the generation exactly as it was, and a
bare continue re-runs that reconciliation alone rather than paying for a second developer run that already finished.
Guidance means the same thing with nothing parked. An adjudication in flight, or one that already recorded a
verdict, is still work a human can ask to be different — so the developer is resumed there too, and the re-measured
candidate that comes back advances the generation, which is what stops a verdict taken over the old work from
applying to the new. Folding the comment into the baseline instead would consume an instruction without acting on it.
The one reply that lands here with nothing to do is a bare /orchestrator continue: no park is waiting on it and no
candidate needs certifying. A bare /orchestrator add-agent-runs N is no guidance either — what it widens is the run
allowance, which the run-limit hold answers — so it resumes no developer, and the adjudication carries on past it.
A categorized question is reopened only by a real answer. Substantive trusted guidance drops the recorded outcome — the record is exactly what suppresses the next spawn — so the adjudicator runs again against what the human said, quoted whole (below). A bare continue may not: a question is not a step that failed, and "proceed" is not an answer to "which half of this is in scope". The command is consumed, the refusal is posted once, and the issue stays parked on the question it is really waiting on. An answer that finds guidance the question's notice withheld resumes the developer with both quoted instead, for the reason given below.
What an adjudication is owed. The late adjudicator reads the thread through the same bounded excerpt of its tail
every conversation-carrying prompt carries, and the two replies consumed on its behalf — a question's answer and the
words beside a retry-cap continue — are consumed ahead of its spawn gates. So both are also recorded as owed
(late_owed_replies) and quoted whole after the excerpt to every late run until one has answered them: the
adjudication repays them with the verdict it records over them, and a developer revision, which quotes them too, with
the candidate its reconciliation re-measures off its answer. Nothing short of that repays them — a run refused at a
gate, paused, or killed; one stopped by its timeout; one whose CLI stopped on its quota or a provider refusal before it
said anything; a reply nothing could parse; a revision whose reconciliation parked, a question over an unchanged commit
among them — so the run its park earns is quoted them again, and a recorded answer is not reused while any reply is
owed. So a long reply the late watermark and user_content_hash already count as read is still handed
whole to the next agent that runs over this candidate, rather than to a run that sees only its end.
Nothing outside the adjudication may decide it either. While a generation is live — recorded, not cancelled, and
either oversized or still owing an owner read — workflow:decomposing is the label it sits on, and both ways that can
be taken away amount to publishing an unadjudicated candidate. The owed read is the half that is easy to miss: a
revision that came back UNDER the ceiling closes the size question and leaves the owner one open, so every gate keyed
to size would wave it through while nobody had established that the issue is still there.
DECOMPOSE=off routes a decomposing issue into the legacy implementing flow, which is
right for an issue only waiting to be decomposed and wrong for one whose implementation is already committed and
measured past the ceiling, so the route is refused while a generation is live — the switch still keeps NEW candidates
out of the gate, it just does not decide the ones already in it. A hand relabel is caught a step later, since the
label is
already gone by the time anything reads it: the dispatcher asks before it routes anything, and an issue whose label a
human moved is put back on workflow:decomposing, told why, and left for the next tick rather than handed to the
stage the new label named (late_relabel.py). The refusal is the safety property and the relabel only the repair, so
a label write that cannot land still stops the dispatch, and so does a pinned read that cannot be taken. The
restoration itself goes out UNGUARDED and posts its notice only after the label lands: the transition graph describes
the moves this orchestrator makes, and putting back one a human made is not among them, so under
WORKFLOW_TRANSITION_GUARD=enforce a guarded validating → decomposing repair would raise every tick and strand the
generation under the wrong label — announcing itself again each time. That last
one is the single place this guard does not follow the additive-safety-net convention the pause probe reads by,
because the costs are not symmetric: failing open publishes an unadjudicated candidate — the handler behind it reads
the same pinned comment, and a first read that failed transiently is followed by a second that may well succeed —
while failing closed costs one tick of one issue, retried on the next poll, during an outage in which nothing else
was going to make progress either. Neither
clears, cancels, or decides anything — a
generation an operator really wants gone goes through the late domain's own cancellation, which records what the
remote is still owed.
Local verify gate (not an agent)¶
After the reviewer emits VERDICT: APPROVED — the whole subject it was handed still stands, and the approval relies
on settled verification evidence that passed and covers every configured command: its own declared run on the reviewed
head, published as reviewer-reported evidence, or the exact current evidence it reused, proved current again; an
approval without either parks under reviewer_unverified and never reaches this gate — _handle_validating runs the
configured VERIFY_COMMANDS directly in the per-issue worktree — these are plain shell commands, not an agent role, so
no *_AGENT env var applies. The gate runs before the approval record, the approval
comment, the squash, the watermark seeding, and the workflow:documenting (final-docs) label flip. A clean run
advances the issue, and so does an empty VERIFY_COMMANDS, whose not_run result is not evidence that anything
passed; any failure parks on workflow:validating with a typed park_reason (verify_failed / verify_timeout /
verify_dirty / verify_head_changed / verify_tree_changed) -- only where the subject the reviewer approved still
stands behind the gate and behind the park's own notice, since a failure over work nobody reviewed is a fresh
reviewer's to answer. See
../configuration.md#local-verification-gate for the env-var
reference.
The same commands also run outside this gate, through the same runner, on a head an automatic base rebase published,
where no current evidence carries onto it and a reviewer was already handed a report of that head. That run gates
nothing: a pass is recorded as orchestrator-executed evidence of the rebased head, and its artifact posted and
settled, before it is routed to workflow:validating, and a failure parks nothing -- its notice is posted on the pull
request and the fresh reviewer verifies the head itself (see
base-rewrite evidence).
A tick that dies around that run leaves its recovery to run the commands again only where no run was recorded; a
recorded run is never run twice, but settled and routed as recorded -- its artifact found under its receipt where an
earlier post landed -- or, where anything it was bound to moved, set aside for the fresh reviewer.