Tier dispatch on the run ledger + fleet registry & profile verbs (fleet §3.2/§6.3/§9) - #213
Merged
Conversation
…run): profile resolve Fleet spec §6.3 Rev 5 + §2/§3.1; agentic-substrate plan Task 13. Tier dispatch RIDES the shipped run ledger instead of building a second store: `dispatch` is the 7th LedgerRecord kind, so validate-on-append covers it for free via the `ledger-record` schema's new stanza. No `amico telemetry` verb exists — that name is the solve-log classifier. - ledger.ts: DispatchRecord + the closed TASK_TYPES taxonomy (the sim/hw axis is carried there, so a lane-less `experiment` row cannot exist). - ledger_dispatch.ts: primary key `work_id x model x variant`, fallback key `task_type x model x variant` below K_MIN, first-attempt-only p_m(s), m*(s) = argmin c_m/p_m(s), escalation ladder with the same-model-id rung skip, and the four confidence bands IMPORTED from ledger_query (one rubric, not two). Simulated rows are admissible opt-in; ledger_query's source==="user" filter is untouched. THE fallback never crosses the sim/hw boundary: a sparse experiment-hw cell relaxes within hardware rows only or falls back to the ladder, so simulated pass-rates can never route real board time. - profile_verb.ts: `amico profile resolve` — schema validation ported from the profile lint, entitlement-filtered skills, the spool-up composition rule (a preset's base is ignored; a chat is always the resident shell), the verifiability rule, and the lossy Claude-Code tool-set preview with `device` excluded from the path-rw test. - ledger_verb.ts: `dispatch` subcommand; verbs.ts: `profile` registered. Tests: amico-run 374 -> 488 passing; @amicode/schema 92 -> 99 passing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
….2, §9 step 2)
Agentic-substrate plan Task 15. Lands the pure module + CLI surface that fleet
steps 4-5 consume, satisfying §8's CI requirement ("the §3.2 state machine as a
pure module with exhaustive transition tests; one-file-per-session TOML records
round-trip") without touching the fork or the extension.
DELIBERATE CONTRAST WITH THE LEDGER, kept: ledger.ts is an append-only immutable
JSONL event log; this registry is mutable per-session TOML state. They share only
what the Rev 4.1 delta review scoped — record I/O + the no-null conventions,
single-writer discipline, adoption/rescan, and the pid-liveness probe. The state
models are NOT forced through one function.
- fleet_registry.ts: the Rev 3 transition table as a total, side-effect-free
function over 6 states x 9 events, with the writer handoff (extension holds a
`spooling` record; the harness holds everything after `spooling --inject-->
running`), stop-from-any-live-state, the `blocked --unblock--> running` budget
re-arm as the ONE re-arming edge, `killed` terminal, and `respooled_to` stamped
only on a respool-kill (supplying it on any other edge is rejected). Records are
one TOML file per session at $AMICO_FLEET_DIR (default ~/.amico/ops/fleet),
written tmp+rename; no nulls anywhere, empty string / 0 for absent, and an
explicit null is a validation error. USER ACTIONS are mapped to events
separately (steerEventFor/stopEventFor/retierEventFor) because the same button
is a different edge in a different state: a stop during triage is `cancel`,
applied by the extension.
- fleet_verb.ts: `amico fleet list|status` read verbs over a derived view;
`steer|stop|re-tier` ENQUEUE SIGNAL FILES under <session_id>.signal.d/ and never
touch a record, which is what lets a CLI operate live sessions without becoming
a second writer; `sweep` is the one exception that writes and is pid-liveness
guarded — a live pid, an unknown pid (0), and a foreign-host record are never
marked crashed. Sweep applies its transition through the same pure machine, so
an orphaned `blocked` record is reported rather than laundered into a crash.
re-tier reuses the shipped escalation ladder for its direction test: upward is a
next-step-boundary stamp change, downward on a plan-walking session is a replan
costing one replan-budget unit, and a resident re-tier is a respool.
- verbs.ts: `fleet` registered alongside `profile`, so CLI dispatch, the MCP
facade, and --help come for free.
Found and fixed while testing: signal ordering. An epoch-ms-only filename prefix
ties for a same-millisecond burst and then sorts by ACTION NAME, silently
reordering steer -> re-tier -> stop into re-tier -> steer -> stop. Names now carry
a fixed-width per-directory sequence, with a regression test.
Tests: amico-run 488 -> 638 passing (113 registry incl. all 54 state x event cells
as individual cases, 37 verb); @amicode/schema 99 unchanged; typecheck clean.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`aggregateDispatch` builds its per-cell Map key as `${model}<NUL>${variant}` to
make a collision-proof composite of two free-form strings. That separator was
written as a RAW 0x00 byte in the source rather than the `\x00` escape, which
made grep/ripgrep classify ledger_dispatch.ts as binary and silently report
zero matches for every pattern in the file — including `laneOf`, in the module
where the sim/hw lane boundary is the load-bearing rule.
Git still diffed it as text (its binary sniff only covers the first 8000 bytes;
the NUL sat at 17679), so the corruption was invisible in review and visible
only to code search — the worse of the two failure modes, since it is agents
and future readers who search.
The escape is behaviourally identical: JS reads `\x00` in a template literal as
U+0000, and the key is write-only (values carry the structured {model, variant},
nothing splits on the separator). Verified: 0 raw NULs remain in the branch, and
`rg laneOf` now returns all 5 sites.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
aarontrowbridge
force-pushed
the
wip/dispatch-ledger
branch
from
July 25, 2026 20:21
52314a8 to
ba09f35
Compare
aarontrowbridge
marked this pull request as ready for review
July 25, 2026 20:21
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.
Phase B of the agentic-substrate program (
plan-20260724-133637), Tasks 13 + 15. #212 has merged, so this is now rebased ontomainand no longer stacked.What's here
dispatchledger stanza + tier-dispatch aggregator (ledger_dispatch.ts):amico ledger dispatch.amico profile resolve(profile_verb.ts) — resolves a capability profile to an effective{model, variant, permissions, gates}.amico fleetverbs (fleet_registry.ts,fleet_verb.ts) — fleet §9 step 2's third verb family.K_MIN/N_HIGH/IQR_TIGHT/IQR_WIDEandnumericStatare imported fromledger_query.ts, never re-declared.This work rides the run ledger rather than building a second store. An earlier revision of the design did specify a parallel
amico telemetry log|fit; that was caught and deleted in favour of a 7thdispatchledger kind.The load-bearing rule: sim and hw never pool
Simulated rows are admissible here (opt-in
--include-simulated) — unlikeledger_query.ts, whosesource === "user"filter is untouched — because tier dispatch asks a different question: can model $m$ author correct work of type $s$, which a fit-recovery gate against known ground truth does test. What it cannot test is robustness to real noise, drift, and unexpected resonances, so a simulated pass is necessary but not sufficient evidence of hardware competence.So both matchers are lane-scoped, not just the fallback. An experiment
work_idis the same string on a simulator and on a board ("t1-fit"either way), so an unscoped primary key would pool the two difficulty populations before the fallback was ever consulted — the same defect class asstructure_hash's goal-blindness that #212'se164855fixed. Asserted by the "the sim/hw boundary" block inledger_dispatch.test.ts.No transfer claim. Whether simulated pass-rate predicts hardware pass-rate is an open empirical question; nothing here assumes it.
Notes for the reviewer
Rebasing onto #212 produced one textual conflict (a usage string, both sides added a clause) and one auto-merge worth a second look:
ledger_query.ts, where #212 addedgoaltoprimaryMatch/fallbackMatchwhile this branch widens four constants andnumericStatto exports. Verified orthogonal — the export widening carries no semantic change, andgoalkeying is intact on both matchers.ba09f35is a self-contained fix for a raw NUL byte that madeledger_dispatch.tsinvisible togrep/rg. Git diffed it as text (its binary sniff covers only the first 8000 bytes; the NUL sat at 17679), so review could not have caught it — only code search could. See that commit message.Verification
Re-run after the rebase onto merged
main, because the tree genuinely changed — main's telemetry work and these ledger changes had not been exercised together before:amico-runschemaextensiontsc --noEmitOne caveat on reading a local run:
vault_verb.test.tsfails on a colddist/because tests that spawn the built binary race esbuild. Warm the bundle and it is 640/640. CI builds before testing, so CI is immune.🤖 Generated with Claude Code