Skip to content

feat(bin): record why every agent dispatch was necessary at the spawn chokepoint (land of upstream #1830) - #58

Open
sbracewell64 wants to merge 4 commits into
mainfrom
fm/land-cfvc-08-onto-fork
Open

feat(bin): record why every agent dispatch was necessary at the spawn chokepoint (land of upstream #1830)#58
sbracewell64 wants to merge 4 commits into
mainfrom
fm/land-cfvc-08-onto-fork

Conversation

@sbracewell64

Copy link
Copy Markdown
Owner

Lands CFVC-08, the reasoning-required spawn record, onto the fork trunk.

The contribution exists upstream as kunchenguid#1830 (head e17ecfe1). That branch was left untouched: this branch was cut fresh from the fork trunk (ed376cf) and carries only that contribution's own three commits, plus one fork-side test adaptation described below.

What changes

Every agent dispatch now records why an agent turn was necessary, so the question is answerable from the record rather than reconstructed.

  • bin/fm-reasoning-lib.sh (new): owns the closed nine-code reason vocabulary, the capability-floor vocabulary read from config/crew-dispatch.json, and the derivations for reasoning_required and escalation_policy.
  • bin/fm-spawn.sh: --reason-code is required for every ship and scout dispatch and refused on --secondmate. An out-of-enum value is refused rather than recorded. --capability-floor is validated against the vocabulary the dispatch config defines. reasoning_required, reason_code, capability_floor, escalation_policy, and tooling_gap_item land in state/<id>.meta.
  • TOOLING_GAP is the one code that is not a reasoning code: it is never counted as justified reasoning and requires --tooling-gap-item naming a currently open item in this home's backlog, so an unfixed tool cannot launder itself into a permanent "necessary agent turn". Its item match is literal, with ERE-injection and newline-injection hardening.
  • escalation_policy honours the delivery mode, including a distinct captain-approves-local-merge token, and bin/fm-promote.sh recomputes it on promotion so a promoted task never keeps a scout's report-only posture.
  • Docs: capability floors and the _floors vocabulary in docs/configuration.md and docs/examples/crew-dispatch.json; the dispatch axis in docs/architecture.md.
  • tests/fm-reasoning-required.test.sh (new, 629 lines): the contribution's own witnessed-red suite.

Conflict resolution against the current fork trunk

git merge-tree --write-tree fork/main HEAD exits 0 and its result tree equals this branch's own tree, so the branch carries only this contribution's changes and merges cleanly.

Six conflicts were resolved. Main won everywhere the contribution did not deliberately change:

  • AGENTS.md meta-line: kept the fork's line (slot_base/contribution_target/base_state, merge_verification, X-mode wording) and inserted only the agent-justification clause. The fork has no muse backend, so the upstream side's muse-session line was dropped. The cross-reference was corrected to section 7, which is where the fork's dispatch contract lives.
  • AGENTS.md dispatch section: kept both the fork's pool-slot refusal sentence and the contribution's new reason-code sentence.
  • bin/fm-spawn.sh (five hunks): all purely additive on both sides, so both were kept: the fork's --slot-base/--contribution-target axes and the contribution's justification axes now coexist in the header, the variable block, the parser, the batch forwarding, and the meta writer.
  • docs/examples/crew-dispatch.json: kept the fork's _scheduling admission-control block and added "floor": "F-GEN" to the two default profiles.
  • tests/fm-muse-harness.test.sh: deleted on the fork trunk, so it stays deleted; the contribution's mechanical edit to it does not apply.

Test evidence

All runs are on this branch's final head 529141d, against base ed376cf.

Changed-file selection, 70 suites: bin/fm-test-run.sh $(bin/fm-test-run.sh --list --changed --base fork/main)

FM_TEST_SUMMARY total=70 failed=6 skipped_gate=2 duration_ms=1257696

The contribution's own suite and every suite this landing touched are green:

tests/fm-reasoning-required.test.sh   exit=0   (10 cases)
tests/fm-task-base.test.sh            exit=0
tests/fm-launch-lib.test.sh           exit=0
tests/fm-model-zero-budget.test.sh    exit=0
tests/fm-backend.test.sh              exit=0
tests/fm-backend-herdr-launcher-workspace-e2e.test.sh  exit=0

Also green: bin/fm-lint.sh (ShellCheck 0.11.0 pinned), bin/fm-test-run.sh --check-coverage (total=134 parallel=24 serial=99 serial_shards=4 herdr=11), bin/fm-doc-audience-check.sh (surfaces=72 local_links=215). The lint pass was negative-controlled: an injected SC2086 violation made fm-lint.sh exit 1 before the file was restored, so the clean pass is not an empty-check artifact.

Failures, disclosed

Six suites failed. None is caused by this change, and each was proven on the unmodified base first.

Five fail identically on base ed376cf in this environment, before any of this branch's commits:

tests/fm-calm-pi-extension.test.sh     branch exit=1   base exit=1
tests/fm-busy-adapter-wiring.test.sh   branch exit=1   base exit=1
tests/fm-backend-tmux-smoke.test.sh    branch exit=1   base exit=1
tests/fm-launch.test.sh                branch exit=1   base exit=1
tests/fm-tmux-agent-liveness.test.sh   branch exit=1   base exit=1

The sixth, tests/fm-secondmate-harness.test.sh, is an artifact of running the suite from a checkout that sits on a feature branch: the worktree-tangle guard fires and its banner lands in the captured output the assertion reads. Control run, on the unmodified base clone with no code change at all, only checked out onto a feature branch:

BASE(on feature branch) tests/fm-secondmate-harness.test.sh exit=1
not ok - ... WORKTREE TANGLE - PRIMARY CHECKOUT IS ON A FEATURE BRANCH

On base at its normal head the same suite exits 0. CI checks out a merge ref rather than a feature branch, so this one is expected to pass there.

Fork-side test adaptation

Commit 529141d is not part of the upstream contribution. Making --reason-code required refused five suites' spawn call sites before they reached the behavior they pin: fm-task-base, fm-launch-lib, fm-model-zero-budget, fm-backend, and fm-backend-herdr-launcher-workspace-e2e. These are fork-only suites, or grew fork-only call sites after the contribution was cut upstream, so the contribution could not adapt them. Each now declares NL_RULE_CLASSIFICATION, matching how the contribution adapted every suite it could see. The Herdr launcher helper adds the flag conditionally, because a --secondmate spawn refuses --reason-code outright. No product behavior changes in that commit.

Venue check state

The fork's own CI has unrelated red on other landing PRs: #54's Behavior portable serial 1 shard failed with one secondmate and one watcher-wake-lock family failure, and its PR must be raised via no-mistakes gate fails by design for a direct-PR landing. If this PR shows the same pattern, that is the trunk's current state rather than a result of this change; the local evidence above is what this branch stands on.

… chokepoint

CFVC-08. bin/fm-spawn.sh is the last gate before an agent turn exists and it
already writes state/<id>.meta, so it is where the justification record belongs.
Every ship and scout dispatch now records four fields:

  reasoning_required  derived from the reason code
  reason_code         a closed nine-token enum; an unknown value is refused
  capability_floor    verbatim from config/crew-dispatch.json
  escalation_policy   derived from kind plus the delivery contract

bin/fm-reasoning-lib.sh is the single owner of the enum, the derivations and the
stable refusal tokens. It records; it does not enforce - no dispatch is blocked
for reasoning too little. The vocabulary is closed because a free-text reason
cannot be counted, and the two derived fields are never caller-supplied so they
cannot disagree with the record they summarize.

TOOLING_GAP is the one code that is NOT a reasoning code. It names a turn taken
only because a deterministic reader is broken or absent. It records
reasoning_required=no so it can never be counted as justified reasoning, and it
requires --tooling-gap-item naming a work item that is currently OPEN in this
home's data/backlog.md. Without that check the code would launder every unfixed
tool into a permanent "necessary agent turn" - the single failure mode that
would make this record worse than no record at all.

SCOPE, and a documented replacement of completion criterion (a). The spec says
every new spawn record carries all four fields. A --secondmate spawn is excluded
and refuses all three flags: it provisions a standing home rather than
dispatching a task - AGENTS.md section 10 keeps a secondmate out of the backlog
for the same reason - and Lane B derived the enum entirely from task
invocations, so demanding one of its codes for a provisioning action would
manufacture exactly the rubber-stamp answer the enum exists to prevent. Absent
fields read as unknown and never as justified, so a secondmate record cannot be
miscounted either way. The criterion is replaced by a stronger tested pair:
every TASK dispatch carries all four fields, AND a secondmate spawn that passes
one is refused with a stable token rather than silently defaulted.

bin/fm-promote.sh recomputes escalation_policy, because promotion changes the
delivery contract that field is derived from. Leaving it would keep a scout's
report-only posture on a task that can now reach a merge gate.

RETIREMENT. The record was ABSENT, so no mechanism is replaced in code. What
retires is the untracked category of agent turns taken because a reader is
broken: before this, such a turn was indistinguishable in the record from
justified reasoning, and TOOLING_GAP plus its refusing filing check is what ends
that. No transitional second path is left alive - --reason-code is the only way
to record a reason, and it is required rather than optional.

CERTIFICATION. tests/fm-reasoning-required.test.sh, eight cases. Every case was
witnessed RED against the pre-change bin/fm-spawn.sh and bin/fm-promote.sh
before being accepted green. Three further targeted negative controls were run
and witnessed red: loosening the open-item match (the already-closed and prefix
rows go red), making reasoning_required always yes (the TOOLING_GAP row goes
red), and degrading an unreadable dispatch config to "unconfigured" (the
unverifiable-floor row goes red). The published-codes case asserts the recorded
value positively rather than the absence of a refusal, because the
absence-only version of it was vacuously green against the reverted
implementation and so proved nothing. The lint gate was also shown able to
reject (exit 1 on a deliberate violation) rather than trusted for being quiet.

Existing spawn call sites in 18 test files carry the new required flag.

Three failures remain in the touched set and were each proven PRE-EXISTING on the
unmodified contribution base by a stashed baseline run, not asserted: two scout
teardown decision gates refused because this environment has tasks-axi 0.2.3
against the required 0.2.4 floor, and one Pi extension case fails on a Node ESM
loader error. All three reproduce identically with these changes reverted.
The landed contribution makes --reason-code required for every ship and
scout dispatch. Five suites that exist only on the fork trunk, or that
grew fork-only spawn call sites since the contribution was cut upstream,
still spawned without it and were refused before reaching the behavior
they pin.

Each call site now declares NL_RULE_CLASSIFICATION, matching how the
contribution adapted the suites it could see upstream. The Herdr launcher
helper picks the flag conditionally: a --secondmate spawn provisions a
standing home and refuses --reason-code outright, so passing it to every
spawn the helper drives would break the secondmate case instead.

No product behavior changes here; this is the fork-side half of the same
mechanical adaptation.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant