Uniform worker dispatch — landed evidence

One instruction dispatched six nodes across three backends — two external coding harnesses and the calling harness itself — with no change to the calling instruction and no harness named anywhere an agent reads. Eleven declared measures produced evidence. Two defects were found by the live runs and fixed; one deliberately constructed measurement did not fire, and is recorded as such.

Declared measures and what each produced

MeasureEvidenceVerdict
End-to-end dispatchThree nodes landed commits in detached worktrees, each git show --stat showing only its assigned path — § dispatchpass
UniformityOne node definition, two backends, identical instruction but for the backend override; both reached the same measured result — § uniformitypass
Task contract enforcedLive rejection naming five failing properties at exit 2; 18 tests over the seven properties — § contractpass
Escape hatch round-tripAn unprompted four-field report, answered and resumed in the same session, whose resumed turn restated its prior measurements without re-running anything — § escape hatchpass
Durable delivery6 of 6 manifests on disk; the recovery ladder run in order on a real silent run, redispatch never the first response — § deliverypass
In-harness branchA directive carrying worktree, manifest path and four fences, bound to a real harness task by attach§ in-harnesspass
Both summariesDispatch and completion summaries captured verbatim, four axes each, completion WHY quantitative — § summariespass
Continuation at three altitudes12 tests including one end-to-end trace from worker manifest to sprint rollup; the plan altitude is refused at the write boundary — § continuationpass
Budget signal capturedOne backend recorded utilisation 1.02% and a reset time; the other legitimately unknown with tokens only. A test proves absence never reads as exhaustion — § budgetpass
No routing leakageThree hits, all in a documented legacy-tier reader; a test now guards the property — § leakagepass, one documented exemption
Suite and validators green1,598 tests pass, 103 of them new; lint clean; reckon doctor all checks passed — § suitepass

One call, six nodes, three backends

Every node went out through reckon crew dispatch. The call names no harness, model, effort level or sandbox flag; those resolve from flight config inside reckon. The caller branches once, on the returned launch kind.

NodeBackendLaunchOutcomeScoped diff
flight-check-aexternal Aclicomplete after one escape-hatch round-tripmanifest only
flight-check-bexternal Bclicomplete first passmanifest only
docs-readmeexternal Aclicommit 8a44624README.md only
docs-agentsexternal Aclicommit 0ae4167AGENTS.md only
scope-fenceexternal Bclicommit d3a7617worker-protocol.md only
suite-nativecalling harnessin-harnesscomplete, task bbgq1cubamanifest only

All three commits were audited with git show --stat before integration, each touching exactly its declared write path, and merged sequentially with normal merge commits. Six worktrees were then removed by the conservative cleanup — clean and reachable from the integrated primary branch — leaving git worktree list with the main checkout alone.

The three implementation nodes did real, previously missing work: the crew surface is now documented in the repository README, in the repository agent guidelines, and — as the live run record — in the worker protocol reference.

The same node, two backends, one instruction

Both invocations are reproduced in full. They differ in exactly two places: the node identity, which two concurrent runs must not share, and the backend override.

reckon crew dispatch --project reckon --plan uniform-worker-dispatch --section "§3" \
  --role implement --node flight-check-a \
  --goal "record the resolved worker-routing report for this checkout" \
  --done-when "the manifest records the resolved default backend, the count of \
attributed provenance keys, and the exit status of the reckon flight command" \
  --write-path <scratch>/flight-check-a-manifest.md \
  --manifest    <scratch>/flight-check-a-manifest.md \
  --time-budget 15m --session uwd1 --set default_backend=A

reckon crew dispatch --project reckon --plan uniform-worker-dispatch --section "§3" \
  --role implement --node flight-check-b \
  ... identical ...
  --time-budget 15m --session uwd1 --set default_backend=B

Reckon translated those into two entirely different argument vectors — different subcommand, different stream flag, different model and effort flags, different bypass flag — without the caller expressing any of it. The two workers then reached the same measured result independently:

                        backend A        backend B
resolved default        codex            codex
provenance keys         36               36
attribution split       host=27          host=27
                        shipped=9        (host supplies backend tuning,
                                          shipped supplies fences)
flight exit status      0                0

That agreement is worth more than the dispatch succeeding twice: two independent harnesses, given the same fenced node and no shared context, produced the same number for a measurement neither was told the answer to.

A malformed node is refused before a worktree exists

The seven properties are checked before dispatch, so the escape hatch handles only the genuine residual. A live rejection, exit code 2, every failing property named in one pass so the node can be reshaped in one edit:

$ reckon crew dispatch --node bad \
    --goal "add the parser and wire the CLI" \
    --done-when "the code is clean" ... --dry-run

single-goal              the goal joins deliverables with 'and'; split it into
                         one node per deliverable
demonstrable             done-when rests on the subjective term(s) clean; name
                         what would be observed
demonstrable             done-when emits no evidence; name a test, a command
                         output or a numeric result against a stated bound
scoped                   no exclusive write path is enumerated
independently-verifiable  no manifest path is named, so completion could only be
                         judged by reading the implementation
                                                                      [exit 2]

Exit codes are branchable: 0 succeeded, 1 the configuration or request is wrong, 2 the node is not dispatchable. --dry-run runs the same resolution and validation as a real dispatch — one code path, not two — so a whole wave can be checked before any of it goes out.

One property earned its keep during implementation. Independently verifiable began as a tautology — dispatch always fills in a manifest path, so asserting one exists proved nothing. It now also requires that path to be absolute, which is a real failure mode: a relative path resolves against the worker's own worktree, so the orchestrator looks where the file will never be, reads a delivered node as silent, and redispatches work that already succeeded.

18 tests cover the seven properties, including the three the measure names — two goals, a subjective done-when, a missing write scope — and each asserts the rejection names its property.

The escape hatch fired on its own, and the round-trip closed

The measure asked for a worker deliberately driven to a NEEDS-HELP: report. One fired without being driven at all, which is stronger evidence: node flight-check-a finished its measurement, found that uv had created an ignored environment outside its exclusive write fence, and refused to remove it unilaterally.

orchestrator worker dispatch NEEDS-HELP advice complete fences: scope · time · evidence · delivery tried · options · leaning · cost-if-wrong resume — same session 019ff524…a1b093a3
The round trip. The worker stops rather than exceeding its fence; the answer returns into the same session, which is why the resumed turn still knows what was tried. A fresh session would have had to rebuild that context — and would likely have repeated the attempt that failed.

All four fields were present, unprompted:

NEEDS-HELP: the flight check passed, but uv created an ignored .venv outside
            the write fence and two safe cleanup attempts could not remove it
tried:         Ran the flight check once; it exited 0 and resolved codex with 36
               attributed provenance keys. Cleanup with an exact validated
               removal was rejected by the command safety layer before
               execution; gio trash failed — unsupported on this mount.
options:       (1) the orchestrator removes the exact worktree-local .venv;
               (2) grant this node an explicit cleanup scope and a permitted
               exact-directory removal mechanism; or (3) retain the ignored
               environment as an acknowledged cache artifact.
leaning:       Option 1, because the orchestrator owns worktree cleanup and can
               remove the exact disposable environment without expanding this
               node's write scope.
cost-if-wrong: Retaining it leaves an ignored 103-package environment in the
               worktree; removing it only means a later uv command must
               recreate the environment.

That is a decision brief, not a plea: the orchestrator answered it in one turn, accepting the worker's own leaning. The advice was resumed into the same session, and the resumed turn demonstrably retained prior context — asked to restate its measurements from memory without re-running anything, it returned:

I still have the measured result: 36 attributed provenance keys, split
host=27 and shipped=9. I'll update only the assigned manifest — without
re-running the flight command or touching .venv.

Those numbers match its first turn exactly, and the manifest was rewritten to status: complete with the report block removed. The orchestrator then honoured its half of the bargain and removed the 144 MB disposable environment itself.

Negative result: the constructed trigger did not fire

A second node, scope-fence, was constructed to force the same trigger — its goal pointed at the maintainer notes while its write scope granted only the worker protocol. It did not ask for help. It documented the run record inside its granted scope and finished clean, which is a defensible reading of a contradictory instruction rather than a failure.

The honest conclusion is that a deliberately contrived contradiction is a poor way to exercise the hatch, because a capable worker resolves it instead. The organic case is the real evidence: the hatch fires when a worker meets a wall it genuinely cannot pass inside its fence. Its commit was reviewed on merit, accepted, and merged — with one repair, since its section renumbering left a cross-reference pointing at itself.

Durable delivery, and the recovery ladder used in anger

Six of six workers wrote their manifest to the orchestrator-named path. Two facts from the run matter more than that count.

A manifest existed before the worker was done. flight-check-a reported manifest_present=true while its phase was still working — the exact situation in which reading the message channel alone would have produced nothing and redispatch would have duplicated a running node holding write scope.

The ladder found a real defect instead of burning a worker slot. After the first resume, observe reported the run as neither complete nor working. Following the ladder — check the manifest, then the on-disk evidence, then the logs — pointed at an empty event log and a stderr file containing error: unexpected argument '-C' found. Redispatching on the idle signal would have hidden that. Both consequences were fixed:

DefectWhy the fixtures could not catch itFix
The resume invocation was rejected outright: the working directory, model, effort and output options belong to the exec command, not to its resume subcommand, which takes only a session id and a prompt. Recorded event streams verify interpretation, never acceptance. A stream fixture cannot tell you the harness refuses an argument. Options reordered ahead of the subcommand, verified live, and pinned by a test that asserts the ordering.
A dead process with an empty log reported as starting, so a launch that never began looked like one about to. The orphan rule only covered a log with events in it. An empty log plus a dead process is now orphaned, and the phase names the stderr log to read.

Both were found by running the thing for real, which is the argument for dog-fooding rather than rehearsing.

The branch reckon cannot spawn

An in-harness backend is the calling harness's own delegation primitive, which reckon cannot start on its behalf. So dispatch prepared everything else and returned a directive:

launch      in-harness          (pid: none — nothing was spawned)
worktree    …/uwd1/suite-native  detached, cut from the integrated base
fences      scope     …/suite-native-manifest.md
            time      15m
            evidence  the manifest records the suite pass count plus the exit status
            delivery  …/suite-native-manifest.md
attach_with reckon crew attach --run <run-id> --task <task-id>

The harness then dispatched its own task against that directive, and reckon crew attach --run … --task bbgq1cuba bound the real task identity to the prepared record. The task ran the suite in the prepared worktree — 1,596 passed at the integrated base — and wrote the manifest to the named path; observe read the record back as complete with manifest_present=true, on the same surface as a spawned run.

A second attach was refused: a second binding would hide which worker holds the write scope.

One limit stated plainly rather than glossed: the bound task was a harness background task, not a harness subagent, because subagent dispatch was withheld in this session. The binding mechanism is identical — an opaque task identifier the harness owns — but the record should not be read as having exercised subagent delegation.

Both summaries, verbatim

Captured as emitted. Four axes each, at most two lines per axis, restating nothing the plan already says.

Dispatching wave 1 — 6 nodes
WHAT   routing report ×2 (uniformity pair) · README + guidelines + run-record docs
       · suite run via the calling harness
WHY    the pair measures backend-independence of one instruction; the three doc
       nodes are real undocumented surface; the suite node exercises the branch
       reckon cannot spawn
HOW    two external harnesses plus the calling harness, detached worktrees cut
       from a927c9c, exclusive scopes below, manifests on named absolute paths
WHEN   15–20 min each; the end-to-end gate closes the wave — evidence stays shut
       until a commit lands scoped clean with its manifest on disk

Wave 1 complete — 6/6 delivered, all eleven gates banked
WHAT   3 commits (8a44624, 0ae4167, d3a7617) + 3 manifest-only nodes
WHY    gate evidence: 2 backends reached the same 36 provenance keys from one
       instruction; 1,598 tests pass (103 new); 6/6 manifests on disk; budget
       read utilisation 1.02% on one backend and unknown on the other
HOW    every diff scoped clean on git show --stat; 6 worktrees removed by
       conservative cleanup; 2 live defects found and fixed
WHEN   next the run ledger, which promotes these pointers into committed
       history — ready, nothing blocks it

The binding discipline held: the completion WHY carries the gate evidence quantitatively. A validator enforces it, refusing a completion summary whose WHY contains no number — which makes a wave that cannot state its measure visibly incomplete rather than plausibly done.

Continuation closes at three altitudes

A chain closing only at plan level leaves the other two ends dangling. All three now close, and each is enforced rather than advised.

sprint close plan landing worker reports the sprints it feeds — derived from the graph names the next invocation, or records that the chain ends returns what it found but was fenced out of roadmap → feeds_sprints refused at the write boundary manifest → follow_ons
Each altitude answers a different question — what did I find, what comes next, what does this let us start — and each has one mechanism. The middle one is the only one that can be refused, because it is the only one written through a validated boundary.
AltitudeMechanismEnforcement
Worker The manifest's follow_ons field becomes plan followup append ops carrying the canonical one-line invocation. Tested: a manifest's candidate reaches a plan followup with the right prompt.
Plan landing A writeback that resolves a followup or sets a terminal status must leave an open followup or record in words that the chain closes. Refused at the write boundary, with a message naming the fix. Nothing is written on refusal.
Sprint close feeds_sprints and unblocks per sprint row, derived from the dependency graph. Tested, including that a same-sprint dependency is not downstream work and a cross-project edge is not claimed locally.

The sprint altitude reads correctly against this repository's own graph on its first run: S5 feeds S6, S7 feeds S8, S8 feeds S9 — matching the delivery shape the architecture reference describes, without anyone writing it down.

The rule had to cover both write paths, and its scope was measured

The first implementation validated the ops writer only. A live probe found the gap immediately: a plain HTTP patch marked a plan landed without going near that code. A rule one curl can walk around is a courtesy, not a rule, so both paths now share one predicate and the server answers 400 no_continuation — verified live, with the plan's version and status unchanged afterwards.

Its scope was chosen by measurement rather than by taste. A state-level invariant — "a terminal plan must carry a continuation" — is the tidier rule and would have covered every path at once, so the portfolio was counted first: 155 of 202 terminal plans across the mounted projects carry no continuation, and all 155 would have become unwritable. The rule is therefore keyed to the write: only a new landing owes an answer, and recorded history stays editable. A test pins that, so the tidier rule cannot be adopted later without someone confronting the number.

Enforcing the plan altitude changed behaviour, and five existing tests had to be retargeted: each set a terminal status incidentally while testing something else — scalar mechanics, op ordering, worktree routing — and now use a non-landing value or carry the chain. One test was given a second open followup so its resolve remains a landing that still names what comes next.

Budget, captured asymmetrically and honestly

The two external backends disagree about what they report, and the record does not pretend otherwise. Both rows are real, from this wave:

Backend ABackend B
headroomunknownknown
utilisation_pct1.02
resets_at2026-09-01T00:00:00Z
threshold_statusallowed_warning
tokensinput 1,047,173 · cached 914,944 · output 6,785 · reasoning 2,690cache read 741,450 · created 59,283 · output 2,820
cost_usd0.620
detailbackend reports token usage but no headroombackend reports utilisation and reset time

Backend A's row is the one that matters. It shows a million input tokens and no headroom at all — and a caller reading fields directly could read that as a spent budget. So budget_exhausted() answers True, False or None, and returns None for every unknown case. A test asserts it over three real fixtures plus the empty and partial blocks: absence is never exhaustion. Acting on the signal belongs to later work; this plan only guarantees the history exists.

No routing leakage, with one exemption stated

A grep for concrete model identifiers and provider names over skills/ and reckon/, excluding the translation module, returns three hits:

reckon/capability.py:33:    "haiku": "routine",
reckon/capability.py:34:    "sonnet": "general",
reckon/capability.py:35:    "opus": "orchestrator",

These are a legacy-tier reader: a compatibility map that translates identifiers found in plan state written before capability requests existed, documented in place as never emitted and never used to select a worker. Repository policy admits legacy tier metadata as compatibility input, so this is an exemption rather than a failure — but it is an exemption, not a clean sweep, and it is recorded as such. Nothing added by this work appears.

A one-off grep decays, so the property is now a test: it walks both trees, exempts the translation module and the legacy map by path, and fails with file and line on anything else. The two new agent-facing references name no harness, no command and no model — including the maintainer note, which describes launch mechanics as "one harness … another harness" throughout.

Where the implementation departs from the design sketch

SketchImplementedWhy
dispatch and attach as the CLI surface Also observe, resume, list and stop The declared measures are unreachable without them: observe is what folds a stream, manifest presence and liveness into the run record, and resume is the same-session round trip the escape hatch depends on. stop is a safety counterpart — a dispatch mechanism with no way to stop a runaway worker holding write access is not one to ship. Promotion into a committed ledger, and the read-side MCP view, remain out of scope.
A dialect keyed to a backend A dialect keyed to the backend's command Backend names are free-form user data — a config may call a backend fast — while the command is the executable whose flags must be spoken. An unknown command raises and lists what can be translated; there is no guessing fallback.
Workers deliver a manifest Manifest paths must be absolute A relative path resolves against the worker's worktree, so the orchestrator reads a delivered node as silent and redispatches successful work — the very failure the delivery fence exists to remove.

Two limits found while running it, for whoever picks this up

The read-only tier cannot write its own manifest on one backend. The flight schema describes the tier as "no writes beyond the worker's own manifest file", but one harness's restrained mode blocks all writes, manifest included. Every node in this wave therefore ran at the worktree-bounded full-access tier — the plan's locked default — and the read-only tier's delivery channel remains unresolved. It is not a blocker for dispatch; it is a real gap for review and investigation roles.

Concurrency is configured but not enforced. concurrency resolves per backend, and nothing in dispatch queues against it — the wave stayed inside the cap because the orchestrator counted, not because reckon refused. Scheduling belongs to the work that reasons about budget and in-flight state; until then the cap is advice to the caller.

Suite

$ uv run pytest -q
1598 passed in 44.81s          (baseline before this work: 1495)

  tests/test_backends.py       29   translation + observation, per dialect,
                                    against streams recorded from live runs
  tests/test_crew.py           54   the seven properties, routing, dispatch
                                    atomicity, attach, observe, reports, reflex
  tests/test_continuation.py   12   all three altitudes + one end-to-end trace
  tests/test_skill_contracts.py +8  uniform instruction, checklist, gate-fence
                                    sole authorship, reflex, no routing leakage

$ uv run ruff check reckon/ tests/     All checks passed!
$ uv run reckon doctor                 All checks passed.
$ uv run pytest -q  (in a detached worktree, via the in-harness node)
1596 passed

103 new tests, no failures, no skips. The two suite counts differ by the two tests added after the in-harness node ran.