feat(board): E1 implement lane split — Architect / Implementer relay (v7.30.0) - #41
Merged
Conversation
…chitect / in-design / ready-for-implementer (E1); final LEGAL table, DISPATCHABLE/PRE_PARK/EDGE_NOTE tables, mechanical suite rename
…literal; Step 8 grep scoped to scripts/ (SKILL.md/ticket-gate rewrites are Task 13's)
…keleton guard covers both lanes
…n the Architect handoff edge
…ign/in-progress/in-review)
…th [answers] reset
…umes, ready-for-* retires)
…'s full RECOVER line (the bare "resume attempt 1/" substring is satisfied incidentally by seeded tickets 10/12)
…in, X4 exemption, per-lane slot caps
…intf|grep -Fq -> herestring, matching test-review-dispatch.sh)
…moved, architect escalation edges added (E1)
…e every other section; assertion pins MODE SELECTION as the plan's Step 1 intended (plan text amended to match)
… — the latter pre-dated this task and could not catch a regression to the retired mode
…st — the board has no ready-for-architect → ready-for-implementer edge, so the documented decompose exit was unreachable from the gate (plan text amended; assertion pins it)
…eedback is T14's; T16 owns the repo-wide proof)
…ect-lane gate variant, lane-aware rituals
…e skill prose must not force a permanent carve-out in the residual-vocabulary grep (plan text amended)
…ocabulary, 1:1 swap)
…r-architect impasse route (E1; pins/fix-wave ride C5)
…ins (pre-spec runs DIRECT and does post a gate comment); add Step 4b for ORIENT + PARKED-tier completeness
… the impasse turn-ending, patch ORIENT's pointer Review found three defects in the T15 lane-split landing: - the authorization-anchor rule claimed no plan:-pinned ticket carries a [gate] pass, which is false for plan: pre-spec (DIRECT mode, posts a real gate) — only plan: <path>@<sha> (PLAN-EXECUTION) posts none; narrowed the rule so pre-spec tickets still anchor on their gate - the RE-REVIEW impasse's ready-for-architect branch had no turn-ending unlike its needs-human sibling, and ESCALATE's PARKED tier didn't cover a ticket that route just parked there - ORIENT pointed only at the [gate] pass comment, which doesn't exist on a real plan: <path>@<sha> ticket; added the handoff-comment alternative as a pointer, consistent with the anchor rule
…es — handoff comment admits the missing-section finding, plan pin joins the spec hierarchy
…ion event, pre-spec is a plan-need ruling
…t version; name the two known residual test descriptions
Every prior test that lands a ticket on ready-for-architect starts from ready-for-implementer; the Implementer's mid-execution return edge had no dedicated coverage. Assert the edge is legal, note-required, and emits the arrow-keyed [board] in-progress → ready-for-architect: <note> comment (it is convergence-counted, unlike the plain-form edges).
The branch added five rules to the review worker protocol (plan: pin in the spec hierarchy, the PLAN-EXECUTION-mode authorization anchor, the missing-Validation-Evidence-section carve-out, the RE-REVIEW seam-clustered impasse route to ready-for-architect plus ESCALATE's PARKED-tier coverage of it, and TOO BIG's design-missing birth classification) with nothing asserting them. Anchors were chosen and verified absent from the pre-branch file (git merge-base main HEAD) so each pin actually discriminates a reversion.
… position it doesn't carry 'both positions: <note>' followed only the second traversal's note — the first side's position was never in this string, only recoverable from the comment trail. Reworded to say what the note actually carries and point the reader at the trail for the other side; kept the leading convergence: token and the no-third-mechanical-bounce phrasing intact.
…n without re-supplying --pr board-transition.sh required --pr on every entry into in-review, including the mechanical return from a needs-human park (PRE_PARK["in-review"] = "in-review"). board-answer.sh's return posts a note only, never --pr, so answering any review-loop park died with "a PR link is required" and left the ticket stranded at needs-human with its bound session never resumed. The PR gate now also accepts the ticket's already-recorded pr: meta, not only the --pr flag — preserving "a ticket in in-review always has a PR recorded" without weakening the check for a genuine first entry.
…rgence resets The convergence rule resets its per-edge escalation count at the last comment prefixed [answers]. board-answer.sh only posted that marker for inline answers; the sweep's unattended RELAY pass always calls board-answer.sh --posted (the human commented on the ticket directly), so on the documented unattended path the reset never fired. A resumed worker's human-authorized retry of the same escalation edge then got bounced right back to needs-human -- the exact bounce the human's answer was meant to end. --posted now posts its own [answers]-prefixed marker before the pre-park return runs, so the mechanical reset fires like it does for inline answers. Also names PLAN-EXECUTION (which runs no gate) in the relay prompt sent to the resumed worker, next to the "re-state your gate verdict" instruction that otherwise doesn't apply to it.
…cket too The orphan check was filtered to in-progress only, so an Architect worker that died with no usable registry binding left mid-design work orphaned invisibly -- the pipeline's most expensive in-flight asset, and nothing surfaced it. board-sweep.sh's RECOVER pass already covers both in-flight worker states (in-progress, in-design); the reconcile report now matches.
…oring mode operation-manual.md still described the pre-lane-split shape: an Implementer self-authoring a plan document (Direct/ExecPlan) via doperpowers:execplan. The shipped protocol (SKILL.md) has DIRECT and PLAN-EXECUTION instead -- an Architect worker authors the plan, the Implementer executes it and authors no plan document, ever. Replaced the stale mode description, named the architect lane in "The pieces" table (it enumerated only the implement/spike protocols before), and fixed the two Edge cases claims that the next gate re-run is unconditional -- false for a plan-pinned ticket, where PLAN-EXECUTION is gate-free by design -- adding the strip-the-stale-pin caveat issue-tracker/SKILL.md already carries for the fallback path.
… dead test stub) - issue-tracker/SKILL.md dispatch ritual step 4: name all three first-board-write shapes (Implementer in-progress+[gate], Architect in-design+[gate], PLAN-EXECUTION in-progress with no gate comment) -- was stale for two of the three lanes. - Gate-language residue aimed at workers that never ran a gate: one qualifying clause each in implementing/SKILL.md's "If Resumed With Answers", board-answer.sh's relay prompt was already covered in the prior commit; board-sweep.sh's recovery nudge gets the same clause here. - sweep-setup.md's Knobs table documented IMPLEMENT_MAX_CONCURRENT but not ARCHITECT_MAX_CONCURRENT/ARCHITECT_MODEL, so an operator arming the sweep couldn't discover the Fable-spend lever. - Deleted the dead scaffold in test-implement-dispatch.sh that wrote a stub skills/architecting/SKILL.md into the real repo tree when missing -- the real file exists now; the guard was inert and, if it ever fired again, would leave an untracked stub and mask a missing-protocol failure.
- needs-human -> in-review return through board-answer.sh without
re-supplying --pr (Finding 1, the merge blocker).
- --posted relay resets the convergence counter: baseline 2nd-traversal
conversion, the relay posts an [answers] marker, and a 3rd traversal of
the same edge does NOT convert (Finding 2).
- board-reconcile.sh flags an orphaned in-design ticket, not just
in-progress (Finding 3).
- Updates the pre-existing "--posted posts no second [answers] comment"
assertion, which pinned the buggy pre-fix behavior, to match the fixed
shape (a [answers] marker is now always posted).
New sections are placed at the end of the file (after "mid-execution
return") deliberately: every ticket reference past that point in the
suite is captured dynamically (${out%% *} / state "s['next']-1"), so a
new mid-file board-register.sh call can't shift a later hardcoded ticket
number the way an earlier attempt at this did.
…tion lesson from the whole-branch review
…e enforced/unenforced half of the model-economics argument stated plainly
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
…tric with the architect's fable pin The lane split's argument is asymmetric model routing, but only the architect half was structural: the plain-Claude implement route inherited the operator's session model (the posture #35 set), so an operator running the frontier model paid frontier rates on both lanes and re-fused the two economies the split exists to separate. IMPLEMENT_MODEL still overrides, and the codex route's fable default is untouched.
…aves in-review, before spawn dedupe ever runs Finding 1 (HIGH): a PR routed to ready-for-architect stranded permanently — no mechanism ever re-attached a reviewer once the ticket returned to in-review, since a normally-finished reviewer's meta made every future sweep tick's _decide skip it as "finished reviewer (idle)" forever. An earlier version of this fix had the review worker retire its own daemon as its final action before ending its turn. That mechanism is unreliable: daemon-retire.sh calls `claude stop` on the target BEFORE writing status=retired, so a worker asking to stop itself most likely never reaches that write — the meta finalizes idle instead, which is exactly the state that strands it. Retiring must happen from outside the dying session. The corrected mechanism moves the retire into the sweep loop itself, before the registry dedupe machinery ever runs: the sweep now resolves each ticketed PR's primary-ticket status (extending the existing gh pr list call with closingIssuesReferences/title/body so no per-PR issue lookup is needed) alongside the confident-ready check. Whenever a ticket isn't in-review, run_for retires any FINISHED (non-active) reviewer meta it finds for that PR and skips the tick without spawning; a genuinely ACTIVE (working/blocked) reviewer is never touched, since it owns its own exit. That retire is what lets the ticket's eventual return to in-review land on the pre-existing "none / retired -> dispatch" row with no special-case dispatch logic needed once it's back. The rule generalizes past ready-for-architect specifically -- any route that moves a ticket off in-review while its PR stays open leaves the bound reviewer stale by the same definition, so the gate reads "not in-review" rather than an enumerated label set. Finding 2 (MEDIUM, same root cause): the sweep previously only recognized needs-human/needs-info/interactive-preferred as reasons to skip a dispatch, so a ticket freshly routed to ready-for-architect (or any other non-in-review state) would still let the sweep spawn a reviewer board-bind.sh refuses (the ticket's real owner is an active daemon) -- one wasted spawn-and-retire per tick. Folded into the same generalized check above; its skip message distinguishes the two real resume paths (board-answer for the three human parks vs. return-to-in-review for everything else) instead of lumping them together.
Role resolution is state-free for spikes (category outranks lane), but the spike protocol's gate-pass write is `board-transition.sh <n> in-progress`, and LEGAL["ready-for-architect"] has no in-progress edge. A spike admitted there (registration or any transition, including the untracked/conflict repair path and the QAgent/mid-execution escalation edges) is left with only the parks, and its stuck ready-for-architect state also eats an architect-lane dispatch slot since ARCHITECT_MAX_CONCURRENT counts by ticket state, not by which protocol actually dispatched. Close it at both ends: board-register.sh refuses the birth combination, board-transition.sh refuses every transition whose target is ready-for-architect on a spike-category ticket. Does not touch LEGAL["ready-for-architect"] itself — adding an in-progress edge there would also legalize an Architect skipping the design phase, which the lane exists to prevent. An earlier review declined this ban absent a reproduced failure; it is now reproduced (registering a spike --state ready-for-architect and running its gate-pass transition dies with "illegal transition"), so the ban is earned.
…rough PRE_PARK Two board-schema correctness fixes discovered together (both center on board-transition.sh's write path and _board.py's transition tables): plan: pin auto-clear — board-transition.sh only ever wrote extra["plan"]; nothing cleared it. A ticket that goes --plan <sha> -> in-progress -> ready-for-architect "blocked" -> in-design -> ready-for-implementer "decomposed" (no --plan) kept the original pin, so the Implementer entered gate-free PLAN-EXECUTION against a plan the Architect had just declared blocked. Now cleared on exactly two edges where the pin is void by definition: any entry into ready-for-architect (the design is being re-cut; --plan can only be supplied when to == ready-for-implementer, so this never collides with a fresh write), and the Architect's own in-design -> ready-for-implementer decompose exit with no --plan (a positive "no plan" statement). Deliberately not extended to other edges into ready-for-implementer (e.g. unparking from needs-human) — those must not silently void a still-valid plan. Updated the two prose sites (issue-tracker/SKILL.md's wake ritual, implementing/operation-manual.md) that told the operator to strip the pin by hand; the tooling now does it. pull_epics routing — PULLABLE gained ready-for-architect in the E1 split, but LEGAL["ready-for-architect"] has no in-progress edge, so an epic parked in the design queue with a newly-active child was silently written to an edge the table forbids. pull_epics now targets PRE_PARK.get(parent_state, "in-progress") — reusing the existing queue -> in-flight mapping rather than a second table — so a ready-for-architect epic pulls to in-design. Legality is asserted at the pull_epics call site (not inside apply_state, which close_epics also uses and needs wider latitude for) so a future PULLABLE/LEGAL drift fails loud instead of silently writing a bad edge again. That assertion surfaced a companion gap: "deferred" was the one other PULLABLE state with no PRE_PARK entry and no in-progress edge in LEGAL["deferred"] either — the identical bug, just never reported. Added in-progress to LEGAL["deferred"], matching its sibling park states (needs-info/needs-human/interactive-preferred already permit it). Confirmed via git log this predates the E1 lane split; not a regression this branch introduced.
…nd worker's own lane board-answer.sh fell back to a hardcoded "in-progress" whenever a ticket's needs-human park carried no pre-park: meta — which happens whenever the park was reached via needs-info, interactive-preferred, or deferred (none are PRE_PARK keys). If the bound session was an Architect, it resumed into in-progress, a state its protocol has no legal exit from (LEGAL["in-progress"] has no ready-for-implementer edge) — its only moves became parks or a re-escalation. implement-dispatch.sh already knows each worker's role (ARCHITECT/ IMPLEMENT/SPIKE) at spawn time but never persisted it; it now writes `role` into the daemon's registry meta right after a successful bind (same read-modify-write-under-lock pattern board-bind.sh/board-sweep.sh use; non-fatal on write failure). board-answer.sh's fallback reads it back: in-design when the bound worker is an Architect, in-progress otherwise — which is also the safe default for any worker bound before this fix (no role in its meta), matching prior behavior exactly for every case it was already correct for.
derive_state maps a lone non-open-state label to CONFLICT, and the status:blocked special case (v8) was never extended to the v9 retirement of status:ready-for-agent — a ticket still carrying it printed "open with 1 status:* labels: ready-for-agent" under a rule that means two or more. Same treatment as blocked: a named branch with a FIX line naming the v9 migration path (ready-for-implementer, or ready-for-architect per the birth rule for design-heavy work).
…GAL["deferred"] reverted The prior fix asserted `to in LEGAL[pstate]` inside pull_epics, and to satisfy that for a deferred epic, added in-progress to LEGAL["deferred"]. That was the wrong invariant: LEGAL is what board-transition.sh enforces for a WORKER or human's own transitions, so the addition silently legalized skipping a deferred ticket's queue and gate entirely by hand — exactly what these lane states exist to prevent, and nobody asked for it. Reverted LEGAL["deferred"] to its pre-branch membership (confirmed via `git diff ba55012` that no other LEGAL entry was touched). pull_epics is the board's own bookkeeping on an epic — never dispatched, never gated — the same latitude close_epics already has for its epic-closing writes; it does not need to answer to worker-transition legality at all. The real invariant is structural: a pull only ever lands on an in-flight state, never a queue or park. The assertion now checks `to in ACTIVE` (the existing in-design/in-progress/in-review tuple) instead, with the docstring rewritten to explain the distinction and record why the LEGAL framing was wrong. Added a test confirming a deferred epic's active child still pulls it to in-progress (the board-bookkeeping path, unaffected by this correction) alongside one confirming an ordinary board-transition.sh call still refuses to move a plain deferred ticket to in-progress (LEGAL unchanged — no gate-skip for a worker/human). Mutation-verified: reverting the PRE_PARK routing reproduces the original Finding C bug (3 targeted assertions fail; the deferred-bookkeeping assertions correctly stay green, since that path was never broken). Separately, corrupting PRE_PARK to route ready-for-architect to a non-ACTIVE state (needs-info) trips the new assertion with a clear message — proving it is a real, load-bearing guard independent of LEGAL.
…t enough, and an escalation is only as real as its return path
Owner
Author
|
Version collision note: main independently released v7.30.0 (65841f1, simplicity-audit skill round) after this PR's release commit reserved the same number. Both sides change the manifest lines identically (7.29.0 → 7.30.0), so the eventual |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Epic E1 of
docs/doperpowers/specs/2026-07-30-implement-lane-split-design.md(v1.3.2). Ships as v7.30.0.One implement worker used to carry a ticket from design judgment through execution — it ran the gate, authored its own ExecPlan when the work needed one, and drove to a PR, all on one model route. That couples two jobs with opposite model economics: a bad plan is the most expensive artifact in the pipeline, so plan authorship wants the frontier model, while executing a finished plan is middle-class work.
This splits the single dispatch queue into two lanes and encodes the relay in board states, so the dispatcher stays judgment-free.
What moved
The state machine (
skills/issue-tracker/scripts/_board.py,board-transition.sh,board-register.sh,board-answer.sh,board-sweep.sh,board-reconcile.sh,board-lint.sh)ready-for-agentis deleted, not aliased. In its place:ready-for-architect(dispatchable to design),in-design(the Architect's in-flight state),ready-for-implementer(dispatchable to execution, and the default birth state — unsure routes here).plan:meta pin, written by--planon the Architect's handoff edge only. Two values with genuinely different meanings:<repo-path>@<40-hex-sha>is an authorization event (the Implementer runs PLAN-EXECUTION — no intake gate, no gate comment, no re-litigating the design), while the literalpre-specis a plan-need ruling (the ticket still runs DIRECT and still posts a real[gate] pass). Conflating the two caused two defects during implementation; every rule that reads the pin now says which value it means.pre-park:meta so an answeredneeds-humanpark returns to the right in-flight state instead of a hardcoded one.needs-human.[board] <from> → <to>: <note>, and a second traversal of the same edge since the last[answers]comment converts mechanically to aneeds-humanpark. The comment log is the counter — no new bookkeeping.in-progress,in-design) go through the counted resume ladder; pre-verdict states (ready-for-architect,ready-for-implementer) are retired and freshly dispatched.Dispatch (
skills/implementing/scripts/implement-dispatch.sh)Role resolution order is SPIKE (by category, state-free — category outranks lane) → ARCHITECT (by state) → IMPLEMENT.
ARCHITECT_MAX_CONCURRENTdefaults to 1,IMPLEMENT_MAX_CONCURRENTto 5. The architect route hard-setsengine="claude"and pins${ARCHITECT_MODEL:-fable}, soengine:*labels andWORKER_ENGINEare never read on that route — plan authorship is not label-routed. X4 semantics are unchanged for every other role.Protocols
skills/architecting/SKILL.md— the Architect worker. Ends at the plan: writes no implementation code, never reviews the Implementer's output. Carries the architect-lane gate variant (open design forks are this lane's work, not gate failures), council scaling by artifact shape, WIP banking at every park, the down-shortcircuit, and the decompose exit.skills/implementing-tickets/renamed toskills/implementing/, with a new Mode Selection section driven by theplan:pin.skills/reviewing-prs/SKILL.mdlearns plan-pin admissibility, the authorization anchor (a real-pin ticket has no[gate] pass; the anchor is the Architect's handoff comment), and theready-for-architectimpasse route for seam-clustered blockers.skills/triaging-feedback/registrar birthsready-for-implementer; its conservative gate is unchanged and it adds no architect-lane birth.Validation Evidence
Suites — all green at HEAD:
tests/issue-tracker/test-board-scripts.sh(280 pass),test-board-sweep.sh,node test-board-template.cjs,tests/implementing/test-implement-dispatch.sh,test-protocol-content.sh, all fourtests/reviewing-prs/*.sh,skills/triaging-feedbackvitest (105 pass, 8 files),tests/claude-code/run-skill-tests.sh(3/3),scripts/lint-shell.sh. Both repo-wide residual-vocabulary greps (ready-for-agent,implementing-tickets) are empty outside the three sanctioned locations:docs/,board-migrate-gh.sh(it migrates the label, so it must name it), and the frozentests/review-bench/results/.The spec's eight acceptance criteria, each checked against the code and protocol text rather than against the implementation reports:
plan:-pinned dispatch spawns an Opus worker that authors no plan document and posts no gate commentready-for-architect, notneeds-humanin-progress → ready-for-architectwith note, summary, and WIPready-for-implementerneeds-humanin-designand completes legally"Partially verified" is drawn on one consistent line: the dispatcher's routing (role, model, protocol file) and the protocol text a worker reads are proven statically; a live LLM worker's actual conformance to that text is not, and no test here substitutes for the first live architect dispatch.
Three gaps the acceptance pass found, all closed on the branch: the
in-progress → ready-for-architectedge had no test at all (added, with three mutation proofs — note requirement, legality-table membership, convergence-edge membership); the five new review-loop rules were unpinned while the equivalent implementing and architecting text had assertions from the start (seven anchors added, each verified absent from the pre-branch file); and the convergence park note promised "both positions" while carrying one (reworded to carry this traversal's position plus a pointer to the comment trail).Whole-branch review. Codex was unavailable (account usage limit), so this went to a fable reviewer with the full branch package. It returned one merge blocker and three Importants; all are fixed, and the reviewer re-verified the fix range and revised its verdict to ready.
needs-human → in-reviewpre-park return was unusable.board-transition.shrequired--pron every entry intoin-review, andboard-answer.shreturned with a note only, so answering any review-loop park died with "a PR link is required", leaving the ticket stranded and the bound session unresumed. Worse on the unattended path: the sweep stamps its at-most-once relay guard before the failing call, so the human's answer was lost permanently and silently. This was a regression this branch introduced — at the merge base the return was hardcoded toin-progress, which has no PR gate. Fixed by letting the gate accept the ticket's already-recordedpr:meta; first entry and the repair path still refuse, verified by probe.--postedposted no[answers]marker. A human-authorized re-traversal therefore re-converted toneeds-human: a livelock of exactly the bounce the human had just sanctioned.--postednow posts its own marker.board-reconcile.sh's orphan check saw onlyin-progress, so a dead Architect'sin-designwork orphaned invisibly.skills/implementing/references/operation-manual.mdstill documented the retired self-authoring mode, contradicting the shipped protocol, in the file bothimplementingandarchitectingname as the operator entry point.Both regressions were reproduced against the mock harness before the fix and re-run after, and each carries a regression test whose baseline assertion fails under the pre-fix code.
Both lanes pin their model (resolved during review)
The whole-branch review flagged that only half this split's economic argument was enforced in code. The architect route hard-pins
${ARCHITECT_MODEL:-fable}, but the plain-Claude implement route inherited the operator's session model — the posture #35 set when it flipped that route off the clodex gateway. An operator running the frontier model by default would therefore pay frontier rates on both lanes and silently re-fuse the two economies this split exists to separate, and acceptance criterion 2's "spawns an Opus worker" would be a convention rather than a property of the dispatcher.Resolved in favor of symmetry: the implement and spike routes now pin
${IMPLEMENT_MODEL:-opus}on the plain-Claude route.IMPLEMENT_MODELstill overrides, and the codex route's ownfablegateway-alias default is untouched. The test that had pinned the inherit behavior now pins the new one, and reverting the one-line change fails exactly that assertion and no other (verified by mutation). Spec revision v1.3.3.Independent code review — seven findings, all fixed
A second independent review ran after the branch was first opened and found seven defects the whole-branch pass had missed. Each was verified against the code before being acted on; all seven are fixed, with tests whose new assertions were proven to bind by mutation.
The escalation stranded the PR permanently (HIGH). When the review worker routed a seam-clustered impasse to
ready-for-architect, nothing could ever re-attach a reviewer: the sweep read the finished reviewer's meta and skipped forever,board-answer.shrelaysneeds-humanparks only, and the workflow fires on PR events the Implementer's later pushes never produce. So once the Architect re-cut the design and a new Implementer finished, the PR sat open and unreviewed until a human noticed. This was a regression this branch introduced — the pre-branch impasse went toneeds-human, which resumed correctly.Fixed in the dispatcher rather than the worker: a reviewer bound to a ticket that has left
in-reviewis stale by definition, so the sweep retires it (never an active one) and skips, and the ticket's eventual return lands on the pre-existingretired → dispatchpath. The first attempt had the worker retire itself; that was rejected becausedaemon-retire.shstops the session before writing the retired status, so the write would very likely never land — the fix would have failed silently in the one case it exists for. The same check also generalizes past the architect lane and closes the wasted spawn-and-retire churn the park guard was written to prevent.A superseded
plan:pin survived every escalation, with no way to clear it (MEDIUM). The pin authorizes gate-free PLAN-EXECUTION, but nothing ever cleared it — so after an escalation and re-cut, an Implementer could enter with no gate against a plan the Architect had declared blocked. Two prose sites told operators to "strip a staleplan:pin", which no flag could do. The pin now clears automatically on entry toready-for-architect(the plan is being re-cut by definition) and on the Architect's own no---planexit edge, and deliberately does NOT clear when a human unparks a ticket back to the queue. Both prose sites now describe the mechanism that exists.A spike born into the architect lane had no legal move (MEDIUM). Role resolution is state-free for spikes, but the spike protocol's gate-pass writes
in-progress, andLEGAL["ready-for-architect"]has no such edge — so the worker's only remaining moves were parks, while its bound meta consumed the single architect slot. Closed at the source: the combination is rejected at registration and at every transition intoready-for-architectfor a spike-category ticket. Adding the edge toLEGALwas rejected — it would also legalize an Architect skipping the design phase.Three more, lower severity.
pull_epicswrote an edge the legality table forbids, bypassing the checkboard-transition.shperforms; it now routes through thePRE_PARKqueue-to-in-flight mapping and asserts its target is an in-flight state. An Architect resumed from a park with no recordedpre-park:landed inin-progress, from which its mandated handoff edge does not exist; the fallback is now lane-aware, using the worker role persisted at dispatch. And a ticket still carrying the retired label linted asopen with 1 status:* labels— a count of 1 under a rule meaning "two or more" — which is exactly what the five unmigrated tickets would have produced; it now gets a named branch and a migration FIX line, matching how the v8 retirement ofblockedwas handled.One correction was made during this wave and is worth recording: the
pull_epicsfix initially addedin-progresstoLEGAL["deferred"]to satisfy its own assertion. That edge was illegal at the merge base, andLEGALis whatboard-transition.shenforces, so it would have let any worker or human move a deferred ticket straight into progress, skipping the queue and the gate. Reverted; the assertion now expresses the real invariant (an epic pull is board bookkeeping and only ever lands on an in-flight state) without borrowing worker-transition legality. NoLEGALentry on this branch now differs from the merge base except by the lane rename itself.Still open, deliberately
The live board is not migrated by this PR. Five open tickets still carry
status:ready-for-agent(#40, #39, #38, #37, #32), which the new tables read as a conflict until each is repaired. The migration and the label deletion run after merge, along with two follow-up registrations (the QAgent pin riding C5, and the ida-solution consumer migration).