Skip to content

fix: simplify evidence receipts and No-Mistakes handoff - #211

Merged
dnth merged 5 commits into
mainfrom
fm/fm-receipt-review-fable
Oct 6, 2026
Merged

dnth merged 5 commits into
mainfrom
fm/fm-receipt-review-fable

Conversation

@dnth

@dnth dnth commented Oct 6, 2026

Copy link
Copy Markdown
Owner

Intent

Simplify Firstmate's evidence-receipt system so receipts prove only acceptance-criterion accounting and No-Mistakes exclusively owns its validation lifecycle, per the captain-approved consolidated design (data/fm-receipt-simplification-review/consolidated.md, merging the Astra and Fable reviews).

Accepted requirements:

  • AC1: bin/fm-receipt-check.sh offers only , --criterion and --parse-criteria, and reports required, evidenced, accepted_blocked, missing and invalid (schema fm-evidence-check.v2); the latest record per criterion wins; failure, stale and unknown-criterion records never satisfy an AC; a successful test of expected-negative behavior (e.g. an expected 401 recorded with outcome success) counts as evidence. Invalidation of a criterion by a finding is "append a failure receipt for that AC, then a fresh success"; no validation generations.
  • AC2: The validation planner (--plan), LOW/HIGH risk classifier, validation generations, No-Mistakes run binding and sealing (--bind-run, --bind-check, --complete, --implementation-complete, --mechanical-ready, --invalidate-claim), restamp, content-tree, descendant and synchronized-run reconciliation helpers in bin/fm-nm-run-lib.sh, and every validation_* and implementation_completed_* metadata key are removed from code, briefs, skills, AGENTS.md and docs, with no compatibility wrappers, no replacement validation state machine, and prefer deletion over deprecation. The classifier's only low path (a whitespace-only CHANGELOG edit) never fired in production; every no-mistakes task now runs full validation.
  • AC3: For no-mistakes tasks, bin/fm-pr-check.sh records nm_run_id and refuses to arm unless No-Mistakes' own axi status for that run matches the task branch, the PR URL and the PR's full head SHA (forge head via gh headRefOid against the run's head_sha) and the run is passed or CI-green; it then runs the existing ask-user decision audit (fm_nm_ask_user_decisions, the sqlite read-only adapter) as that guarantee's single PR-ready owner, and the evidence completeness check. The ask-user audit stays in firstmate because No-Mistakes exposes no CLI for per-finding gate decision provenance. A PR already recorded as pr= for the task passed those gates at registration, so re-registering the same URL (fm-pr-merge.sh does this before every merge; reconciliation re-arms skipped polls) refreshes pr_head= and re-arms without re-running the gates; a different URL is gated in full.
  • AC4: bin/fm-crew-state.sh accepts a ship done only with a clean worktree, complete evidence, and pr= recorded for PR modes or a clean fm/ branch for local-only, and applies the same ask-user decision audit at done against the recorded nm_run_id or the attributed run.
  • AC5: bin/fm-spawn.sh --relaunch preserves pr=, pr_head= and nm_run_id= (previously it rebuilt metadata and dropped them), proven by restart tests including a restart mid-handoff. This is in this PR, not a follow-up.
  • AC6: The PR-publication race protection that rode on the validation-plan lock survives under the neutral name state/..pr-publication.lock in fm-pr-check.sh and fm-watch.sh, with its race tests.
  • AC7: Obsolete binding, sealing, restamp, generation and classifier tests are deleted; the behavioral set is added (complete, missing, failed, expected-negative, accepted-blocked, unknown AC, invalidation, one handoff per delivery mode, wrong or foreign run refused, restart preservation); bin/fm-lint.sh and bin/fm-doc-audience-check.sh pass; the report states production and test lines removed versus added.

Decisions and constraints: delivery mode and yolo stay fixed at intake (AGENTS.md section 7), never chosen after implementation. Mechanical merge guards in fm-pr-merge.sh / fm-merge-local.sh (accepted-blocked and red-check refusals) are explicitly OUT of scope for a separate follow-up PR. The receipt writer no longer stamps a commit head; the schema still tolerates a legacy head field so old ledgers stay readable. The legacy done-artifact check remains owned by bin/fm-classify-lib.sh and bin/fm-teardown.sh. fm_nm_ci_checks_state keeps parsing the CI log because axi status exposes no live checks-ready field. A no-mistakes PR-ready fails closed when the forge head cannot be observed (GitLab), consistent with No-Mistakes publishing GitHub PRs only. Tests use existing fakes only; no live Herdr, No-Mistakes runs, Boat or RunPod. Test fixtures in fm-pr-merge, fm-main-ci-watch and fm-teardown that register PRs carry a complete direct-PR evidence contract because registration now gates on evidence. Tests that fail identically on untouched origin/main (kimi tomllib, OMP/Pi TS versions, check-unregister, prepush-guard, send-turn-start, treehouse-orphan-recovery, secondmate-safety) are pre-existing environment failures and not in scope.

Firstmate-Validation-Generation: 599d0d30f079b483b226eb76b405aebb

What Changed

  • Reduce receipts to acceptance-criterion accounting with latest-record precedence, explicit accepted-blocked exceptions, and no commit-head stamping; remove validation planning, generations, binding, sealing, and reconciliation helpers.
  • Gate PR registration on complete evidence and matching No-Mistakes branch, PR, full head SHA, and passed or CI-green status. Audit ask-user decisions at registration and done; require clean worktrees and delivery artifacts for done acceptance.
  • Preserve PR and run identity across relaunches, retain publication locking, and update briefs, documentation, and tests for the simplified handoff.

Risk Assessment

🚨 High: Captain, the done gate can bypass the required decision audit for existing tasks, so this change needs correction before merging.

Testing

Receipt suites, manual CLI checks, and focused handoff, completion, relaunch, and publication-race checks passed using real Firstmate scripts with required external-service fixtures. A tracing-induced stderr failure passed after tracing was removed. CLI and metadata evidence was captured; no rendered UI changed. No lint, full suite, or other pipeline phase ran.

  • Live validation: ✅ go - 10 of 10 scenarios driven live against the product
Scenario Result Live Evidence
Record criterion evidence and receive v2 accounting with missing criteria named ✅ pass live Receipt CLI transcript; tests/fm-receipt-check.test.sh
Record expected 401 success, invalidate it with failure, then restore it with fresh success ✅ pass live Receipt CLI transcript
Account for accepted-blocked separately and reject missing captain exceptions, unknown criteria, and stale records ✅ pass live Receipt CLI transcript
Query and parse criteria, reject removed lifecycle options, and retain legacy receipt readability ✅ pass live Receipt CLI transcript; tests/fm-receipt.test.sh
Register direct-PR and No-Mistakes handoffs only after their evidence and run-identity requirements pass ✅ pass live Handoff and done CLI output; tests/fm-pr-check-handoff.test.sh
Refuse foreign branches, PRs, heads, unfinished runs, unavailable forge heads, and unapproved ask-user decisions ✅ pass live Handoff and done CLI output; tests/fm-pr-check-handoff.test.sh
Re-register an accepted PR while No-Mistakes is unavailable, but gate a different URL ✅ pass live Handoff and done CLI output
Accept done only with complete evidence, clean worktree, delivery identity, and applicable recorded-run decisions ✅ pass live Handoff and done CLI output; selected tests/fm-crew-state.test.sh scenarios
Relaunch mid-handoff and preserve PR URL, full head, and run ID without inventing absent records ✅ pass live Persisted metadata after relaunch
Serialize PR publication, defer incomplete metadata while locked, and reject it after the lock becomes stale ✅ pass live Publication race and watcher output
Evidence: Receipt CLI transcript
$ fm-receipt-check.sh accounting
{"schema":"fm-evidence-check.v2","task":"accounting","kind":"ship","status":"missing","required":["AC1","AC2"],"evidenced":[],"accepted_blocked":[],"missing":["AC1","AC2"],"invalid":[]}

exit=1

$ fm-receipt.sh accounting AC1 api Expected unauthorized request 401 --outcome success
{"summary":"Expected unauthorized request","criterion":"AC1","type":"api","outcome":"success","result":"401"}

exit=0

$ fm-receipt.sh accounting AC2 manual Captain accepted blocked provider unavailable --outcome accepted-blocked

error: --outcome accepted-blocked requires a non-empty --captain-exception reference
exit=2

$ fm-receipt.sh accounting AC2 manual Captain accepted blocked provider unavailable --outcome accepted-blocked --captain-exception 2026-10-06 fixture captain accepts blocked AC2
{"captain_exception":"2026-10-06 fixture captain accepts blocked AC2","result":"unavailable","outcome":"accepted-blocked","summary":"Captain accepted blocked provider","criterion":"AC2","type":"manual"}

exit=0

$ fm-receipt-check.sh accounting
{"schema":"fm-evidence-check.v2","task":"accounting","kind":"ship","status":"complete","required":["AC1","AC2"],"evidenced":["AC1"],"accepted_blocked":[{"criterion":"AC2","captain_exception":"2026-10-06 fixture captain accepts blocked AC2"}],"missing":[],"invalid":[]}

exit=0

$ fm-receipt.sh accounting AC1 review Regression finding 401 regressed --outcome failure
{"summary":"Regression finding","result":"401 regressed","type":"review","criterion":"AC1","outcome":"failure"}

exit=0

$ fm-receipt-check.sh accounting
{"schema":"fm-evidence-check.v2","task":"accounting","kind":"ship","status":"missing","required":["AC1","AC2"],"evidenced":[],"accepted_blocked":[{"criterion":"AC2","captain_exception":"2026-10-06 fixture captain accepts blocked AC2"}],"missing":["AC1"],"invalid":[]}

exit=1

$ fm-receipt.sh accounting AC1 api Expected rejection restored 401 --outcome success
{"outcome":"success","summary":"Expected rejection restored","result":"401","criterion":"AC1","type":"api"}

exit=0

$ fm-receipt-check.sh accounting
{"schema":"fm-evidence-check.v2","task":"accounting","kind":"ship","status":"complete","required":["AC1","AC2"],"evidenced":["AC1"],"accepted_blocked":[{"criterion":"AC2","captain_exception":"2026-10-06 fixture captain accepts blocked AC2"}],"missing":[],"invalid":[]}

exit=0

$ fm-receipt.sh accounting AC9 test Unknown criterion passed --outcome success

error: criterion is not declared by a valid ship brief: AC9
exit=1

$ fm-receipt-check.sh accounting --criterion AC1


exit=0

$ fm-receipt-check.sh accounting --criterion AC9


exit=1

$ fm-receipt-check.sh --parse-criteria ~/.no-mistakes/worktrees/dd71c22cc6d7/01M47T6VR4W645HT5XXJ5XCHMQ/.phase-test-tmp/manual-home/data/accounting/brief.md
AC1	Expected rejection is evidenced.
AC2	A blocked criterion is distinctly accounted for.

exit=0

$ fm-receipt-check.sh accounting --plan

error: unknown option: --plan
exit=2

$ fm-receipt-check.sh accounting --bind-run

error: unknown option: --bind-run
exit=2

$ fm-receipt-check.sh accounting --bind-check

error: unknown option: --bind-check
exit=2

$ fm-receipt-check.sh accounting --complete

error: unknown option: --complete
exit=2

$ fm-receipt-check.sh accounting --implementation-complete

error: unknown option: --implementation-complete
exit=2

$ fm-receipt-check.sh accounting --mechanical-ready

error: unknown option: --mechanical-ready
exit=2

$ fm-receipt-check.sh accounting --invalidate-claim

error: unknown option: --invalidate-claim
exit=2

$ fm-receipt-check.sh accounting
{"schema":"fm-evidence-check.v2","task":"accounting","kind":"ship","status":"invalid","required":["AC1","AC2"],"evidenced":["AC1"],"accepted_blocked":[{"criterion":"AC2","captain_exception":"2026-10-06 fixture captain accepts blocked AC2"}],"missing":[],"invalid":["line 5: undeclared criterion AC9"]}

exit=2

$ fm-receipt-check.sh accounting
{"schema":"fm-evidence-check.v2","task":"accounting","kind":"ship","status":"invalid","required":["AC1","AC2"],"evidenced":["AC1"],"accepted_blocked":[{"criterion":"AC2","captain_exception":"2026-10-06 fixture captain accepts blocked AC2"}],"missing":[],"invalid":["line 5: invalid receipt"]}

exit=2
Evidence: Handoff and done CLI output

handoff-transcript.log — actual captured Firstmate CLI output

'●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
●  WATCHER DOWN - SUPERVISION IS OFF
●  1 task(s) in flight, but no watcher has a fresh beacon (last beat: never, grace 300s).
●  This read-only session should report the lapse, not repair it.
●  This is a supervision warning only; the guarded operation WILL still run.
●  Watcher repair belongs to the session holding the fleet lock; do not drain, arm, or repair from this read-only session.
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
error: PR-ready refused for direct: missing evidence: AC2'
'●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
●  WATCHER DOWN - SUPERVISION IS OFF
●  1 task(s) in flight, but no watcher has a fresh beacon (last beat: never, grace 300s).
●  This read-only session should report the lapse, not repair it.
●  This is a supervision warning only; the guarded operation WILL still run.
●  Watcher repair belongs to the session holding the fleet lock; do not drain, arm, or repair from this read-only session.
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
error: PR-ready refused for direct: missing evidence: AC2'
'●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
●  WATCHER DOWN - SUPERVISION IS OFF
●  1 task(s) in flight, but no watcher has a fresh beacon (last beat: never, grace 300s).
●  This read-only session should report the lapse, not repair it.
●  This is a supervision warning only; the guarded operation WILL still run.
●  Watcher repair belongs to the session holding the fleet lock; do not drain, arm, or repair from this read-only session.
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
armed: state/direct.check.sh'
'●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
●  WATCHER DOWN - SUPERVISION IS OFF
●  2 task(s) in flight, but no watcher has a fresh beacon (last beat: never, grace 300s).
●  This read-only session should report the lapse, not repair it.
●  This is a supervision warning only; the guarded operation WILL still run.
●  Watcher repair belongs to the session holding the fleet lock; do not drain, arm, or repair from this read-only session.
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
armed: state/nmpass.check.sh'
'●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
●  WATCHER DOWN - SUPERVISION IS OFF
●  2 task(s) in flight, but no watcher has a fresh beacon (last beat: never, grace 300s).
●  This read-only session should report the lapse, not repair it.
●  This is a supervision warning only; the guarded operation WILL still run.
●  Watcher repair belongs to the session holding the fleet lock; do not drain, arm, or repair from this read-only session.
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
armed: state/nmpass.check.sh'
'●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
●  WATCHER DOWN - SUPERVISION IS OFF
●  2 task(s) in flight, but no watcher has a fresh beacon (last beat: never, grace 300s).
●  This read-only session should report the lapse, not repair it.
●  This is a supervision warning only; the guarded operation WILL still run.
●  Watcher repair belongs to the session holding the fleet lock; do not drain, arm, or repair from this read-only session.
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
error: No-Mistakes status could not be observed for nmpass'
'●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
●  WATCHER DOWN - SUPERVISION IS OFF
●  3 task(s) in flight, but no watcher has a fresh beacon (last beat: never, grace 300s).
●  This read-only session should report the lapse, not repair it.
●  This is a supervision warning only; the guarded operation WILL still run.
●  Watcher repair belongs to the session holding the fleet lock; do not drain, arm, or repair from this read-only session.
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
error: No-Mistakes run RUN-ci is neither passed nor CI-green'
'●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
●  WATCHER DOWN - SUPERVISION IS OFF
●  3 task(s) in flight, but no watcher has a fresh beacon (last beat: never, grace 300s).
●  This read-only session should report the lapse, not repair it.
●  This is a supervision warning only; the guarded operation WILL still run.
●  Watcher repair belongs to the session holding the fleet lock; do not drain, arm, or repair from this read-only session.
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
armed: state/nmci.check.sh'
'●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
●  WATCHER DOWN - SUPERVISION IS OFF
●  4 task(s) in flight, but no watcher has a fresh beacon (last beat: never, grace 300s).
●  This read-only session should report the lapse, not repair it.
●  This is a supervision warning only; the guarded operation WILL still run.
●  Watcher repair belongs to the session holding the fleet lock; do not drain, arm, or repair from this read-only session.
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
error: No-Mistakes run RUN-w is on branch '\''fm/other-task'\'', not the task branch'
'●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

... [4927 bytes truncated] ...

d report the lapse, not repair it.
●  This is a supervision warning only; the guarded operation WILL still run.
●  Watcher repair belongs to the session holding the fleet lock; do not drain, arm, or repair from this read-only session.
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
error: No-Mistakes status reports no run for nmwrong'
'●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
●  WATCHER DOWN - SUPERVISION IS OFF
●  4 task(s) in flight, but no watcher has a fresh beacon (last beat: never, grace 300s).
●  This read-only session should report the lapse, not repair it.
●  This is a supervision warning only; the guarded operation WILL still run.
●  Watcher repair belongs to the session holding the fleet lock; do not drain, arm, or repair from this read-only session.
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
error: No-Mistakes status could not be observed for nmwrong'
'●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
●  WATCHER DOWN - SUPERVISION IS OFF
●  4 task(s) in flight, but no watcher has a fresh beacon (last beat: never, grace 300s).
●  This read-only session should report the lapse, not repair it.
●  This is a supervision warning only; the guarded operation WILL still run.
●  Watcher repair belongs to the session holding the fleet lock; do not drain, arm, or repair from this read-only session.
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
error: the forge'\''s PR head could not be observed, so run RUN-w cannot be matched to https://github.com/o/r/pull/14'
'●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
●  WATCHER DOWN - SUPERVISION IS OFF
●  5 task(s) in flight, but no watcher has a fresh beacon (last beat: never, grace 300s).
●  This read-only session should report the lapse, not repair it.
●  This is a supervision warning only; the guarded operation WILL still run.
●  Watcher repair belongs to the session holding the fleet lock; do not drain, arm, or repair from this read-only session.
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
error: No-Mistakes run RUN-ask resolved ask-user findings without matching firstmate decisions
step review: 1 recorded ask-user gate decision(s) but only 0 resolved [key=nm-RUN-ask-review] record(s):
  finding R9 resolved as approve (round 1)
error: firstmate must record one resolved [key=nm-RUN-ask-<step>] line per decision event in state/nmask.status'
'●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
●  WATCHER DOWN - SUPERVISION IS OFF
●  5 task(s) in flight, but no watcher has a fresh beacon (last beat: never, grace 300s).
●  This read-only session should report the lapse, not repair it.
●  This is a supervision warning only; the guarded operation WILL still run.
●  Watcher repair belongs to the session holding the fleet lock; do not drain, arm, or repair from this read-only session.
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
error: No-Mistakes run RUN-ask resolved ask-user findings without matching firstmate decisions
step review: 1 recorded ask-user gate decision(s) but only 0 resolved [key=nm-RUN-ask-review] record(s):
  finding R9 resolved as approve (round 1)
error: firstmate must record one resolved [key=nm-RUN-ask-<step>] line per decision event in state/nmask.status'
'●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
●  WATCHER DOWN - SUPERVISION IS OFF
●  5 task(s) in flight, but no watcher has a fresh beacon (last beat: never, grace 300s).
●  This read-only session should report the lapse, not repair it.
●  This is a supervision warning only; the guarded operation WILL still run.
●  Watcher repair belongs to the session holding the fleet lock; do not drain, arm, or repair from this read-only session.
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
armed: state/nmask.check.sh'
'●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
●  WATCHER DOWN - SUPERVISION IS OFF
●  6 task(s) in flight, but no watcher has a fresh beacon (last beat: never, grace 300s).
●  This read-only session should report the lapse, not repair it.
●  This is a supervision warning only; the guarded operation WILL still run.
●  Watcher repair belongs to the session holding the fleet lock; do not drain, arm, or repair from this read-only session.
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
missing no-mistakes state database at ~/.no-mistakes/worktrees/dd71c22cc6d7/01M47T6VR4W645HT5XXJ5XCHMQ/.phase-test-tmp/fm-pr-check-handoff.8bmPV5/nm-empty/state.sqlite
error: No-Mistakes run RUN-unread ask-user decision evidence could not be read'

crew-transcript.log — actual captured Firstmate CLI output

'state: parked · source: evidence-gate · missing evidence: AC1,AC2'
'state: parked · source: evidence-gate · missing evidence: AC2'
'state: done · source: status-log · PR https://github.com/o/r/pull/1 checks green'
'state: parked · source: evidence-gate · evidence check failed'
'state: parked · source: delivery-gate · PR not registered; run fm-pr-check on the PR-ready report'
'state: done · source: run-step · run passed: PR merged/closed'
'state: parked · source: validation-gate · worktree is dirty or could not be inspected'
'state: parked · source: delivery-gate · PR not registered; run fm-pr-check on the PR-ready report'
'state: done · source: status-log · PR https://github.com/o/r/pull/1 checks green'
'state: parked · source: validation-gate · worktree is dirty or could not be inspected'
'state: parked · source: delivery-gate · branch fm/delivery-gate-local is not checked out'
'state: done · source: status-log · ready in branch fm/delivery-gate-local'
'state: parked · source: decision-gate · run RUN-done-ask resolved ask-user findings without matching firstmate decisions'
'state: done · source: status-log · PR https://github.com/o/r/pull/1 checks green'
'state: parked · source: decision-gate · run RUN-done-ask ask-user decision evidence could not be read'
'state: working · source: status-log · fast path'
'state: working · source: status-log · fast path'
Evidence: Publication race and watcher output
ok - PR registration serializes on the per-task publication lock and releases it
ok - watcher defers valid pre-metadata polls while the publication lock is held
ok - watcher bounds pre-metadata deferral by publication lock freshness
ok - concurrent watchers observe only complete private poll publications

Artifact: ~/.no-mistakes/worktrees/dd71c22cc6d7/01M47T6VR4W645HT5XXJ5XCHMQ/.phase-test-tmp/fm-pr-check-security.WgD5HM/concurrent-1/watch.out
check: ~/.no-mistakes/worktrees/dd71c22cc6d7/01M47T6VR4W645HT5XXJ5XCHMQ/.phase-test-tmp/fm-pr-check-security.WgD5HM/concurrent-1/home/state/task-a.check.sh: merged

Artifact: ~/.no-mistakes/worktrees/dd71c22cc6d7/01M47T6VR4W645HT5XXJ5XCHMQ/.phase-test-tmp/fm-pr-check-security.WgD5HM/concurrent-2/watch.out
check: ~/.no-mistakes/worktrees/dd71c22cc6d7/01M47T6VR4W645HT5XXJ5XCHMQ/.phase-test-tmp/fm-pr-check-security.WgD5HM/concurrent-2/home/state/task-a.check.sh: merged

Artifact: ~/.no-mistakes/worktrees/dd71c22cc6d7/01M47T6VR4W645HT5XXJ5XCHMQ/.phase-test-tmp/fm-pr-check-security.WgD5HM/concurrent-3/watch.out
check: ~/.no-mistakes/worktrees/dd71c22cc6d7/01M47T6VR4W645HT5XXJ5XCHMQ/.phase-test-tmp/fm-pr-check-security.WgD5HM/concurrent-3/home/state/task-a.check.sh: merged

Artifact: ~/.no-mistakes/worktrees/dd71c22cc6d7/01M47T6VR4W645HT5XXJ5XCHMQ/.phase-test-tmp/fm-pr-check-security.WgD5HM/watcher-defer-metadata/watch.out
check: ~/.no-mistakes/worktrees/dd71c22cc6d7/01M47T6VR4W645HT5XXJ5XCHMQ/.phase-test-tmp/fm-pr-check-security.WgD5HM/watcher-defer-metadata/home/state/task-a.check.sh: merged

Artifact: ~/.no-mistakes/worktrees/dd71c22cc6d7/01M47T6VR4W645HT5XXJ5XCHMQ/.phase-test-tmp/fm-pr-check-security.WgD5HM/watcher-stale-defer-metadata/watch.out
check: rejected unauthenticated state checks: ~/.no-mistakes/worktrees/dd71c22cc6d7/01M47T6VR4W645HT5XXJ5XCHMQ/.phase-test-tmp/fm-pr-check-security.WgD5HM/watcher-stale-defer-metadata/home/state/task-a.check.sh

Artifact: ~/.no-mistakes/worktrees/dd71c22cc6d7/01M47T6VR4W645HT5XXJ5XCHMQ/.phase-test-tmp/fm-pr-check-security.WgD5HM/concurrent-1/watch.err

Artifact: ~/.no-mistakes/worktrees/dd71c22cc6d7/01M47T6VR4W645HT5XXJ5XCHMQ/.phase-test-tmp/fm-pr-check-security.WgD5HM/concurrent-2/watch.err

Artifact: ~/.no-mistakes/worktrees/dd71c22cc6d7/01M47T6VR4W645HT5XXJ5XCHMQ/.phase-test-tmp/fm-pr-check-security.WgD5HM/concurrent-3/watch.err

Artifact: ~/.no-mistakes/worktrees/dd71c22cc6d7/01M47T6VR4W645HT5XXJ5XCHMQ/.phase-test-tmp/fm-pr-check-security.WgD5HM/watcher-defer-metadata/watch.err

Artifact: ~/.no-mistakes/worktrees/dd71c22cc6d7/01M47T6VR4W645HT5XXJ5XCHMQ/.phase-test-tmp/fm-pr-check-security.WgD5HM/watcher-stale-defer-metadata/watch.err
Evidence: Persisted metadata after relaunch
ok - all dead-endpoint relaunch tests
ok - fm-spawn --relaunch: pr=, pr_head=, and nm_run_id= survive a restart mid-handoff
ok - fm-spawn --relaunch: a task not yet registered gains no empty delivery records

Persisted task metadata: ~/.no-mistakes/worktrees/dd71c22cc6d7/01M47T6VR4W645HT5XXJ5XCHMQ/.phase-test-tmp/fm-relaunch-dead-endpoint.7RsQUr/delivery-meta/home/state/rel-de-delivery-meta-2400093.meta
window=ses-rel-de-delivery-meta-2400093:fm-rel-de-delivery-meta-2400093
endpoint_task_id=rel-de-delivery-meta-2400093
worktree=~/.no-mistakes/worktrees/dd71c22cc6d7/01M47T6VR4W645HT5XXJ5XCHMQ/.phase-test-tmp/fm-relaunch-dead-endpoint.7RsQUr/delivery-meta/pool/17/proj
project=~/.no-mistakes/worktrees/dd71c22cc6d7/01M47T6VR4W645HT5XXJ5XCHMQ/.phase-test-tmp/fm-relaunch-dead-endpoint.7RsQUr/delivery-meta/project
harness=omp
kind=ship
mode=no-mistakes
yolo=off
tasktmp=/tmp/fm-rel-de-delivery-meta-2400093
model=openai-codex/gpt-5.6-luna
effort=high
spawn_gen=s1791264553.2401454.20176
pr=https://github.com/o/r/pull/77
pr_head=0123456789abcdef0123456789abcdef01234567
nm_run_id=01RUNMIDHANDOFF
omp_bin=~/.no-mistakes/worktrees/dd71c22cc6d7/01M47T6VR4W645HT5XXJ5XCHMQ/.phase-test-tmp/fm-relaunch-dead-endpoint.7RsQUr/delivery-meta/fake/fake/fakebin/omp
omp_bun=~/.no-mistakes/worktrees/dd71c22cc6d7/01M47T6VR4W645HT5XXJ5XCHMQ/.phase-test-tmp/fm-relaunch-dead-endpoint.7RsQUr/delivery-meta/fake/fake/fakebin/bun

Persisted task metadata: ~/.no-mistakes/worktrees/dd71c22cc6d7/01M47T6VR4W645HT5XXJ5XCHMQ/.phase-test-tmp/fm-relaunch-dead-endpoint.7RsQUr/delivery-none/home/state/rel-de-delivery-none-2400093.meta
window=ses-rel-de-delivery-none-2400093:fm-rel-de-delivery-none-2400093
endpoint_task_id=rel-de-delivery-none-2400093
worktree=~/.no-mistakes/worktrees/dd71c22cc6d7/01M47T6VR4W645HT5XXJ5XCHMQ/.phase-test-tmp/fm-relaunch-dead-endpoint.7RsQUr/delivery-none/pool/17/proj
project=~/.no-mistakes/worktrees/dd71c22cc6d7/01M47T6VR4W645HT5XXJ5XCHMQ/.phase-test-tmp/fm-relaunch-dead-endpoint.7RsQUr/delivery-none/project
harness=omp
kind=ship
mode=no-mistakes
yolo=off
tasktmp=/tmp/fm-rel-de-delivery-none-2400093
model=openai-codex/gpt-5.6-luna
effort=high
spawn_gen=s1791264559.2411356.3366
omp_bin=~/.no-mistakes/worktrees/dd71c22cc6d7/01M47T6VR4W645HT5XXJ5XCHMQ/.phase-test-tmp/fm-relaunch-dead-endpoint.7RsQUr/delivery-none/fake/fake/fakebin/omp
omp_bun=~/.no-mistakes/worktrees/dd71c22cc6d7/01M47T6VR4W645HT5XXJ5XCHMQ/.phase-test-tmp/fm-relaunch-dead-endpoint.7RsQUr/delivery-none/fake/fake/fakebin/bun

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

⚠️ **Review** - 2 issues (1 error, 1 warning)
  • 🚨 bin/fm-crew-state.sh:133 - AC4 requires the "same ask-user decision audit at done against the recorded nm_run_id or the attributed run." The new if [ -n &#34;$audit_run&#34; ]; then guard skips that audit entirely when neither identity is available. A pre-upgrade task with pr=, complete receipts, a clean worktree, and an idle worker reporting done is therefore accepted when No-Mistakes is unavailable. The changed fixture at tests/fm-crew-state.test.sh:1557 supplies precisely this metadata, and tests/fm-crew-state.test.sh:1573 expects done. Fail closed in emit() when no audit run can be established, covering both status-log and coarse-run completion, and correct that fixture's expectation.
  • ⚠️ docs/verification/evidence-receipts.md:96 - AC7 requires that "the report states production and test lines removed versus added." The rewritten verification report lists guarantees and commands but contains no such accounting. Add separate production and test additions/deletions for the reviewed commit range.

🔧 Fix applied.
2 issues (1 error, 1 warning) still open:

  • 🚨 bin/fm-crew-state.sh:133 - AC4 requires the "same ask-user decision audit at done against the recorded nm_run_id or the attributed run." The new if [ -n &#34;$audit_run&#34; ]; then guard skips that audit entirely when neither identity is available. A pre-upgrade task with pr=, complete receipts, a clean worktree, and an idle worker reporting done is therefore accepted when No-Mistakes is unavailable. The changed fixture at tests/fm-crew-state.test.sh:1557 supplies precisely this metadata, and tests/fm-crew-state.test.sh:1573 expects done. Fail closed in emit() when no audit run can be established, covering both status-log and coarse-run completion, and correct that fixture's expectation.
  • ⚠️ bin/fm-pr-check.sh:131 - AC3 requires that “re-registering the same URL ... re-arms without re-running the gates,” but [ -z &#34;$NM_RUN_ID&#34; ] || ALREADY_REGISTERED=1 adds an unrequired prerequisite. Pre-upgrade no-mistakes tasks have registered pr= records without nm_run_id, which the old registration never wrote. When fm-pr-merge.sh:349 re-registers that URL with No-Mistakes unavailable, registration now refuses and blocks merging. Remove the extra prerequisite and let exact recorded-URL matching control re-registration. Sibling sites: bin/fm-pr-check.sh:136 repeats evidence checks; bin/fm-pr-check.sh:152 repeats run-identity and decision checks. This is separate from declined R1’s done-time audit.
✅ **Test** - passed

✅ No issues found.

  • Live validation: ✅ go - 10 of 10 scenarios driven live against the product
Scenario Result Live Evidence
Record criterion evidence and receive v2 accounting with missing criteria named ✅ pass live Receipt CLI transcript; tests/fm-receipt-check.test.sh
Record expected 401 success, invalidate it with failure, then restore it with fresh success ✅ pass live Receipt CLI transcript
Account for accepted-blocked separately and reject missing captain exceptions, unknown criteria, and stale records ✅ pass live Receipt CLI transcript
Query and parse criteria, reject removed lifecycle options, and retain legacy receipt readability ✅ pass live Receipt CLI transcript; tests/fm-receipt.test.sh
Register direct-PR and No-Mistakes handoffs only after their evidence and run-identity requirements pass ✅ pass live Handoff and done CLI output; tests/fm-pr-check-handoff.test.sh
Refuse foreign branches, PRs, heads, unfinished runs, unavailable forge heads, and unapproved ask-user decisions ✅ pass live Handoff and done CLI output; tests/fm-pr-check-handoff.test.sh
Re-register an accepted PR while No-Mistakes is unavailable, but gate a different URL ✅ pass live Handoff and done CLI output
Accept done only with complete evidence, clean worktree, delivery identity, and applicable recorded-run decisions ✅ pass live Handoff and done CLI output; selected tests/fm-crew-state.test.sh scenarios
Relaunch mid-handoff and preserve PR URL, full head, and run ID without inventing absent records ✅ pass live Persisted metadata after relaunch
Serialize PR publication, defer incomplete metadata while locked, and reject it after the lock becomes stale ✅ pass live Publication race and watcher output
  • TMPDIR="$PWD/.phase-test-tmp" bash tests/fm-receipt-check.test.sh
  • TMPDIR="$PWD/.phase-test-tmp" bash tests/fm-receipt.test.sh
  • TMPDIR="$PWD/.phase-test-tmp" bash -x tests/fm-pr-check-handoff.test.sh
  • Manual fm-receipt.sh and fm-receipt-check.sh calls with isolated task records and adversarial ledger inputs
  • Selected crew-state tests: evidence completeness, malformed brief, registered PR, clean local branch, recorded-run decision audit, and fast-mode isolation
  • Selected relaunch tests: preserve delivery identity mid-handoff and avoid inventing delivery records
  • Selected publication tests: lock serialization, fresh-lock deferral, stale-lock refusal, and concurrent watcher publication
  • Captured CLI transcripts, watcher output, and persisted relaunch metadata; removed disposable fixtures and confirmed clean worktree
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

dnth added 5 commits October 6, 2026 10:39
…nting

Receipts had grown a second validation lifecycle beside No-Mistakes: a risk
classifier whose low path never fired in production, plan generations, run
binding by ancestry, restamp and content-tree proofs, branch-custody evidence,
terminal sealing, and a validation_* metadata family that eleven follow-up PRs
kept patching. fm-receipt-check.sh now answers one question - did the worker
account for every declared acceptance criterion - through the default check,
--criterion, and --parse-criteria, emitting fm-evidence-check.v2. The latest
receipt per criterion decides it, so a finding is recorded as a later failure
and satisfied again only by a fresher success; no generations.

Run identity moves to the PR-ready owner. fm-pr-check.sh requires complete
evidence for every ship mode and, for no-mistakes tasks, proves the run from
No-Mistakes' own axi status (task branch, PR URL, full head_sha against the
forge's PR head, passed or CI-green), records nm_run_id=, and runs the ask-user
decision audit as its single PR-ready owner. fm-crew-state.sh accepts a ship
done only with a clean worktree, complete evidence, pr= for the PR modes or a
clean fm/<id> branch for local-only, and the same ask-user audit against the
recorded or attributed run. fm-spawn.sh --relaunch carries pr=, pr_head=, and
nm_run_id= forward so a restart mid-handoff can still satisfy those gates. The
PR-publication race protection that rode on the validation-plan lock survives
as state/.<id>.pr-publication.lock.

Briefs, validation-supervision, ship-landing, AGENTS.md section 7, and the
verification record state the one scope: receipts certify nothing about
review, CI, No-Mistakes completion, or merge readiness. Binding, sealing,
restamp, generation, and classifier tests are deleted; the behavioral set
(complete, missing, failed, expected-negative, accepted-blocked, unknown AC,
invalidation, one handoff per mode, wrong or foreign run refused, restart
preservation) is added across the receipt, pr-check handoff, crew-state, and
relaunch suites.
…ir owners

The per-task publication lock gets its operational-home-layout entry, the
no-mistakes timeout fm-pr-check.sh reads gets its configuration.md line, and the
GitLab merge-watch record states that only direct-PR registers a merge request
because no-mistakes PR-ready compares the forge head with the run's head_sha.
fm-pr-merge.sh registers the PR once more before every merge, and
reconciliation re-arms a skipped poll; a PR this task already records as pr=
passed the evidence, run-identity, and ask-user gates at its registration, so
the re-registration refreshes pr_head= and re-arms without consulting
No-Mistakes again, while a different URL is gated in full. The merge, CI-watch,
and teardown fixtures that register a PR now carry a complete direct-PR
evidence contract instead of a no-mistakes mode they never exercised.
@dnth
dnth merged commit 1608b2c into main Oct 6, 2026
17 checks passed
@dnth
dnth deleted the fm/fm-receipt-review-fable branch October 6, 2026 06:24
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