§1 — Why this exists

The gating reflex being hardened here was developed in imas-ambix, where a plan declares an evidence-gate table of measure against required evidence before the work starts, downstream work stays visibly closed when a gate fails, and negative results stay on the page. Today that reflex is prose: nothing but discipline enforces it, and nothing else can read it.

Making it first-class state repairs a measured defect at the same time. Reckon derives a plan's blocked state — blocking comes from unresolved dependencies and effective_status projects it over the persisted workflow status — but the projection is one-way. It can add blocked and never remove a stale one:

effective_status('blocked', [])        -> 'blocked'   # never clears
effective_status('active',  [{...}])   -> 'blocked'   # correctly derived
effective_status('active',  [])        -> 'active'

A plan authored plan-status="blocked" therefore reads blocked forever after every blocker clears. roadmap already detects this as orphaned-blocked-status — the system knows the state has rotted and files a report instead of resolving it.

The same defect class exists in sprint items, where it contradicts an explicit written contract. references/sprint-orchestration.md §8 states that item lifecycle status and implementation fraction are derived from plan HTML and must never be persisted in the sprint — yet index.json carries a persisted per-item status, and it is already stale: distributed-sprint-state reads pending while its plan is shipped. roadmap derives the correct rollup, so only consumers of the raw index are misled — which includes any agent reading project state directly.

Reviewing the surface for this defect class found four further instances, so this plan's scope is one idea applied six times: state that can be computed must not also be stored. Persisted blocked status, persisted sprint-item status, hand-set sprint activation guarded by a single-session invariant, plan runnability as an authoring default with no decay alarm, and staleness that is detected but never reaches the agent doing the work. The gate mechanism leads because a gate is a declared blocker, so it reuses the derivation the others are being moved onto.

§2 — Deliverables

The gate element. A <section data-reckon="gates"> whose <div class="r-gate"> children carry a stable id, the section they anchor to, the sections they gate, status, the measure, the required evidence, the verdict and an evidence link. Parser, schema, renderer and round-trip parity follow the pattern already established for decisions and followups — including the derived-status discipline that a gate's passed state is computed from its verdict rather than stored twice.

Write ops. edit_plan gains gate to declare one and pass/fail to record a verdict with evidence. A pass with gates.require_evidence in force and no recorded measure is rejected, not warned — a gate that can pass without evidence is not a gate.

Derivation wiring. An unpassed gate becomes a row in blocking, flowing through the existing effective_status path. A failed gate therefore renders downstream sections and plans blocked automatically, and passing it unblocks them automatically, with no status edit anywhere.

Blocked becomes derived-only. Remove blocked from the persistable status set and reject it on write, matching reckon's established reject-write-warn-doctor posture. Fix effective_status so a legacy persisted blocked resolves to its underlying open state before the projection is applied, which turns the one-way defect into a two-way derivation. Persisted sprint-item status goes the same way — derived from plan HTML on read, rejected on write.

Server rendering. The server renders gates on the plan page and surfaces a gate column in sprint view, so a closed gate is visible without reading prose. This touches only server and SPA files and conflicts with nothing.

Swap the skill's gate enforcement — do not re-author the rule. The gate-fence rule itself is owned by uniform-worker-dispatch, which authors it in reckon-ship/SKILL.md with enforcement by reading the plan's prose evidence-gate table. This plan changes only the mechanism: enforcement moves to computed gate state, and gates.enforce from flight config selects strict refusal or a warning. Two consequences bind. The rule's wording is not this plan's to rewrite. And because SKILL.md is a shared file, this slice is a separate node that must not be dispatched until the owning plan's changes are integrated — everything else here touches only reckon/ and the SPA and may run concurrently. Split the node at that file boundary rather than serialising the whole plan; the file-conflict edge belongs in the execution graph, which the sprint contract already requires for shared files.

The portfolio sweep. Once the pattern exists, sweep every mounted project: migrate each persisted plan-status="blocked" to a recorded blocker or gate, drop persisted sprint-item status, and repair the error-level wiring findings the portfolio scan reported — 82 findings of which 17 are error-level, 69 of them in imas-efit alone, including two dependency cycles and three inactive-hard-dependency errors. Twelve live plans still persist blocked and forty-six sprint items still persist a status. This is a review-and-cleanup job, not an implementation one: each repair is a plan-state edit through the owning skill, re-scanned after. A finding that needs a material decision is reported, not guessed. Cross-repository edits stay in their owning checkout.

Sprint activation becomes derived. Today a sprint's active status is hand-set and guarded by an invariant that raises when a second sprint is activated, enforced under a lock. That is a single-session assumption: with detached worktrees, per-node exclusive write scopes and independent orchestrators, concurrent sprints are the intended mode of operation, and the invariant forbids expressing it — a plan with no dependencies cannot run alongside another sprint's last item purely because of a presentational pointer. Drop the uniqueness constraint and derive the default focus as the earliest sprint in order holding incomplete items. The consuming views already degrade gracefully through fallback chains, so they need a default rather than a maintained one. Two consequences follow: the pointer can never be stale, and starting a sprint ceases to be an operation at all.

Plan runnability becomes derived plus alarmed. A plan is excluded from runnable work by its authoring status, which does one genuinely valuable job — separating written-down from authorised-to-act-on, which becomes safety-relevant once an orchestrator can fan out unattended workers. But it is an authoring default with no decay alarm, and it has rotted: of the eleven authored-but-unauthorised plans across the portfolio, one has sat sixty-four days, and five report no age at all because they carry no recorded modification date — the alarm falls silent on exactly the documents most likely to have decayed. Split the concept. Derive the well-formedness half — a plan is dispatchable when it declares at least one gate and carries an open followup, which lifts the node-level task-definition contract to plan level and is checkable rather than asserted. Keep the authorisation half as a deliberate act, but report it: an authored-but-unauthorised plan surfaces with its age, so a months-old draft with locked decisions cannot sit silent.

Staleness becomes visible to the agent doing the work. Detection already exists — reckon audit flags plans active beyond thirty days with incomplete implementation, research beyond sixty days that never closed, and completed plans with no implementation fraction — and plan-modified is server-stamped on every write. The gap is that none of it reaches an agent reading the plan, so a worker can begin on a plan idle for three months with no signal that its assumptions may have expired. Surface derived age and the staleness verdict on the plan read payload, using the thresholds the audit already defines rather than inventing second ones. This is advisory, never blocking: a stale plan is a prompt to re-read its assumptions, not a refusal to work.

Migrate this sprint's own gates. The seven plans of sprints S7 to S9 author their gates in ambix's prose form because the element did not yet exist. Converting them to the new element is this plan's first dogfood and proves the migration path on real documents.

§4 — Constraints

Legacy documents carrying persisted blocked or item status must still read successfully with a compatibility diagnostic. Rejection applies to writes only; a read-side hard failure would strand every unmigrated project in the portfolio.

The sweep never rebalances a sprint, retires a plan, or resolves a decision to make a finding disappear. It migrates state representation and repairs mis-wired relationships. A finding whose repair requires a material judgement is reported for the owning project.

§5 — The sweep, sized and stalled on a dispatch gap

The sweep was scoped against a live scan rather than the plan's original figures, and the inventory below is what remains. It is recorded here so the next session inherits the survey instead of repeating it.

RepositoryWorkState
reckon8 persisted sprint-item statuses in the project indexdispatched — the only part of the sweep whose edits land in the same repository as this plan
imas-efit11 documents persisting blocked, and 16 error-level wiring findings of which 8 are mechanical: research inputs wired as hard prerequisites across three plans, which the scan itself says to move to informsundispatchable
imas-ambix36 persisted sprint-item statuses in the project indexundispatchable
nova1 document persisting blocked, 2 persisted item statusesundispatchable, and separately not quiet — eight concurrent runs were writing that project's state
hanney1 research input wired as a hard prerequisiteundispatchable

The obstacle is not the work. reckon crew dispatch --repo <other> resolves the node's plan inside the repository it is dispatched into and refuses with plan-unavailable, so a node serving a plan in one repository cannot edit another — which is every part of this sweep but the first row. The fallback of aiming write paths at a foreign checkout is worse than the gap, because it puts a worker outside the worktree that bounds its blast radius. The capability belongs to the orchestration plan and is recorded there; the sweep-complete gate stays closed until it lands.

Of the sixteen error-level findings in imas-efit, only the eight non-executable-hard-dependency edges are in this plan's remit. The remaining eight — two dependency cycles, three inactive dependencies on superseded or archived plans, and three sprint-order inversions — need material judgement or a sprint rebalance, both of which §4 forbids the sweep from doing. They are residue to report to their owning project, not work to guess at.

§6 — Migrating an authored table has no op

Both the worker migrating the routing plans and this session's own edit of this document hit the same wall: edit_plan can declare a gate and record its verdict, but nothing removes the prose table those gates replace. Text mode refuses to touch data-reckon regions and the gate ops only append, so the migration step that the plan calls its dogfood is the one step with no sanctioned path — every migration so far has gone through raw HTML editing inside a worktree, announced as a bypass.

This is the plan's own mechanism, so it belongs here rather than in a followup. The gate ops need a companion that retires the authored prose a gate section supersedes, so migrating a document is a state write like every other.

§ Decisions

How are gates represented so the toolset and server enforce them?

Routing gates through the existing blocking derivation makes downstream blocking automatic in both directions with no new machinery, and unifies the fix for persisted blocked status. Followups conflate work-to-do with evidence-that-must-hold; prose decays exactly the way persisted blocked did.

How strict is the removal of persisted blocked and item status?

Matches reckon's existing reject-write-warn-doctor posture. A read-side hard failure would strand every unmigrated project across the portfolio until the sweep completed.

Should more than one sprint be allowed in flight at once?

A lock-guarded invariant currently raises when a second sprint is activated. The consuming views already fall back to the first sprint when no pointer resolves.

Worktree isolation and independent orchestrators make concurrent sprints the intended mode, and the dependency graph already answers what is runnable — the pointer is a weaker second answer to the same question, which loses whenever the two disagree. Deriving it removes the staleness class entirely and retires sprint activation as an operation.

Is the authoring-status gate on runnable work worth keeping?

Four of eight draft plans portfolio-wide are more than two months old; one holds three locked decisions and a resolved followup.

The gate does real work once an orchestrator can dispatch unattended, so deleting it trades a safety signal for convenience. But an unalarmed default decays, as the four rotted drafts demonstrate. Deriving the checkable half and alarming the deliberate half keeps the signal and removes the decay.

§ Followups

Make gates first-class and blocked derived-only, then sweep the portfolio

Land the gates element with parser, schema, renderer and ops; wire unpassed gates into the blocker derivation; make blocked and sprint-item status derived-only with reject-on-write; then sweep every mounted project for stale labels and error-level wiring findings, and migrate this sprint set's own prose gates.

/reckon-ship derived-gate-state

Waves 1-2 landed — commits cbeaa2e, 18b0113, 67d1cbd, 63cb5a6, 4554a75, bc0881d. Gates are now a real element: they round-trip byte-identically, PlanState carries them, the published schema validates a gates-bearing document and rejects a gate missing its measure, and passed is re-derived from verdict even when contradictory input supplies passed=true. Five of the six derived-state rungs have their first half: sprint focus derives from order and incompleteness with the uniqueness invariant removed (two concurrent incomplete sprints, zero audit findings); sprint-item status derives from plan HTML with reject-on-write and a read-side compatibility warning (a legacy pending/0.0 item reads active/0.4, index.json byte-unchanged); staleness reaches the agent as advisory age plus verdict from doccheck's existing thresholds (31-day fixture reads stale, effective_status active, zero blockers); and a closed gate renders in both plan and sprint views with audit-doc reporting no errors. Suite green at 1,683 after wave 2, up from 1,660 after wave 1. Remaining rungs move to f-dgs-002.

Wire gates into the blocker derivation, then finish blocked-unwritable and sweep the portfolio

The gate element exists and is typed, so the rungs that consume it are now unblocked. Write ops come first - edit_plan gains gate to declare one and pass/fail to record a verdict, with an evidence-free pass rejected rather than warned. Then the derivation: an unpassed gate becomes a row in blocking so a failed gate renders downstream sections blocked and passing it unblocks them, with no status edit anywhere.

Blocked-unwritable still needs its plan-status half - remove blocked from the persistable set, reject it on write, and fix effective_status so a legacy persisted blocked resolves to its underlying open state before the projection applies. The sprint-item half of that rung already landed. Plan runnability derived plus alarmed follows the same shape and depends on gates existing, which they now do.

Two rungs remain that are not code. The skill's gate enforcement swaps from reading prose to reading computed gate state - a SKILL.md edit whose owning plan has shipped, so the file-conflict fence is clear. And the portfolio sweep migrates every mounted project's persisted blocked status and repairs the error-level wiring findings, ending with this sprint set's own prose gates migrated to the element.

/reckon-ship derived-gate-state

Gates now gate — commits ca18aef, 54bdf1e. edit_plan declares a gate and records a verdict with evidence, refusing an evidence-free pass while require_evidence holds. A shared unpassed_gate_blockers derivation feeds all three consumers - typed read_plan through mcp_views._blocking, discovery read_plan through serve._derive_lifecycle, and roadmap independently, which reports gate_blockers, derives effective_status=blocked and drops the plan out of ready_now. One bidirectional test flips only the verdict from failed to passed and proves both read paths plus ready_now change in both directions, with persisted workflow status active throughout. Suite green at 1,699, up from 1,683. The derivation node first blocked on an insufficient fence, having changed nothing: read_plan builds blocking in two modules the coordinator had not scoped, recorded as f-uwd-scope-sufficiency. Remaining rungs move to f-dgs-003.

Finish blocked-unwritable and runnability, then swap the skill and sweep

Three rungs of code remain. Blocked-unwritable needs its plan-status half: remove blocked from the persistable status set, reject it on write, and fix effective_status so a legacy persisted blocked resolves to its underlying open state before the projection applies - turning the one-way defect two-way. Plan runnability derives the well-formedness half, a plan being dispatchable when it declares at least one gate and carries an open followup, while authorisation stays deliberate but is reported with its age so a months-old draft cannot sit silent.

Then two rungs of documents. The skill's gate fence swaps from reading a prose table to reading computed gate state, with gates.enforce selecting strict refusal or a warning; the rule's wording belongs to its owning plan and is not this plan's to rewrite. And the sweep migrates every mounted project's persisted blocked status and per-item status, repairs the error-level wiring findings, and ends by migrating this sprint set's own prose gates to the element - the dogfood that proves the migration path on real documents.

Sequencing note for whoever takes it: the own-gates migration edits plan HTML that concurrent orchestrator sessions are also writing followups into, so it needs either a quiet window or per-file coordination rather than a parallel fan-out.

/reckon-ship derived-gate-state

Both code rungs landed, then a failed gate caught what they broke — commits 382501e, e570a14, 7ae4fa3, 7770425. The opening defect is two-way at last: effective_status('blocked', []) returns active where it previously stayed blocked forever, while a live blocker still yields blocked, and a write persisting blocked is rejected naming the status with legacy documents still reading under a compatibility warning. Dispatchable and authorised are now separate derived signals, so a well-formed draft reads dispatchable=true, authorised=false, ready=false. The wave-four suite gate then FAILED at 1,706 passed with 3 roadmap readiness failures, and the failure was worth more than the tests: ready_now had emptied across all ten pending plans from two stacked causes - mcp._inventory_row stripped gates and followups before build_roadmap, so open_followup read missing on plans that plainly had one, and gate-based dispatchability stranded every not-yet-migrated document, which the locked legacy-read-posture decision forbids. Two corrective nodes fixed both in 27 and 8 lines; ready_now verified back to 6 against the live project rather than fixtures alone, and the re-run gate passed at 1,709. Two plan-content corrections belong with this: the runnability rung's cited evidence of four-of-eight drafts over two months is stale - the live scan reports 14 drafts across 12 projects, one over 60 days, several with no modification date reporting unknown age - and the sweep's gate must be restated against that. Remaining work moves to f-dgs-004.

Swap the skill's gate fence onto computed state, then migrate and sweep

Every code rung has landed, so the two document rungs are all that remain, and the machinery they depend on is now real rather than planned.

The skill's gate fence currently reads a prose table by discipline. It swaps to reading computed gate state, with gates.enforce selecting strict refusal or a warning. The rule's wording belongs to the plan that authored it and is not this plan's to rewrite - only the mechanism changes.

Then the migration and sweep. This sprint set's own prose gates become elements, which is the dogfood proving the path on real documents, and every mounted project has its persisted blocked status and per-item status migrated with the error-level wiring findings repaired. Two constraints bind. The migration edits plan HTML that concurrent orchestrator sessions are also writing followups into, so it needs a quiet window or per-file coordination rather than a parallel fan-out. And the sweep's own gate must be restated first: the plan cites four of eight drafts over two months, while the live portfolio reports 14 drafts across 12 projects with only one over 60 days and several carrying no modification date at all, so the gate as written cannot be met by a correct implementation.

A related adapter finding is already recorded and now fixed in part: rows reaching the public MCP roadmap keep their gate and followup state, but age-less drafts still report unknown rather than falling back to a creation or modification time, which the sweep will need if it is to classify decay rather than report silence.

/reckon-ship derived-gate-state

The fence swapped, five plans migrated, the decay alarm found its voice — commits 3984939, 0183e95, 8747f33, e855ffc, ea82ad9; suite green at 1,738, up 29 from 1,709, with 0 lint diagnostics. The fence now reads computed blocking and gate_blockers with gates.enforce resolved from flight config; the prose-table grep count is 0 and the rule's four normative bullets and refusal sentence are byte-identical, so only the mechanism changed. Sixty-one measures moved from prose tables into gate elements across five plans with none dropped, every evidence anchor resolving and audit-doc reporting no errors. draft-decay-alarmed PASSED after its own wording was corrected against a live scan: all 11 authored-but-unauthorised plans across 6 projects now report an age and its source, 6 from plan-modified and 5 from file modification time, 0 null ages, the 64-day plan verdicted stale. The alarm had been silent on 5 of those 11, which is the decay class the plan exists to remove. This project's index dropped its 8 persisted item statuses with the composed items byte-identical at 4,948 bytes. The dogfood turned the document against itself: with 4 of 13 gates unpassed this plan reads effective_status blocked, names the four gate ids and leaves ready_now, while its persisted status stays active. Two mechanisms were found missing and are now sections rather than handoffs, and the sweep stalled on the first of them: a node cannot be dispatched into a repository other than the one holding its plan, so four of the five sweep repositories are unreachable, and no op retires the prose a gate section supersedes, so every migration went through announced raw-HTML bypasses. Remaining work moves to f-dgs-005.

Retire the superseded prose, migrate the held plans, then sweep once a node can cross repositories

Four gates remain open and each is held by a named condition rather than by unfinished effort.

Start with §6, because it unblocks the rest. Declaring a gate appends; nothing retires the authored table those gates replace, so all six migrations so far ran through raw HTML inside a worktree with the bypass announced each time. Give the gate ops a companion that retires the prose a gates section supersedes, and migrating a document becomes a state write like any other.

Then own-gates-migrated: three plans of the sprint set still carry prose tables — effort-calibration, inflight-visibility and north-star-orientation. They were held, not skipped: concurrent orchestrator sessions were mid-landing on all three, writing followups into the same HTML, and racing them would conflict on the system's own state store. Check crew live before starting; the work itself is small.

sweep-complete is held on the cross-repository dispatch gap recorded as f-eoh-cross-repo-node on execution-orchestration-hardening. §5 carries the full inventory so the survey need not be repeated: 11 blocked documents and 8 mechanical dependency-kind repairs in imas-efit, 36 persisted item statuses in imas-ambix, 1 blocked document and 2 item statuses in nova, 1 dependency-kind repair in hanney. The other 8 imas-efit findings are residue to report, not repair.

wave-fence-honoured needs a real gate refusal, not a manufactured one. The two waves that did not open this session were held by the dispatch gap and by concurrency, neither of which is a gate. Record the transcript when a gate genuinely closes a wave.

/reckon-ship derived-gate-state

§ Evidence gates

Two-way derivation

A test asserting effective_status returns an open state for a legacy persisted blocked with no blockers, and blocked when a blocker exists. The current one-way behaviour is the seeded regression case.

Evidence

Gate blocks and unblocks

A failed gate renders its downstream sections blocked in read_plan and roadmap; passing it returns them to ready, with no status write in between. Both directions asserted in one test.

Evidence

Evidence-free pass rejected

A test proving a pass op with no recorded measure is refused while require_evidence is in force, and that the refusal names the gate.

Evidence

Blocked unwritable

Tests proving plan-status="blocked" and a persisted sprint-item status are both rejected on write, and that reading a legacy document carrying them still succeeds with a compatibility diagnostic.

Evidence

Round-trip parity

Gates survive the existing state round-trip invariant with byte-identity of the regenerated reckon-owned section, matching decisions and followups.

Evidence

Wave fence honoured

A recorded orchestrator transcript showing a wave refused while a gate is closed, and opened once it passes.

Concurrent sprints expressible

Two sprints hold incomplete items simultaneously with no error raised, the derived default focus resolves to the earliest of them, and a test proves the derivation cannot go stale because nothing persists it.

Evidence

Runnability derived

A plan declaring a gate and carrying an open followup reads runnable; one missing either does not, and the report names which property is absent. Authorisation remains a separate deliberate act.

Evidence

Draft decay alarmed

A portfolio scan lists every authored-but-unauthorised plan with its age and the source that age came from. All eleven currently-unauthorised plans appear across the six projects holding them, the one sixty-four days old is verdicted stale, and no row reports an unknown age.

Evidence

Staleness reaches the agent

A plan read carries derived age and a staleness verdict computed from the audit's existing thresholds, not new ones. A test proves the verdict is advisory and never blocks a read or a dispatch.

Evidence

Sweep complete

A portfolio re-scan showing zero orphaned-blocked-status findings, zero of the twelve live plans still persisting blocked and zero of the forty-six persisted sprint-item statuses across all mounted projects, and the seventeen error-level wiring findings reduced to a listed residue each carrying a stated reason it was not auto-repairable.

Own gates migrated

All eight plans of sprints S7 to S9 carry data-reckon="gates" elements in place of their prose tables and pass reckon audit-doc with no errors.

Suite green

Full reckon suite passes; new-test count recorded.

Evidence
gate-fence-computed-state landed — commit 3984939; gate suite-green passed; measure 30 contract tests pass with 1 new test asserting the fence reads computed blocking and gate_blockers through read_plan and roadmap plus the resolved gates.enforce, the prose-table grep count is 0, and the four normative bullets and the refusal sentence are byte-identical to the baseline; artifacts skills/reckon-ship/SKILL.md, tests/test_skill_contracts.py
gate-migration-ledger-plans landed — commit 8747f33; gate own-gates-migrated partial; measure 30 of 30 former prose rows migrated to r-gate elements across two plans, 22 and 8 respectively, all carrying data-verdict=passed with landed-evidence links, 0 prose gate tables remaining, audit-doc 0 errors, 40 tests pass; artifacts docs/plans/budget-aware-dispatch.html, docs/plans/crew-run-ledger.html
draft-age-fallback landed — commit 0183e95; gate draft-decay-alarmed advanced; measure the portfolio's eleven authorisation rows went from five reporting a null age and eleven carrying no source, to zero null ages and every row naming its source, six from plan-modified and five from file-mtime, with 36 tests passing of which 4 are new; artifacts reckon/roadmap.py, reckon/doccheck.py
gate-migration-routing-plans landed — commit e855ffc; gate own-gates-migrated advanced to five of eight plans; measure 18 of 18 routing-plan rows migrated, 7 and 11 respectively, all closed and passed with all 18 evidence anchors resolving, 0 prose tables left inside either gates section, audit-doc 0 errors, 40 tests pass; artifacts docs/plans/flight-control-config.html, docs/plans/uniform-worker-dispatch.html
This plan's own gates declared — 13 gate elements replacing the prose table, 9 passed against anchors in the new cumulative evidence record and 4 left open. The derivation was then verified on this document: roadmap reports effective_status blocked, readiness blocked, and gate_blockers naming wave-fence-honoured, draft-decay-alarmed, sweep-complete and own-gates-migrated, with the plan dropped out of ready_now and no workflow status written.
suite-verification landed — tested HEAD 13cb77d; gate suite-green passed; measure 1,738 passed and 0 failed in 48.68s, up 29 from 1,709, with ruff reporting 0 diagnostics, against an integrated tree from which all five wave commits are reachable; artifacts scratchpad log only, no repository paths changed
sprint-item-status-drop landed — commit ea82ad9; gate sweep-complete advanced for this project only; measure the project index went from 8 compatibility warnings to 0 with the composed sprint items byte-identical before and after at 4,948 bytes and a matching digest, proving only ignored fields were removed, and 51 project-state tests pass; artifacts docs/state/reckon/index.json, 8 deletions and no additions