Skip to content

feat(bin): reconcile ruling documents against open captain decision holds - #1819

Closed
sbracewell64 wants to merge 6 commits into
kunchenguid:mainfrom
sbracewell64:fm/cfvc-01-ruling-hold-reconcile
Closed

sbracewell64 wants to merge 6 commits into
kunchenguid:mainfrom
sbracewell64:fm/cfvc-01-ruling-hold-reconcile

Conversation

@sbracewell64

Copy link
Copy Markdown

Intent

Implement increment CFVC-01 of the approved CFVC remediation plan: reconcile ruling documents against open captain decision holds, so that no captain decision that has been answered remains presented as pending.

WHY. 24 of 35 open captain holds carried a recorded captain verdict while still queued as awaiting a captain decision. One investigation lane built its entire operator-removal analysis on such a hold. The captain is re-asked answered questions and investigations inherit stale premises. bin/fm-decision-hold.sh resolve already existed and was never called from a ruling; a model re-read ruling prose on every bearings and every investigation. The commission puts reconciliation under CODE.

TARGET STATE. A deterministic prefilter emits, per open captain hold, the ruling-document excerpts that name it, or no_delta. An agent grades each excerpt as rules / commissions / cites / defers. fm-decision-hold.sh resolve records graded answers. Unmatched holds stay open and are reported as such.

VALUE-CREATOR SPLIT, which is deliberate and load-bearing. CODE owns the candidate set, the excerpt, and the no-delta terminal. AGENT owns the grade, because deciding whether a ruling row rules a hold versus commissions, cites, or defers it requires classifying natural language - a rubric, not a rule. ENGINEER owns closure authority. The naive version of this measurement was a raw grep count of 26; the graded version was 24 ruled / 2 explicitly unruled, and only reading the excerpts produced that. The reviewer should expect a deliberate refusal to over-mechanise: this must not become a parser.

THE CAPTAIN'S CLOSURE RULING BINDS (2026-08-06, option c, recorded at data/captain-rulings-2026-08-06/cfvc-commission-approval.md). Firstmate may close a captain decision hold on the strength of a ruling ONLY when BOTH hold: (1) the ruling document names the hold identifier VERBATIM, and (2) the ruling carries an EXPLICIT VERDICT TOKEN. Everything else ESCALATES - not "closes with a note", not "closes provisionally". A near-match, paraphrase, or semantically equivalent identifier is not verbatim. A hold named in a commission rather than a ruling is graded cites, never rules, and does not satisfy condition 1. An agent grading pass may PROPOSE a closure; it may never PERFORM one that fails either condition. This is an authority question and no model may expand its own authority.

CHANGED COMPONENTS. New bin/fm-ruling-reconcile.sh; a --from-ruling <path>:<line> provenance field on bin/fm-decision-hold.sh resolve; bin/fm-session-start.sh surfaces the count. Schema fm-ruling-index.v1 (holds.tsv, rulings.tsv, matches.tsv, index.meta with hold_fingerprint + ruling_fingerprint), mirroring fm-research-index.v1 deliberately - NO new schema family and NO new store. The index is derived; authority stays in the ruling documents and the backlog. Output is a schema-header-then-rows envelope: stale_holds[N]{hold_id,ruling_file,ruling_line,excerpt,grade}. Reads tasks-axi list --kind captain.

DELIBERATE DESIGN DECISIONS a reviewer reading only the diff would not know:

  • The prefilter is modelled on bin/fm-research-scan.sh's derived-index discipline by explicit instruction. That file does NOT exist at this contribution base (it exists only at the running fleet head), so it was used as a design reference and deliberately NOT depended on at runtime. The new script is self-contained.
  • Document class is decided structurally from naming, not by loose substring, because the real corpus contains both a ruling ABOUT a commission (captain-rulings-2026-08-04-commission-32.md) and a commission INSIDE a rulings directory (captain-rulings-2026-08-06/cfvc-remediation-commission.md). Prefix form wins, then suffix form, then directory. An unrecognised name is other and escalates. Misclassification is biased toward escalation on purpose.
  • The verdict vocabulary is tuned for PRECISION, not recall, and its coverage is REPORTED RATHER THAN TUNED: 15 of 24 rows on the flagship ruling table are eligible; the other nine state their verdict without emphasis and escalate. OPTION and RUN were measured against the real corpus; OPTION was removed because it matched the attribution span "Captain, 2026-08-06, choosing option (c) verbatim:". Widening the vocabulary until it reproduced a hoped-for count would defeat the condition it implements. Do not flag the 15/24 coverage as a shortfall - it is the intended conservative behaviour, and escalation is always the safe direction.
  • Eligibility is NECESSARY AND NOT SUFFICIENT. A closable-if-graded-rules row still requires a caller grade of rules and a separate fm-decision-hold.sh call. The script never closes a hold.
  • A markdown table row is scanned alone rather than through a line window. This fixes a measured defect: a windowed scan attributed one table row's verdict to a different row six lines above it.
  • --from-ruling is VERIFIED rather than trusted - it calls the closure test and refuses rather than stamping an unverified provenance. It is optional, so resolutions reached any other way are unaffected. The blank line before "Captain decision:" in the hold body is load-bearing because verify_resolution_identity parses on it; the provenance line is inserted above it.

RETIREMENT IS MANDATORY and was performed in this same change: the string "State: awaiting captain decision." is removed from the hold body. A hold asserting its own state is a claim, not a verdict, and it outlived the answer - an investigation read that string and treated an already-ruled hold as still open. The structured state/held/hold_kind fields are now the only state owner. This also retires the recurring manual archaeology of re-reading ruling prose per bearings and per investigation. No old-and-new pair is left alive.

CERTIFICATION - RED-CAPABLE TESTS ONLY. A test that only passes after the implementation is not sufficient evidence. Every one of the eight cases in tests/fm-ruling-reconcile.test.sh was run against a deliberately broken copy of the production script and OBSERVED FAILING, then run against the real code and observed passing. That discipline found three real defects a green suite had hidden: (a) a find -type f corpus walk that silently dropped a symlinked ruling document instead of refusing it; (b) two grep -qv assertions that could never fail on multi-line output; (c) the cross-row verdict bleed described above. The empty-set law binds: an unreadable, symlinked, or out-of-corpus ruling document yields NO_RULING_READ and exit 3 with no index published - never "unruled".

COMPLETION CRITERION (a) WAS REPLACED, DELIBERATELY. The spec required reproducing "24 ruled / 2 explicitly unruled / 9 unmatched" over the live home. That state no longer exists and cannot be reproduced at any effort: the backfill was already performed by hand before this increment ran, which corrected the measurement to 27 of 36 and then drained the register to ZERO open captain holds. The plan's own instruction covers this case - replace a stale criterion with an equally strong or stronger red-capable one and document why. The replacement is a sealed fixture corpus that drives the ruling, commission, unmatched and no-verdict cases apart in one scan, which is stronger because it cannot decay the way a one-time snapshot did. Criteria (b) (no_delta costs no extraction) and (c) (the mutation test) are met as written. This is intentional, not an omission.

MIGRATION was one backfill pass over the measured instances, graded individually, NO BULK CLOSE - and it had already been completed manually before this task began, so this change ships the mechanism that retires the manual pass rather than repeating it. Rollback: delete the derived index; resolve is idempotent and provenance-stamped.

CONSTRAINTS THAT BIND THIS WORK. No new scheduler, daemon, wake queue, LoopSpec runner, or project-management status system - existing execution and control machinery is reused. Work identity has one owner: the deterministic Runtime / control plane; nothing here becomes a competing source of truth. LANDING AUTHORITY IS NOT EXPANDED - this change creates no merge authority whatsoever; ruling B4 (autonomous landing authority NOT AUTHORIZED) stands. blocking_on-style state must be derived by code, never declared. A broken reader is a TOOLING_GAP, not legitimate reasoning demand.

SHARED TRACKED MATERIAL. This touches firstmate's own tracked surface (AGENTS.md, bin/, .agents/skills/, docs/, tests/), so the firstmate-coding-guidelines skill was loaded and applied: one sentence per line in tracked Markdown, plain dash never em dash, no agent co-author, shellcheck-clean bin scripts via bin/fm-lint.sh, colocated tests named .test.sh extending the existing runner, tests exercising behaviour through an executable interface and never asserting implementation-source bytes, one-owner rule for contracts with cross-references rather than restatements, and a dated maintainer-verification record under docs/.

KNOWN PRE-EXISTING FAILURES AT THIS CONTRIBUTION BASE, verified by reverting my changes and re-running - NOT caused by this work and NOT in scope: (1) the installed tasks-axi is 0.2.3 but this base's floor is 0.2.4 (the running fleet head's floor is 0.2.2, which is why the live home works), so tests/fm-decision-hold-lifecycle.test.sh cannot run here - it passes completely when shimmed past the floor, confirming no regression from this change; (2) tests/fm-session-start.test.sh's MISSING-diagnostic case fails identically with my session-start change reverted.

What Changed

  • New bin/fm-ruling-reconcile.sh: a deterministic, model-free prefilter that matches open captain decision holds (tasks-axi list --kind captain) against the ruling documents naming them, publishing a derived fm-ruling-index.v1 index (holds/rulings/matches plus fingerprinted meta) under state/ruling-index. It exposes scan, status, propose, closure-test, and schema; classifies documents structurally as ruling/commission/other; requires a delimited verbatim hold identifier plus an explicit emphasised verdict token on the cited row for eligibility; and never grades an excerpt or closes a hold. An unreadable, symlinked, or out-of-corpus ruling document yields NO_RULING_READ and exit 3 with no index published, rather than presenting as "unruled".
  • bin/fm-decision-hold.sh resolve gained an optional --from-ruling <path>:<line> that is verified rather than trusted: it invokes closure-test before any mutation, reads closure= as a field of that output (not as a substring of the stream), refuses the resolve unless the verdict is exactly permitted, and otherwise stamps a Ruling provenance: line above the load-bearing blank line preceding Captain decision:. The self-asserted State: awaiting captain decision. body line was retired so the structured state/held/hold_kind fields are the only state owner, and signal traps now clean up the closure work file and re-raise instead of swallowing an interrupt mid-resolve.
  • bin/fm-session-start.sh emits a bounded RULING_RECONCILE: line in the fleet-state digest, rebuilding the index when locked and reporting the existing index's count in read-only mode. Adds tests/fm-ruling-reconcile.test.sh (16 cases over a sealed fixture corpus, exercised against deliberately broken copies of the production scripts), plus documentation in docs/decision-hold-lifecycle.md, docs/scripts.md, AGENTS.md, and the decision-hold-lifecycle skill.

Risk Assessment

✅ Low: Every finding from the three prior rounds is now fixed and independently verified by execution, the new interrupt case is demonstrably red-capable rather than vacuous, the CI partition guard passes with the added test file, and the only remaining item is an info-level test-robustness nit.

Testing

Ran the new 16-case suite (all pass) plus an independent red-capability check that broke the two production scripts five ways and confirmed the suite caught every break, then drove the whole feature end-to-end over a realistic home: four captain holds created (body no longer self-reports "State: awaiting captain decision."), scan reaching delta then no_delta with extracted=0, the propose envelope separating ruling / commission / no-verdict / unmatched, closure-test permitting only the ruling+verbatim+verdict+rules case, resolve --from-ruling refusing a commission provenance while leaving the hold queued and then closing on the real ruling line with provenance stamped, the queued captain count dropping 4 to 3, the RULING_RECONCILE line appearing at session start, and a symlinked ruling yielding NO_RULING_READ with exit 3 instead of "unruled". No screenshots apply - this change is entirely CLI and persisted-index surface, so the evidence is the terminal transcript and the derived index files. The two failures the author flagged as pre-existing were reproduced and confirmed pre-existing (version floor, and identical failure with the session-start hunk reverted); the worktree was left clean and the repo copy used for mutation testing was deleted.

Evidence: End-to-end operator transcript (CFVC-01 full walkthrough)

$ fm-decision-hold.sh hold cfvc-review <key> --title ... --reason ...   (x4)
cfvc-review-decision-operator-removal
cfvc-review-decision-index-schema
cfvc-review-decision-landing-authority
cfvc-review-decision-wake-queue

$ tasks-axi show cfvc-review-decision-operator-removal --full
task:
  id: cfvc-review-decision-operator-removal
  title: CFVC operator-removal decision
  state: queued
  blocked: no
  blocked_by: none
  held: yes
  hold_reason: needs the captain
  hold_kind: captain
  hold_until: "-"
  kind: captain
  repo: firstmate
  priority: "-"
  created: 2026-08-06
  closed: "-"
  deps: none
  links: none
  body: "Origin: cfvc-review\nDecision key: operator-removal"

# RETIREMENT CHECK: the hold body no longer asserts its own state.
  OK: "State: awaiting captain decision." is gone; held/hold_kind own the state

$ tasks-axi list --kind captain --state queued
count: 4
tasks[4]{id,state,kind,repo,title}:
  cfvc-review-decision-operator-removal,queued,captain,firstmate,CFVC operator-removal decision
  cfvc-review-decision-index-schema,queued,captain,firstmate,CFVC index-schema decision
  cfvc-review-decision-landing-authority,queued,captain,firstmate,CFVC landing-authority decision
  cfvc-review-decision-wake-queue,queued,captain,firstmate,CFVC wake-queue decision
help[1]:
  - Run `tasks-axi show <id>` for full notes on a task

$ fm-ruling-reconcile.sh scan
schema=fm-ruling-index.v1
verdict=delta
open_holds=4
ruling_documents=2
extracted=3
holds_with_candidates=3
eligible_excerpts=1
index=/tmp/no-mistakes-evidence/01KZBJMKGZ2JEPTEBDAP862AD0/demo-home/state/ruling-index

$ fm-ruling-reconcile.sh scan     # unchanged corpus + unchanged holds
schema=fm-ruling-index.v1
verdict=no_delta
open_holds=4
ruling_documents=2
extracted=0
eligible_excerpts=1
index=/tmp/no-mistakes-evidence/01KZBJMKGZ2JEPTEBDAP862AD0/demo-home/state/ruling-index

$ fm-ruling-reconcile.sh propose
schema=fm-ruling-index.v1
authority=none
grading=required
closure_rule=verbatim-identifier-in-ruling AND explicit-verdict-token AND grade=rules
quoted_fields=excerpt,verdict_token
stale_holds[4]{hold_id,ruling_file,ruling_line,excerpt,grade,doc_class,verdict_token,verdict_line,eligibility}:
  cfvc-review-decision-index-schema,captain-rulings-2026-08-06.md,8,"| **C2** | `cfvc-review-decision-index-schema` | Replace the fictional cap of 3 with an **enforced ceiling of 10**. |",ungraded,ruling,"none",0,escalate
  cfvc-review-decision-landing-authority,cfvc-remediation-commission.md,5,"- `cfvc-review-decision-landing-authority` — **APPROVED** for investigation only.",ungraded,commission,"**APPROVED**",5,escalate
  cfvc-review-decision-operator-removal,captain-rulings-2026-08-06.md,7,"| **C1** | `cfvc-review-decision-operator-removal` | **APPROVED** — remove the operator as scoped. |",ungraded,ruling,"**APPROVED**",7,closable-if-graded-rules
  cfvc-review-decision-wake-queue,none,0,"unmatched: no ruling document names this hold (queued)",ungraded,none,"none",0,escalate

$ fm-ruling-reconcile.sh closure-test cfvc-review-decision-operator-removal --ruling captain-rulings-2026-08-06.md --line 7 --grade rules
schema=fm-ruling-index.v1
hold_id=cfvc-review-decision-operator-removal
ruling_file=captain-rulings-2026-08-06.md
ruling_line=7
grade=rules
doc_class=ruling
verbatim_identifier=yes
verdict_token=**APPROVED**
verdict_line=7
closure=permitted
provenance=captain-rulings-2026-08-06.md:7
command=bin/fm-decision-hold.sh resolve <origin-id> <decision-key> --decision-file <path> --routed-to <task-id> --from-ruling captain-rulings-2026-08-06.md:7
note=this script never closes a hold; fm-decision-hold.sh performs the closure and re-verifies this provenance

$ fm-ruling-reconcile.sh closure-test cfvc-review-decision-operator-removal --ruling captain-rulings-2026-08-06.md --line 7 --grade cites
schema=fm-ruling-index.v1
hold_id=cfvc-review-decision-operator-removal
ruling_file=captain-rulings-2026-08-06.md
ruling_line=7
grade=cites
doc_class=ruling
verbatim_identifier=yes
verdict_token=**APPROVED**
verdict_line=7
closure=escalate
reason=grade-is-cites-not-rules

$ fm-ruling-reconcile.sh closure-test cfvc-review-decision-index-schema --ruling captain-rulings-2026-08-06.md --line 8 --grade rules
schema=fm-ruling-index.v1
hold_id=cfvc-review-decision-index-schema
ruling_file=captain-rulings-2026-08-06.md
ruling_line=8
grade=rules
doc_class=ruling
verbatim_identifier=yes
verdict_token=none
verdict_line=0
closure=escalate
reason=no-explicit-verdict-token

$ fm-ruling-reconcile.sh closure-test cfvc-review-decision-landing-authority --ruling cfvc-remediation-commission.md --line 5 --grade rules
schema=fm-ruling-index.v1
hold_id=cfvc-review-decision-landing-authority
ruling_file=cfvc-remediation-commission.md
ruling_line=5
grade=rules
doc_class=commission
verbatim_identifier=yes
verdict_token=**APPROVED**
verdict_line=5
closure=escalate
reason=not-a-ruling-document

$ fm-decision-hold.sh resolve cfvc-review operator-removal ... --from-ruling cfvc-remediation-commission.md:5
fm-decision-hold: --from-ruling cfvc-remediation-commission.md:5 fails the captain closure test: not-a-ruling-document
  exit=1

$ tasks-axi show cfvc-review-decision-operator-removal --full   # after the refusal
  id: cfvc-review-decision-operator-removal
  state: queued
  held: yes
  hold_kind: captain
  body: "Origin: cfvc-review\nDecision key: operator-removal"

$ fm-decision-hold.sh resolve cfvc-review operator-removal ... --from-ruling captain-rulings-2026-08-06.md:7
resolved: cfvc-review-decision-operator-removal -> cfvc-operator-removal-work
  exit=0

$ tasks-axi show cfvc-review-decision-operator-removal --full   # after the verified close
task:
  id: cfvc-review-decision-operator-removal
  title: CFVC operator-removal decision
  state: done
  blocked: no
  blocked_by: none
  held: no
  hold_reason: needs the captain
  hold_kind: captain
  hold_until: "-"
  kind: captain
  repo: firstmate
  priority: "-"
  created: "-"
  closed: 2026-08-06
  deps: none
  links: none
  body: "Resolution recorded by fm-decision-hold.\nDecision digest: 6cbabe494233e7b8b952d525195c4245834ea893db067f6c34758602f98d3853\nRouted identities: cfvc-operator-removal-work\nRuling provenance: captain-rulings-2026-08-06.md:7\n\nCaptain decision:\nRemove the operator as scoped.\n\nRouted work:- cfvc-operator-removal-work"

$ tasks-axi list --kind captain --state queued   # the answered decision is no longer pending
count: 3
tasks[3]{id,state,kind,repo,title}:
  cfvc-review-decision-index-schema,queued,captain,firstmate,CFVC index-schema decision
  cfvc-review-decision-landing-authority,queued,captain,firstmate,CFVC landing-authority decision
  cfvc-review-decision-wake-queue,queued,captain,firstmate,CFVC wake-queue decision
help[1]:
  - Run `tasks-axi show <id>` for full notes on a task

$ fm-ruling-reconcile.sh scan     # the register drains
schema=fm-ruling-index.v1
verdict=delta
open_holds=3
ruling_documents=2
extracted=2
holds_with_candidates=2
eligible_excerpts=0
index=/tmp/no-mistakes-evidence/01KZBJMKGZ2JEPTEBDAP862AD0/demo-home/state/ruling-index

$ fm-ruling-reconcile.sh status
schema=fm-ruling-index.v1
derived=true
authority=none
generated=2026-08-06T00:00:00Z
corpus_root=/tmp/no-mistakes-evidence/01KZBJMKGZ2JEPTEBDAP862AD0/demo-home/data
hold_fingerprint=3f36d78b1692f14e1c800ecd62d5638faa3cd565891753fb1ea359f3a84aeda6
ruling_fingerprint=0d003d3fc4b03973556300ddc8915bde9c2e2c25054d883eb26d4bc1bc9add18
open_holds=3
ruling_documents=2
verdict_window=12
eligible_excerpts=0

$ fm-session-start.sh   # FLEET STATE section, as an operator sees it
FLEET STATE
================================================================================

data/backlog.md
--------------------------------------------------------------------------------
compact backlog listing (tasks-axi; max 80 item(s); task bodies omitted)
count: 5
tasks[5]{id,state,kind,repo,title,blocked_by,hold_kind,hold_reason}:
  cfvc-review-decision-index-schema,queued,captain,firstmate,CFVC index-schema decision,none,captain,needs the captain
  cfvc-review-decision-landing-authority,queued,captain,firstmate,CFVC landing-authority decision,none,captain,needs the captain
  cfvc-review-decision-wake-queue,queued,captain,firstmate,CFVC wake-queue decision,none,captain,needs the captain
  cfvc-operator-removal-work,queued,task,firstmate,Operator removal work,none,"-","-"
  cfvc-review-decision-operator-removal,done,captain,firstmate,CFVC operator-removal decision,none,captain,needs the captain
help[2]:
  - Run `tasks-axi show <id> --file=/tmp/no-mistakes-evidence/01KZBJMKGZ2JEPTEBDAP862AD0/demo-home/data/backlog.md` for full notes on a task
  - Run `tasks-axi ready --file=/tmp/no-mistakes-evidence/01KZBJMKGZ2JEPTEBDAP862AD0/demo-home/data/backlog.md` to see unblocked queued work
Full task bodies remain available on demand: tasks-axi show <id> --full when compatible tasks-axi is available, or data/backlog.md.

RULING_RECONCILE: 3 open captain decision(s); 0 ruling excerpt(s) await grading before any closure

Work under way (state/*.meta)

$ rm ruling; ln -s /dev/null ruling; fm-ruling-reconcile.sh scan
schema=fm-ruling-index.v1
verdict=NO_RULING_READ
unreadable=captain-rulings-2026-08-06.md
reason=symlinked-ruling-document-not-read
  exit=3

$ fm-session-start.sh   # an unread ruling is reported, never absorbed as 'unruled'
FLEET STATE
================================================================================

data/backlog.md
--------------------------------------------------------------------------------
compact backlog listing (tasks-axi; max 80 item(s); task bodies omitted)
count: 5
tasks[5]{id,state,kind,repo,title,blocked_by,hold_kind,hold_reason}:
  cfvc-review-decision-index-schema,queued,captain,firstmate,CFVC index-schema decision,none,captain,needs the captain
  cfvc-review-decision-landing-authority,queued,captain,firstmate,CFVC landing-authority decision,none,captain,needs the captain
  cfvc-review-decision-wake-queue,queued,captain,firstmate,CFVC wake-queue decision,none,captain,needs the captain
  cfvc-operator-removal-work,queued,task,firstmate,Operator removal work,none,"-","-"
  cfvc-review-decision-operator-removal,done,captain,firstmate,CFVC operator-removal decision,none,captain,needs the captain
help[2]:
  - Run `tasks-axi show <id> --file=/tmp/no-mistakes-evidence/01KZBJMKGZ2JEPTEBDAP862AD0/demo-home/data/backlog.md` for full notes on a task
  - Run `tasks-axi ready --file=/tmp/no-mistakes-evidence/01KZBJMKGZ2JEPTEBDAP862AD0/demo-home/data/backlog.md` to see unblocked queued work
Full task bodies remain available on demand: tasks-axi show <id> --full when compatible tasks-axi is available, or data/backlog.md.

schema=fm-ruling-index.v1
verdict=NO_RULING_READ
unreadable=captain-rulings-2026-08-06.md
reason=symlinked-ruling-document-not-read

Work under way (state/*.meta)
Evidence: Red-capability / mutation harness output

=== CONTROL: unmutated scripts, 16 cases === ok - an interrupt terminates the resolve, leaves the hold open, and still cleans up suite exit: 0 (zero == green on real code) === MUTANT: doc_class no longer refuses a commission === not ok - the commission document must be classified commission suite exit: 1 (non-zero == the suite caught the break) === MUTANT: hold identifier matched as a bare substring === not ok - an unmatched hold must be reported with ruling_file=none suite exit: 1 (non-zero == the suite caught the break) === MUTANT: verdict token matched unanchored === not ok - acceptable must not read as an explicit verdict token suite exit: 1 (non-zero == the suite caught the break) === MUTANT: verdict scan windows across neighbouring table rows === not ok - removing the verdict token must flip eligibility to escalate, got: closable-if-graded-rules suite exit: 1 (non-zero == the suite caught the break) === MUTANT: --from-ruling stamped instead of verified === not ok - resolve must refuse a provenance that fails the closure test suite exit: 1 (non-zero == the suite caught the break) === RESTORED: unmutated scripts === ok - an interrupt terminates the resolve, leaves the hold open, and still cleans up suite exit: 0

=== CONTROL: unmutated scripts, 16 cases ===
ok - an interrupt terminates the resolve, leaves the hold open, and still cleans up
  suite exit: 0   (zero == green on real code)

=== MUTANT: doc_class no longer refuses a commission ===
not ok - the commission document must be classified commission
  suite exit: 1   (non-zero == the suite caught the break)

=== MUTANT: hold identifier matched as a bare substring ===
not ok - an unmatched hold must be reported with ruling_file=none
  suite exit: 1   (non-zero == the suite caught the break)

=== MUTANT: verdict token matched unanchored ===
ok - a hold identifier matches only when it is bounded by delimiters
not ok - **acceptable** must not read as an explicit verdict token, got: schema=fm-ruling-index.v1
  suite exit: 1   (non-zero == the suite caught the break)

=== MUTANT: verdict scan windows across neighbouring table rows ===
ok - eligibility separates ruling, commission, and durable-source silence
not ok - removing the verdict token must flip eligibility to escalate, got: closable-if-graded-rules
  suite exit: 1   (non-zero == the suite caught the break)

=== MUTANT: --from-ruling stamped instead of verified ===
ok - closure requires a ruling, a verbatim identifier, a verdict token, and a rules grade
not ok - resolve must refuse a provenance that fails the closure test
  suite exit: 1   (non-zero == the suite caught the break)

=== RESTORED: unmutated scripts ===
ok - an interrupt terminates the resolve, leaves the hold open, and still cleans up
  suite exit: 0
Evidence: The grading envelope an agent actually receives, and the closure verdict
$ fm-ruling-reconcile.sh propose
schema=fm-ruling-index.v1
authority=none
grading=required
closure_rule=verbatim-identifier-in-ruling AND explicit-verdict-token AND grade=rules
quoted_fields=excerpt,verdict_token
stale_holds[4]{hold_id,ruling_file,ruling_line,excerpt,grade,doc_class,verdict_token,verdict_line,eligibility}:
cfvc-review-decision-index-schema,captain-rulings-2026-08-06.md,8,"| **C2** | `cfvc-review-decision-index-schema` | Replace the fictional cap of 3 with an **enforced ceiling of 10**. |",ungraded,ruling,"none",0,escalate
cfvc-review-decision-landing-authority,cfvc-remediation-commission.md,5,"- `cfvc-review-decision-landing-authority` - **APPROVED** for investigation only.",ungraded,commission,"**APPROVED**",5,escalate
cfvc-review-decision-operator-removal,captain-rulings-2026-08-06.md,7,"| **C1** | `cfvc-review-decision-operator-removal` | **APPROVED** - remove the operator as scoped. |",ungraded,ruling,"**APPROVED**",7,closable-if-graded-rules
cfvc-review-decision-wake-queue,none,0,"unmatched: no ruling document names this hold (queued)",ungraded,none,"none",0,escalate

$ fm-decision-hold.sh resolve cfvc-review operator-removal ... --from-ruling cfvc-remediation-commission.md:5
fm-decision-hold: --from-ruling cfvc-remediation-commission.md:5 fails the captain closure test: not-a-ruling-document
exit=1
$ tasks-axi show cfvc-review-decision-operator-removal --full # after the refusal
state: queued
held: yes
hold_kind: captain
body: "Origin: cfvc-review\nDecision key: operator-removal"

$ fm-decision-hold.sh resolve cfvc-review operator-removal ... --from-ruling captain-rulings-2026-08-06.md:7
resolved: cfvc-review-decision-operator-removal -> cfvc-operator-removal-work
exit=0
$ tasks-axi show cfvc-review-decision-operator-removal --full # after the verified close
state: done
held: no
body: "...Routed identities: cfvc-operator-removal-work\nRuling provenance: captain-rulings-2026-08-06.md:7\n\nCaptain decision:\nRemove the operator as scoped...."

$ tasks-axi list --kind captain --state queued # the answered decision is no longer pending
count: 3

$ fm-session-start.sh # FLEET STATE section
RULING_RECONCILE: 3 open captain decision(s); 0 ruling excerpt(s) await grading before any closure
Evidence: Persisted derived index (fm-ruling-index.v1) after the walkthrough
--- persisted derived index ---

== state/ruling-index/index.meta ==
schema=fm-ruling-index.v1
derived=true
authority=none
generated=2026-08-06T00:00:00Z
corpus_root=/tmp/no-mistakes-evidence/01KZBJMKGZ2JEPTEBDAP862AD0/demo-home/data
hold_fingerprint=3f36d78b1692f14e1c800ecd62d5638faa3cd565891753fb1ea359f3a84aeda6
ruling_fingerprint=0d003d3fc4b03973556300ddc8915bde9c2e2c25054d883eb26d4bc1bc9add18
open_holds=3
ruling_documents=2
verdict_window=12

== state/ruling-index/holds.tsv ==
cfvc-review-decision-index-schema	queued
cfvc-review-decision-landing-authority	queued
cfvc-review-decision-wake-queue	queued

== state/ruling-index/rulings.tsv ==
captain-rulings-2026-08-06.md	ruling	8c630b0facbacd17423d5e4b44defcf1dfac42ed235b5c12db6e48738ef94bc4
cfvc-remediation-commission.md	commission	c1a4c23cff368b151bfa307e89b0c2de27c117ba6e072150b1f0f5148b6aff34

== state/ruling-index/matches.tsv ==
cfvc-review-decision-index-schema	captain-rulings-2026-08-06.md	8	ruling	none	0	escalate	| **C2** | `cfvc-review-decision-index-schema` | Replace the fictional cap of 3 with an **enforced ceiling of 10**. |
cfvc-review-decision-landing-authority	cfvc-remediation-commission.md	5	commission	**APPROVED**	5	escalate	- `cfvc-review-decision-landing-authority` — **APPROVED** for investigation only.
cfvc-review-decision-wake-queue	none	0	none	none	0	escalate	unmatched: no ruling document names this hold (queued)
Evidence: Reproducible end-to-end demo script
#!/usr/bin/env bash
# End-to-end operator walkthrough of CFVC-01: reconcile ruling documents against
# open captain decision holds. Drives the real bin/ scripts over a realistic
# firstmate home, exactly as an operator would from a session.
set -u

ROOT=${ROOT:?set ROOT to the firstmate worktree}
EVID=${EVID:?set EVID to the evidence directory}
HOME_DIR="$EVID/demo-home"
RECONCILE="$ROOT/bin/fm-ruling-reconcile.sh"
HOLD="$ROOT/bin/fm-decision-hold.sh"

rm -rf "$HOME_DIR"
mkdir -p "$HOME_DIR/data/cfvc-review" "$HOME_DIR/state" "$HOME_DIR/fakebin"
cp "$ROOT/.tasks.toml" "$HOME_DIR/.tasks.toml"
printf '## In flight\n\n## Queued\n\n## Done\n' > "$HOME_DIR/data/backlog.md"
printf 'CFVC review evidence.\n' > "$HOME_DIR/data/cfvc-review/report.md"
for t in tmux treehouse no-mistakes gh gh-axi; do
  printf '#!/usr/bin/env bash\nexit 0\n' > "$HOME_DIR/fakebin/$t"
  chmod +x "$HOME_DIR/fakebin/$t"
done

# The installed tasks-axi is 0.2.3; this base's floor is 0.2.4. Report the floor
# and delegate every real command to the real binary so the code path runs for
# real rather than being skipped.
TASKS_AXI_BIN=$(command -v tasks-axi)
# shellcheck disable=SC1091
. "$ROOT/bin/fm-tasks-axi-lib.sh"
cat > "$HOME_DIR/fakebin/tasks-axi" <<EOF
#!/usr/bin/env bash
if [ "\${1:-}" = --version ]; then printf '%s\n' "$FM_TASKS_AXI_MIN"; exit 0; fi
exec "$TASKS_AXI_BIN" "\$@"
EOF
chmod +x "$HOME_DIR/fakebin/tasks-axi"

# --- the corpus a real home would have --------------------------------------

cat > "$HOME_DIR/data/captain-rulings-2026-08-06.md" <<'EOF'
# Captain rulings — 2026-08-06

**Captain, 2026-08-06, choosing option (c) verbatim:**

| ID | Register identity | Ruling |
|---|---|---|
| **C1** | `cfvc-review-decision-operator-removal` | **APPROVED** — remove the operator as scoped. |
| **C2** | `cfvc-review-decision-index-schema` | Replace the fictional cap of 3 with an **enforced ceiling of 10**. |
EOF

cat > "$HOME_DIR/data/cfvc-remediation-commission.md" <<'EOF'
# CFVC remediation commission

Commissioned work, not a ruling.

- `cfvc-review-decision-landing-authority` — **APPROVED** for investigation only.
EOF

env_home() {
  env PATH="$HOME_DIR/fakebin:$PATH" FM_ROOT_OVERRIDE="$ROOT" FM_HOME="$HOME_DIR" \
    FM_DATA_OVERRIDE="$HOME_DIR/data" FM_STATE_OVERRIDE="$HOME_DIR/state" \
    FM_RULING_NOW=2026-08-06T00:00:00Z "$@"
}

say() { printf '\n\033[1m$ %s\033[0m\n' "$*"; }

# --- 1. four captain decisions go on hold ------------------------------------

say "fm-decision-hold.sh hold cfvc-review <key> --title ... --reason ...   (x4)"
for k in operator-removal index-schema landing-authority wake-queue; do
  env_home "$HOLD" hold cfvc-review "$k" \
    --title "CFVC $k decision" --reason "needs the captain" --repo firstmate
done

say "tasks-axi show cfvc-review-decision-operator-removal --full"
(cd "$HOME_DIR" && env PATH="$HOME_DIR/fakebin:$PATH" tasks-axi show cfvc-review-decision-operator-removal --full)
printf '\n# RETIREMENT CHECK: the hold body no longer asserts its own state.\n'
if (cd "$HOME_DIR" && env PATH="$HOME_DIR/fakebin:$PATH" tasks-axi show cfvc-review-decision-operator-removal --full) \
     | grep -q 'awaiting captain decision'; then
  printf '  FAIL: "State: awaiting captain decision." is still in the body\n'
else
  printf '  OK: "State: awaiting captain decision." is gone; held/hold_kind own the state\n'
fi

say "tasks-axi list --kind captain --state queued"
(cd "$HOME_DIR" && env PATH="$HOME_DIR/fakebin:$PATH" tasks-axi list --kind captain --state queued)

# --- 2. the deterministic prefilter ------------------------------------------

say "fm-ruling-reconcile.sh scan"
env_home "$RECONCILE" scan

say "fm-ruling-reconcile.sh scan     # unchanged corpus + unchanged holds"
env_home "$RECONCILE" scan

say "fm-ruling-reconcile.sh propose"
env_home "$RECONCILE" propose

# --- 3. the captain's two-condition closure test ------------------------------

RULING=captain-rulings-2026-08-06.md
COMMISSION=cfvc-remediation-commission.md
line_of() { grep -nF -- "$1" "$HOME_DIR/data/$2" | head -1 | cut -d: -f1; }

L_OK=$(line_of 'cfvc-review-decision-operator-removal' "$RULING")
L_NOVERDICT=$(line_of 'cfvc-review-decision-index-schema' "$RULING")
L_COMM=$(line_of 'cfvc-review-decision-landing-authority' "$COMMISSION")

say "fm-ruling-reconcile.sh closure-test cfvc-review-decision-operator-removal --ruling $RULING --line $L_OK --grade rules"
env_home "$RECONCILE" closure-test cfvc-review-decision-operator-removal --ruling "$RULING" --line "$L_OK" --grade rules

say "fm-ruling-reconcile.sh closure-test cfvc-review-decision-operator-removal --ruling $RULING --line $L_OK --grade cites"
env_home "$RECONCILE" closure-test cfvc-review-decision-operator-removal --ruling "$RULING" --line "$L_OK" --grade cites

say "fm-ruling-reconcile.sh closure-test cfvc-review-decision-index-schema --ruling $RULING --line $L_NOVERDICT --grade rules"
env_home "$RECONCILE" closure-test cfvc-review-decision-index-schema --ruling "$RULING" --line "$L_NOVERDICT" --grade rules

say "fm-ruling-reconcile.sh closure-test cfvc-review-decision-landing-authority --ruling $COMMISSION --line $L_COMM --grade rules"
env_home "$RECONCILE" closure-test cfvc-review-decision-landing-authority --ruling "$COMMISSION" --line "$L_COMM" --grade rules

# --- 4. closure is performed by fm-decision-hold.sh, and verified -------------

printf 'Remove the operator as scoped.\n' > "$HOME_DIR/decision.txt"
(cd "$HOME_DIR" && env PATH="$HOME_DIR/fakebin:$PATH" tasks-axi add cfvc-operator-removal-work "Operator removal work" --repo firstmate >/dev/null)
(cd "$HOME_DIR" && env PATH="$HOME_DIR/fakebin:$PATH" tasks-axi block cfvc-operator-removal-work --by cfvc-review-decision-operator-removal >/dev/null)

say "fm-decision-hold.sh resolve cfvc-review operator-removal ... --from-ruling $COMMISSION:$L_COMM"
env_home "$HOLD" resolve cfvc-review operator-removal \
  --decision-file "$HOME_DIR/decision.txt" --routed-to cfvc-operator-removal-work \
  --from-ruling "$COMMISSION:$L_COMM"
printf '  exit=%s\n' "$?"

say "tasks-axi show cfvc-review-decision-operator-removal --full   # after the refusal"
(cd "$HOME_DIR" && env PATH="$HOME_DIR/fakebin:$PATH" tasks-axi show cfvc-review-decision-operator-removal --full) | grep -E '^ +(id|state|held|hold_kind|body):'

say "fm-decision-hold.sh resolve cfvc-review operator-removal ... --from-ruling $RULING:$L_OK"
env_home "$HOLD" resolve cfvc-review operator-removal \
  --decision-file "$HOME_DIR/decision.txt" --routed-to cfvc-operator-removal-work \
  --from-ruling "$RULING:$L_OK"
printf '  exit=%s\n' "$?"

say "tasks-axi show cfvc-review-decision-operator-removal --full   # after the verified close"
(cd "$HOME_DIR" && env PATH="$HOME_DIR/fakebin:$PATH" tasks-axi show cfvc-review-decision-operator-removal --full)

say "tasks-axi list --kind captain --state queued   # the answered decision is no longer pending"
(cd "$HOME_DIR" && env PATH="$HOME_DIR/fakebin:$PATH" tasks-axi list --kind captain --state queued)

say "fm-ruling-reconcile.sh scan     # the register drains"
env_home "$RECONCILE" scan

say "fm-ruling-reconcile.sh status"
env_home "$RECONCILE" status

# --- 5. what the operator sees at session start --------------------------------

say "fm-session-start.sh   # FLEET STATE section, as an operator sees it"
(cd "$HOME_DIR" && env_home "$ROOT/bin/fm-session-start.sh" 2>&1) \
  | sed -n '/^FLEET STATE$/,/^Work under way/p'

# --- 6. the empty-set law ------------------------------------------------------

say "rm ruling; ln -s /dev/null ruling; fm-ruling-reconcile.sh scan"
rm -f "$HOME_DIR/data/$RULING"
ln -s /dev/null "$HOME_DIR/data/$RULING"
env_home "$RECONCILE" scan --rebuild
printf '  exit=%s\n' "$?"

say "fm-session-start.sh   # an unread ruling is reported, never absorbed as 'unruled'"
(cd "$HOME_DIR" && env_home "$ROOT/bin/fm-session-start.sh" 2>&1) \
  | sed -n '/^FLEET STATE$/,/^Work under way/p'
Evidence: Reproducible red-capability harness
#!/usr/bin/env bash
# Red-capability check: break the production scripts five ways and confirm the
# suite REJECTS each break. A suite that only ever passes proves nothing.
set -u
M=${M:?set M to the mutant repo copy}
MUTATE=${MUTATE:?set MUTATE to mutate.py}
S="$M/bin/fm-ruling-reconcile.sh"
H="$M/bin/fm-decision-hold.sh"
cp "$S" "$M/.pristine-reconcile"
cp "$H" "$M/.pristine-hold"

restore() { cp "$M/.pristine-reconcile" "$S"; cp "$M/.pristine-hold" "$H"; }

suite() { timeout 600 bash "$M/tests/fm-ruling-reconcile.test.sh" 2>&1; }

run_mutant() {  # <label> <file> <old> <new>
  restore
  python3 "$MUTATE" "$2" "$3" "$4" || { printf 'MUTATION FAILED: %s\n' "$1"; return; }
  printf '\n=== MUTANT: %s ===\n' "$1"
  suite | grep -E '^not ok|^ok' | tail -2
  printf '  suite exit: %s   (non-zero == the suite caught the break)\n' "${PIPESTATUS[0]}"
}

printf '=== CONTROL: unmutated scripts, 16 cases ===\n'
suite | tail -1
printf '  suite exit: %s   (zero == green on real code)\n' "${PIPESTATUS[0]}"

run_mutant "doc_class no longer refuses a commission" "$S" \
  "    *commission.md) printf 'commission\\n'; return ;;" \
  "    *never-matches-anything) printf 'commission\\n'; return ;;"

run_mutant "hold identifier matched as a bare substring" "$S" \
  "  printf '(^|[^A-Za-z0-9._-])%s([^A-Za-z0-9._-]|[.]([[:space:]]|\$)|\$)' \"\$esc\"" \
  "  printf '%s' \"\$esc\""

run_mutant "verdict token matched unanchored" "$S" \
  "VERDICT_RE='(^|[^A-Za-z])('\"\$VERDICT_TOKENS\"')(E?[DS]|ING)?([^A-Za-z]|\$)'" \
  "VERDICT_RE='('\"\$VERDICT_TOKENS\"')'"

run_mutant "verdict scan windows across neighbouring table rows" "$S" \
  "    '|'*|' '*'|'*|\$'\\t'*'|'*)" \
  "    'NEVER-MATCHES-A-TABLE-ROW')"

run_mutant "--from-ruling stamped instead of verified" "$H" \
  "    if [ \"\$closure_verdict\" != permitted ]; then" \
  "    if false; then"

restore
printf '\n=== RESTORED: unmutated scripts ===\n'
suite | tail -1
printf '  suite exit: %s\n' "${PIPESTATUS[0]}"
- Outcome: ⚠️ 2 infos across 1 run (9m21s)

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

⚠️ **Review** - 1 info
  • 🚨 bin/fm-decision-hold.sh:431 - The --from-ruling verification accepts the provenance whenever the substring closure=permitted appears anywhere in the merged stdout+stderr of closure-test, and closure-test echoes the caller-supplied path back as ruling_file=&lt;path&gt;. Confirmed by running the real script: closure-test &lt;hold&gt; --ruling &#39;no-such-closure=permitted.md&#39; --line 1 --grade rules prints closure=escalate and reason=NO_RULING_READ, yet case &#34;$closure_out&#34; in *&#34;closure=permitted&#34;* matches on the echoed ruling_file= line, so resolve --from-ruling &#39;no-such-closure=permitted.md:1&#39; would proceed and stamp Ruling provenance: onto the hold for a document that does not exist. This defeats the stated "verified rather than trusted" property at the exact point closure is granted. Fix: match the verdict as a whole line, e.g. printf &#39;%s\n&#39; &#34;$closure_out&#34; | grep -qxF &#39;closure=permitted&#39;.
  • 🚨 bin/fm-ruling-reconcile.sh:521 - cmd_closure_test resolves --ruling with no containment check against the corpus root: an absolute path is used verbatim, and a relative path is joined to the root without normalisation, so ../../../tmp/forged/captain-rulings-forged.md escapes. Confirmed on the real script: a file written to /tmp named captain-rulings-forged.md returns doc_class=ruling, verbatim_identifier=yes, verdict_token=**APPROVED**, closure=permitted for both the absolute and the traversal form. build_ruling_inventory (lines 262-269) already treats an out-of-corpus ruling document as a terminal NO_RULING_READ refusal, and the commit message states that law explicitly, but the path that actually authorises a closure does not enforce it — so a caller can satisfy the captain's condition 1 with a document it authored anywhere on disk. Fix: resolve the path with pwd -P on its directory and refuse anything not under the resolved corpus root, mirroring lines 262-269.
  • ⚠️ bin/fm-ruling-reconcile.sh:546 - Condition 1 ("names the hold identifier VERBATIM") is implemented as a bare substring test — case &#34;$text&#34; in *&#34;$hold&#34;* here, and grep -nF -- &#34;$hold&#34; &#34;$path&#34; at line 402 for the scan. Hold ids are &lt;origin&gt;-decision-&lt;key&gt;, so any id that is a prefix of another id matches the wrong row. Confirmed on the real script: with a ruling row naming sample-review-decision-alpha-two, closure-test sample-review-decision-alpha --line &lt;that row&gt; --grade rules returns verbatim_identifier=yes, verdict_token=**APPROVED**, closure=permitted — so hold alpha can be closed on the strength of a row that rules alpha-two. Fix: require the match to be delimited, e.g. grep -nE &#34;(^|[^A-Za-z0-9._-])${hold}([^A-Za-z0-9._-]|$)&#34; and the equivalent guard on the cited line.
  • ⚠️ bin/fm-ruling-reconcile.sh:102 - The comment at lines 87-89 states "Common English words were therefore removed after measurement: OPTION ... and RUN matches ordinary instruction prose", but RUN is still in VERDICT_TOKENS at line 102. Because the tokens are applied as an unanchored case-insensitive grep -inE over the emphasised span, RUN matches any emphasised word containing "run". Confirmed on the real script: a row reading | **A2** | + backtick-id + | The **Runtime** owns work identity. | returns verdict_token=**Runtime** and closure=permitted — an emphasised noun this codebase uses constantly is accepted as an explicit verdict token, which is the condition-2 vacuity the header warns against. PARK (**sparkline**), ACCEPT (**acceptable**) and ADOPT (**adoption**) have the same shape. Please confirm whether RUN was meant to be dropped (as the comment says) and whether the tokens should be word-anchored; either the code or the comment is wrong.
  • ⚠️ bin/fm-ruling-reconcile.sh:219 - collect_holds discards both the stderr and the exit status of each tasks-axi list call (2&gt;/dev/null) || continue), so a reader that fails — an older build that rejects --kind/--state, or any runtime error — yields an empty holds.tsv. The scan then publishes an index with open_holds=0, and emit_summary's quiet path returns silently at line 442, so session start reports nothing at all. That is the same "a broken reader presents as an empty answer" failure the empty-set law refuses for ruling documents, applied to the other input. Unlike fm-decision-hold.sh, this script only checks command -v tasks-axi (line 345) and never the version floor. Fix: fail loudly (non-zero, with the captured stderr) when a list invocation exits non-zero, rather than treating it as "no open holds".
  • ⚠️ bin/fm-ruling-reconcile.sh:195 - The header (lines 180-189) and docs/decision-hold-lifecycle.md:46 both state that class is decided "structurally rather than by loose substring" and that "a ruling declares itself in the &lt;who&gt;-rulings-&lt;when&gt; prefix form", but the implemented glob is *ruling-*|*rulings-*, which matches anywhere in the basename and is evaluated before the commission suffix check. Any document whose name contains ruling- is therefore classified ruling even when it also ends in commission.md, making the commission branch unreachable for such names and letting a commission satisfy the captain's condition 1 — the exact inversion the two-position design exists to prevent. No file in the current corpus triggers it, so please confirm whether the ruling test should be anchored to the documented prefix form (e.g. ruling-*|*-ruling-*|*-rulings-* evaluated only at the leading segment) or whether the unanchored form is deliberate.
  • ℹ️ bin/fm-ruling-reconcile.sh:308 - verdict_in_window's non-table branch loops up to VERDICT_WINDOW + 1 times, and each iteration re-reads the whole file with sed -n &#34;${n}p&#34; and spawns two greps — roughly 39 processes per matched line, with the file scanned from the start 13 times. Since MAX_MATCHES is 20 per hold per document and this runs inline in fm-session-start.sh on every hold or corpus change, the cost scales as holds x documents x 20. A single sed -n &#34;${line},${end}p&#34; (or one awk pass) piped into the existing grep chain gives identical output for one file read and two processes.
  • ℹ️ bin/fm-ruling-reconcile.sh:485 - cmd_propose declares a fixed nine-field comma-separated row but emits the excerpt ($8) and the verdict token ($5) unescaped in the middle of it. Excerpts are markdown table rows, which routinely contain commas (the project's own fixture excerpt is | **A1** | sample-review-decision-alpha | **APPROVED** - build it as specified. |), so any consumer that splits on commas mis-aligns every field after excerpt. tsv_cell already normalises tabs and newlines but not commas. Moving the excerpt to the last field, or quoting it, would keep the declared header honest; flagging rather than fixing because the envelope field order is specified in the intent.
  • ℹ️ docs/scripts.md:26 - docs/scripts.md is the bin/ toolbelt index and gained no row for the new operator-invocable fm-ruling-reconcile.sh, even though fm-decision-hold.sh (line 26) is listed and the two are now a pair. Noting rather than asking, since 24 other bin scripts are also absent from that table, so completeness is evidently not an enforced invariant.

🔧 Fix: harden ruling closure guard against forged provenance and vacuous verdicts
3 issues (1 warning, 2 infos) still open:

  • ⚠️ bin/fm-ruling-reconcile.sh:175 - hold_pattern uses [^A-Za-z0-9._-] as the trailing boundary, which includes ., so a hold identifier that ends a sentence is not recognised as a verbatim naming. Confirmed on the real script: a ruling line reading The captain has **RESOLVED** the question of sample-review-decision-theta. returns verbatim_identifier=no / reason=identifier-not-verbatim-on-cited-line, while the same identifier followed by a comma returns closure=permitted. The scan shares this pattern, so such a hold gets the unmatched: no ruling document names this hold row instead of an excerpt - the false-unmatched this increment exists to eliminate, now silent. Including . is defensible because validate_slug permits dots inside ids, so this is a precision/recall call rather than a plain bug: a targeted fix is to accept a trailing . only when it is itself followed by whitespace or end-of-line, e.g. ([^A-Za-z0-9._-]|[.]([[:space:]]|$)|$), which keeps a.b from matching a.b.c. Please confirm which behaviour you want; escalation is the safe direction either way.
  • ℹ️ bin/fm-ruling-reconcile.sh:240 - The intent states as a deliberate design decision: "Prefix form wins, then suffix form, then directory." The hardened doc_class tests the commission suffix first and treats it as decisive, then the anchored ruling prefix, then the directory - the inverse order. This is observable, not just wording: rulings-2026-08-06-commission.md now classifies as commission where the intent's order would make it ruling. The real-corpus shapes the intent names are unaffected (captain-rulings-2026-08-04-commission-32.md still classifies ruling, cfvc-remediation-commission.md still commission, cfvc-commission-approval.md under captain-rulings-2026-08-06/ still ruling), and the inversion only ever biases toward commission, which escalates - consistent with the same paragraph's "Misclassification is biased toward escalation on purpose." docs/decision-hold-lifecycle.md:46 was updated to describe the new order, so the code and the one-owner doc agree; only the intent text still states the old order. Please confirm the intent sentence should be updated rather than the code.
  • ℹ️ bin/fm-decision-hold.sh:426 - The new closure_err=$(mktemp ...) is the only temp file this script creates, and fm-decision-hold.sh has no trap. Both normal paths rm -f it, but an interrupt during the fm-ruling-reconcile.sh closure-test subprocess - the one command between creation and removal - leaves fm-decision-hold-closure.XXXXXX behind in TMPDIR. Adding trap &#39;rm -f &#34;$closure_err&#34;&#39; EXIT HUP INT TERM around the block (or reusing the pattern fm-ruling-reconcile.sh:110 already uses for its work directory) closes it without changing any behaviour.

🔧 Fix: accept sentence-ending hold identifiers without weakening the boundary
2 issues (1 warning, 1 info) still open:

  • ⚠️ bin/fm-decision-hold.sh:72 - trap cleanup_closure_err EXIT HUP INT TERM registers a handler that neither exits nor re-raises, so bash runs it and then resumes the script instead of terminating. Confirmed by running an equivalent script: after kill -INT $$ the next statement still executes and the script exits 0. fm-decision-hold.sh mutates backlog state in command_resolve (dependency edges, hold body, then the close), and its own header states "A failure before the final step leaves the captain hold open" - with this trap, Ctrl-C mid-resolve is swallowed and the resolve runs to completion and closes the hold, which is the opposite of that documented property. bin/fm-ruling-reconcile.sh:121 has the identical trap cleanup_work EXIT HUP INT TERM shape, also added on this branch. Fix: keep the EXIT trap for cleanup and make the signal traps terminate, e.g. trap cleanup_closure_err EXIT plus trap &#39;cleanup_closure_err; exit 130&#39; INT and trap &#39;cleanup_closure_err; exit 143&#39; HUP TERM; the EXIT trap then re-runs harmlessly because the handler clears the variable.
  • ℹ️ bin/fm-ruling-reconcile.sh:222 - Bookkeeping only, already adjudicated last round - not a request to change code. The intent still reads "Prefix form wins, then suffix form, then directory", while doc_class tests the commission suffix first (observably: rulings-2026-01-03-commission.md classifies commission, verified on the real script). You ruled the code correct and the prose stale, and that was carried out: the header comment now states "THE IMPLEMENTED ORDER IS SUFFIX, THEN PREFIX, THEN DIRECTORY, AND IT MUST NOT BE REORDERED" with the fail-safe rationale, docs/decision-hold-lifecycle.md:46 matches, a regression case pins the order, and a repo-wide grep finds no remaining "prefix form wins" prose. The only copy still carrying the old sentence is the --intent argument text itself, which lives outside the diff; updating it would stop a future review round re-flagging the same thing.

🔧 Fix: terminate on interrupt instead of absorbing the signal
1 info still open:

  • ℹ️ tests/fm-ruling-reconcile.test.sh:711 - The safety tripwire in install_reconcile_shim greps the real bin/fm-ruling-reconcile.sh for the literal header phrase fm-ruling-reconcile.sh - deterministic. The guard itself is right - cat &gt; onto a symlink would truncate a tracked repo file - but keying it to prose means rewording that header comment fails the suite with the actively misleading message "the stub overwrote the real bin/fm-ruling-reconcile.sh" when nothing was overwritten. It also brushes against the project guideline the intent cites, that tests exercise behaviour through an executable interface and never assert implementation-source bytes. The preceding rm -f plus [ ! -L ... ] check at lines 709-710 already prevents the truncation, so the tripwire can assert the same thing through the interface instead, e.g. &#34;$ROOT/bin/fm-ruling-reconcile.sh&#34; schema &gt;/dev/null || fail ... - I confirmed that invocation exits 0 against the real script and is immune to comment edits.
⚠️ **Test** - 2 infos
  • ℹ️ tests/fm-decision-hold-lifecycle.test.sh - tests/fm-decision-hold-lifecycle.test.sh cannot complete on this machine: the installed tasks-axi is 0.2.3 and this base's floor (bin/fm-tasks-axi-lib.sh FM_TASKS_AXI_MIN) is 0.2.4, so the run stops at 'compatible tasks-axi is required'. Not caused by this change - with a version-floor shim that reports 0.2.4 and delegates every real command to the installed binary, all 9 cases pass, confirming the fm-decision-hold.sh edits introduce no regression. Reviewers running this suite locally will see the same refusal until tasks-axi is upgraded.
  • ℹ️ tests/fm-session-start.test.sh - tests/fm-session-start.test.sh fails on 'MISSING diagnostic did not appear at all' (the case asserting a 'MISSING: node' line in the digest). Verified pre-existing and unrelated to this change: I reverse-applied the fm-session-start.sh hunk from this diff, reran the suite, and observed the identical single failure, then restored the worktree clean. The new RULING_RECONCILE surface renders correctly in the FLEET STATE section in the end-to-end run.
  • bash tests/fm-ruling-reconcile.test.sh - all 16 cases pass
  • Red-capability harness: five exact-string mutations applied to isolated copies of bin/fm-ruling-reconcile.sh and bin/fm-decision-hold.sh (commission no longer refused by doc_class, hold identifier matched as bare substring, verdict token matched unanchored, table-row special case removed so the windowed scan bleeds across rows, --from-ruling closure verdict check disabled) - each mutant rejected by the suite, control green before and after
  • End-to-end operator walkthrough over a realistic home: fm-decision-hold.sh hold x4, tasks-axi show --full, tasks-axi list --kind captain --state queued, fm-ruling-reconcile.sh scan (delta then no_delta), fm-ruling-reconcile.sh propose, fm-ruling-reconcile.sh closure-test x4 (rules/cites/no-verdict/commission), fm-decision-hold.sh resolve --from-ruling refused then accepted, fm-ruling-reconcile.sh status, fm-session-start.sh, and the symlinked-ruling empty-set case
  • bash tests/fm-decision-hold-lifecycle.test.sh - refuses on the tasks-axi version floor; rerun with a floor shim (tasks-axi --version -> 0.2.4, all real commands delegated) passes all 9 cases
  • bash tests/fm-session-start.test.sh at target, then again after git diff 2cf0283..93a6f75 -- bin/fm-session-start.sh | git apply -R - identical single failure, worktree restored clean
  • bash tests/fm-documentation-audiences.test.sh and bash bin/fm-doc-audience-check.sh - docs surfaces added by this change are classified and their local links resolve
🔧 **Document** - 2 issues found → auto-fixed ✅
  • ℹ️ docs/decision-hold-lifecycle.md:78 - Out-of-scope consolidation worth a follow-up: docs/decision-hold-lifecycle.md is classified maintainer-architecture in docs/documentation-audiences.json, but its 'Verification record' section now carries roughly 25 lines of dated mutation-testing evidence, while the repo's maintainer-verification home is docs/verification/ (eight records, all classified maintainer-verification) and .agents/skills/firstmate-coding-guidelines/SKILL.md places active empirical evidence there. The mixing predates this change (the file already owned three 'Verification date:' lines), so relocating it would be a documentation restructure beyond repairing this change's staleness. Proposal: move the reconciliation, closure-gate, boundary, and interrupt evidence into a new docs/verification/decision-holds.md and leave a pointer, as one dedicated follow-up.
  • ℹ️ docs/decision-hold-lifecycle.md:87 - Judgment call I did not resolve: the verification record says 'Two of those controls found real defects that the green suite alone had hidden' and names the symlinked-corpus walk and the two grep -qv assertions, but the same file documents a third defect found the same way at line 53 (a windowed verdict scan attributing one table row's verdict to a row six lines above), whose mutation-controlled case is test_table_row_does_not_inherit_the_next_rows_verdict in the same eight. Only the author can confirm whether that third defect was surfaced by the red-capable control or by separate corpus measurement, so I left the count alone rather than asserting an attribution the repo does not establish.

🔧 Fix: attribute each reconcile defect to its discovery method
✅ Re-checked - no issues remain.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

…olds

CFVC-01. A captain answers a decision in a ruling document and the hold that
asked the question stays queued, so the captain is re-asked answered questions
and investigations inherit stale premises. Nothing read a ruling back onto a
hold; the reconciliation was done by hand, repeatedly.

Adds bin/fm-ruling-reconcile.sh: a deterministic, model-free prefilter that
matches open captain holds against the ruling documents naming them, modelled on
bin/fm-research-scan.sh's derived-index discipline. It produces the candidate
set, the bounded excerpt, and an eligibility verdict, and reaches a no_delta
terminal without opening a ruling document when nothing that could change the
answer has changed. It never grades and never closes.

Enforces the captain's closure ruling of 2026-08-06 (option c): a hold may be
closed on the strength of a ruling only when the ruling names the identifier
verbatim AND carries an explicit verdict token. Both conditions are necessary
and NOT sufficient - closure additionally needs a caller grade of `rules` and a
separate fm-decision-hold.sh call. A hold named in a commission is never
eligible. resolve --from-ruling <path>:<line> re-verifies both conditions before
any mutation and refuses rather than stamping an unverified provenance.

Retires, in this same change:
- "State: awaiting captain decision." from the hold body. A hold reporting its
  own state is a claim, not a verdict, and it outlived the answer. The
  structured state/held/hold_kind fields are now the only state owner.
- The recurring manual archaeology of re-reading ruling prose per bearings and
  per investigation.

Completion criterion (a) is REPLACED, and this is why. It required reproducing
"24 ruled / 2 explicitly unruled / 9 unmatched" over the live home. That state
no longer exists and cannot be reproduced: the backfill was already performed by
hand before this increment ran, which corrected the measurement to 27 of 36 and
then drained the register to zero open captain holds. Reproducing a decayed
snapshot is not available at any effort. The replacement is stronger because it
cannot decay: a sealed fixture corpus drives the ruling, commission, unmatched
and no-verdict cases apart in one scan, and every assertion is proven able to
reject its defect. Criteria (b) and (c) are met as written.

Certification. Each of the eight cases was run against a deliberately broken
copy of the production script and observed failing, then against the real code
and observed passing. Two controls found real defects a green suite had hidden:
a `find -type f` corpus walk that silently dropped a symlinked ruling document
instead of refusing it, and two `grep -qv` assertions that could never fail on
multi-line output. A third defect was found by measurement against the real
corpus: a windowed verdict scan attributed one table row's verdict to a
different row six lines above it, so a table row is now scanned alone.

The verdict vocabulary is tuned for precision, not recall, and its coverage is
reported rather than tuned: 15 of 24 rows on the flagship ruling table are
eligible, and the nine that state a verdict without emphasis escalate. Widening
the vocabulary until it reproduced a hoped-for count would defeat the condition
it implements. Escalation is always the safe direction; no vocabulary change can
close a hold on its own.

The derived index is never authority. Decision truth stays in the ruling
documents and hold truth stays in the backlog; deleting state/ruling-index is
always safe. An unreadable, symlinked, or out-of-corpus ruling document yields
NO_RULING_READ and exit 3 with no index published, because an unmatched hold
must never rest on a ruling nobody read.

No merge or landing authority is created or expanded.
@sbracewell64
sbracewell64 force-pushed the fm/cfvc-01-ruling-hold-reconcile branch from 5311209 to f3f82b6 Compare August 6, 2026 17:41
@kunchenguid

kunchenguid commented Aug 7, 2026 •

Copy link
Copy Markdown
Owner

Automated reminder: thanks for the PR! This branch currently has a merge conflict with the base branch.

When you get a chance, please rebase onto (or merge) the latest base branch, resolve the conflict, and push. After that, checks will re-run and the PR will get looked at again.

Noted for firstmate#1819 at f3f82b6d.

sbracewell64 added a commit to sbracewell64/firstmate that referenced this pull request Aug 9, 2026
…olds (land of upstream kunchenguid#1819)

Ports upstream PR kunchenguid#1819 onto this fork's trunk. Nothing here is redesigned:
bin/fm-ruling-reconcile.sh, the closure-authority enforcement in
fm-decision-hold.sh's `resolve --from-ruling`, the session-start
RULING_RECONCILE line, the documentation, and the tests are upstream's as
written.

What it does: a decision hold may be closed only when the ruling names the
hold identifier verbatim AND carries an explicit structured verdict.
Everything else escalates. There is no bulk close.

Provenance:
  upstream PR   kunchenguid#1819 (open, untouched)
  head commit   5311209
  its base      2cf0283
  landed onto   ed376cf (sbracewell64/firstmate main)

The dispatch brief named 41d2e5e as the source. That is the contribution's
first commit; the delivered contribution head is 5311209, which adds the
pipeline-review hardening - including the forged-provenance guard and its
test. This carries the full PR head so that guard lands with the feature.

The trunks have diverged, so the diff did not apply cleanly. Three
resolutions, all fork-versus-upstream divergence rather than changes to what
kunchenguid#1819 does:

AGENTS.md - upstream has renamed X-mode to Relay and has dropped the
research-index/ state entry; this fork has neither change. The trunk's
wording and its research-index/ line are kept, and only kunchenguid#1819's own three
additions are applied: the ruling-index/ entry, the RULING_RECONCILE line in
the fleet-state digest, and the extra decision-hold-lifecycle load trigger.

bin/fm-session-start.sh header - upstream numbers the digest steps one higher
than this fork does, and this fork's read-only paragraph says "five"
bootstrap mutating sweeps where upstream says "six". The trunk's numbering
and its sweep count are kept; only kunchenguid#1819's substantive additions are applied
(the RULING_RECONCILE description and the ruling-index rebuild in the
skipped-when-read-only list). The five-versus-six wording is a pre-existing
trunk inconsistency with the same file's own step 2 comment, and is
deliberately left as trunk work rather than swept in here.

bin/fm-session-start.sh fleet-state digest - this fork emits a fleet-admission
block at exactly the point kunchenguid#1819 inserts its RULING_RECONCILE block. Both are
kept, admission first, each with its own `fi`.
@kunchenguid kunchenguid removed the wheelhouse:pending-contributor-action Managed by Wheelhouse label Aug 9, 2026
sbracewell64 added a commit to sbracewell64/firstmate that referenced this pull request Aug 9, 2026
…olds (land of upstream kunchenguid#1819) (#51)

Ports upstream PR kunchenguid#1819 onto this fork's trunk. Nothing here is redesigned:
bin/fm-ruling-reconcile.sh, the closure-authority enforcement in
fm-decision-hold.sh's `resolve --from-ruling`, the session-start
RULING_RECONCILE line, the documentation, and the tests are upstream's as
written.

What it does: a decision hold may be closed only when the ruling names the
hold identifier verbatim AND carries an explicit structured verdict.
Everything else escalates. There is no bulk close.

Provenance:
  upstream PR   kunchenguid#1819 (open, untouched)
  head commit   5311209
  its base      2cf0283
  landed onto   ed376cf (sbracewell64/firstmate main)

The dispatch brief named 41d2e5e as the source. That is the contribution's
first commit; the delivered contribution head is 5311209, which adds the
pipeline-review hardening - including the forged-provenance guard and its
test. This carries the full PR head so that guard lands with the feature.

The trunks have diverged, so the diff did not apply cleanly. Three
resolutions, all fork-versus-upstream divergence rather than changes to what
kunchenguid#1819 does:

AGENTS.md - upstream has renamed X-mode to Relay and has dropped the
research-index/ state entry; this fork has neither change. The trunk's
wording and its research-index/ line are kept, and only kunchenguid#1819's own three
additions are applied: the ruling-index/ entry, the RULING_RECONCILE line in
the fleet-state digest, and the extra decision-hold-lifecycle load trigger.

bin/fm-session-start.sh header - upstream numbers the digest steps one higher
than this fork does, and this fork's read-only paragraph says "five"
bootstrap mutating sweeps where upstream says "six". The trunk's numbering
and its sweep count are kept; only kunchenguid#1819's substantive additions are applied
(the RULING_RECONCILE description and the ruling-index rebuild in the
skipped-when-read-only list). The five-versus-six wording is a pre-existing
trunk inconsistency with the same file's own step 2 comment, and is
deliberately left as trunk work rather than swept in here.

bin/fm-session-start.sh fleet-state digest - this fork emits a fleet-admission
block at exactly the point kunchenguid#1819 inserts its RULING_RECONCILE block. Both are
kept, admission first, each with its own `fi`.
sbracewell64 added a commit to sbracewell64/firstmate that referenced this pull request Aug 9, 2026
…olds (land of upstream kunchenguid#1819) (#51)

Ports upstream PR kunchenguid#1819 onto this fork's trunk. Nothing here is redesigned:
bin/fm-ruling-reconcile.sh, the closure-authority enforcement in
fm-decision-hold.sh's `resolve --from-ruling`, the session-start
RULING_RECONCILE line, the documentation, and the tests are upstream's as
written.

What it does: a decision hold may be closed only when the ruling names the
hold identifier verbatim AND carries an explicit structured verdict.
Everything else escalates. There is no bulk close.

Provenance:
  upstream PR   kunchenguid#1819 (open, untouched)
  head commit   5311209
  its base      2cf0283
  landed onto   ed376cf (sbracewell64/firstmate main)

The dispatch brief named 41d2e5e as the source. That is the contribution's
first commit; the delivered contribution head is 5311209, which adds the
pipeline-review hardening - including the forged-provenance guard and its
test. This carries the full PR head so that guard lands with the feature.

The trunks have diverged, so the diff did not apply cleanly. Three
resolutions, all fork-versus-upstream divergence rather than changes to what

AGENTS.md - upstream has renamed X-mode to Relay and has dropped the
research-index/ state entry; this fork has neither change. The trunk's
wording and its research-index/ line are kept, and only kunchenguid#1819's own three
additions are applied: the ruling-index/ entry, the RULING_RECONCILE line in
the fleet-state digest, and the extra decision-hold-lifecycle load trigger.

bin/fm-session-start.sh header - upstream numbers the digest steps one higher
than this fork does, and this fork's read-only paragraph says "five"
bootstrap mutating sweeps where upstream says "six". The trunk's numbering
and its sweep count are kept; only kunchenguid#1819's substantive additions are applied
(the RULING_RECONCILE description and the ruling-index rebuild in the
skipped-when-read-only list). The five-versus-six wording is a pre-existing
trunk inconsistency with the same file's own step 2 comment, and is
deliberately left as trunk work rather than swept in here.

bin/fm-session-start.sh fleet-state digest - this fork emits a fleet-admission
block at exactly the point kunchenguid#1819 inserts its RULING_RECONCILE block. Both are
kept, admission first, each with its own `fi`.
sbracewell64 added a commit to sbracewell64/firstmate that referenced this pull request Aug 10, 2026
…olds (land of upstream kunchenguid#1819) (#51)

Ports upstream PR kunchenguid#1819 onto this fork's trunk. Nothing here is redesigned:
bin/fm-ruling-reconcile.sh, the closure-authority enforcement in
fm-decision-hold.sh's `resolve --from-ruling`, the session-start
RULING_RECONCILE line, the documentation, and the tests are upstream's as
written.

What it does: a decision hold may be closed only when the ruling names the
hold identifier verbatim AND carries an explicit structured verdict.
Everything else escalates. There is no bulk close.

Provenance:
  upstream PR   kunchenguid#1819 (open, untouched)
  head commit   5311209
  its base      2cf0283
  landed onto   ed376cf (sbracewell64/firstmate main)

The dispatch brief named 41d2e5e as the source. That is the contribution's
first commit; the delivered contribution head is 5311209, which adds the
pipeline-review hardening - including the forged-provenance guard and its
test. This carries the full PR head so that guard lands with the feature.

The trunks have diverged, so the diff did not apply cleanly. Three
resolutions, all fork-versus-upstream divergence rather than changes to what

AGENTS.md - upstream has renamed X-mode to Relay and has dropped the
research-index/ state entry; this fork has neither change. The trunk's
wording and its research-index/ line are kept, and only kunchenguid#1819's own three
additions are applied: the ruling-index/ entry, the RULING_RECONCILE line in
the fleet-state digest, and the extra decision-hold-lifecycle load trigger.

bin/fm-session-start.sh header - upstream numbers the digest steps one higher
than this fork does, and this fork's read-only paragraph says "five"
bootstrap mutating sweeps where upstream says "six". The trunk's numbering
and its sweep count are kept; only kunchenguid#1819's substantive additions are applied
(the RULING_RECONCILE description and the ruling-index rebuild in the
skipped-when-read-only list). The five-versus-six wording is a pre-existing
trunk inconsistency with the same file's own step 2 comment, and is
deliberately left as trunk work rather than swept in here.

bin/fm-session-start.sh fleet-state digest - this fork emits a fleet-admission
block at exactly the point kunchenguid#1819 inserts its RULING_RECONCILE block. Both are
kept, admission first, each with its own `fi`.
sbracewell64 added a commit to sbracewell64/firstmate that referenced this pull request Aug 11, 2026
…olds (land of upstream kunchenguid#1819) (#51)

Ports upstream PR kunchenguid#1819 onto this fork's trunk. Nothing here is redesigned:
bin/fm-ruling-reconcile.sh, the closure-authority enforcement in
fm-decision-hold.sh's `resolve --from-ruling`, the session-start
RULING_RECONCILE line, the documentation, and the tests are upstream's as
written.

What it does: a decision hold may be closed only when the ruling names the
hold identifier verbatim AND carries an explicit structured verdict.
Everything else escalates. There is no bulk close.

Provenance:
  upstream PR   kunchenguid#1819 (open, untouched)
  head commit   5311209
  its base      2cf0283
  landed onto   ed376cf (sbracewell64/firstmate main)

The dispatch brief named 41d2e5e as the source. That is the contribution's
first commit; the delivered contribution head is 5311209, which adds the
pipeline-review hardening - including the forged-provenance guard and its
test. This carries the full PR head so that guard lands with the feature.

The trunks have diverged, so the diff did not apply cleanly. Three
resolutions, all fork-versus-upstream divergence rather than changes to what

AGENTS.md - upstream has renamed X-mode to Relay and has dropped the
research-index/ state entry; this fork has neither change. The trunk's
wording and its research-index/ line are kept, and only kunchenguid#1819's own three
additions are applied: the ruling-index/ entry, the RULING_RECONCILE line in
the fleet-state digest, and the extra decision-hold-lifecycle load trigger.

bin/fm-session-start.sh header - upstream numbers the digest steps one higher
than this fork does, and this fork's read-only paragraph says "five"
bootstrap mutating sweeps where upstream says "six". The trunk's numbering
and its sweep count are kept; only kunchenguid#1819's substantive additions are applied
(the RULING_RECONCILE description and the ruling-index rebuild in the
skipped-when-read-only list). The five-versus-six wording is a pre-existing
trunk inconsistency with the same file's own step 2 comment, and is
deliberately left as trunk work rather than swept in here.

bin/fm-session-start.sh fleet-state digest - this fork emits a fleet-admission
block at exactly the point kunchenguid#1819 inserts its RULING_RECONCILE block. Both are
kept, admission first, each with its own `fi`.
sbracewell64 added a commit to sbracewell64/firstmate that referenced this pull request Aug 11, 2026
…olds (land of upstream kunchenguid#1819) (#51)

Ports upstream PR kunchenguid#1819 onto this fork's trunk. Nothing here is redesigned:
bin/fm-ruling-reconcile.sh, the closure-authority enforcement in
fm-decision-hold.sh's `resolve --from-ruling`, the session-start
RULING_RECONCILE line, the documentation, and the tests are upstream's as
written.

What it does: a decision hold may be closed only when the ruling names the
hold identifier verbatim AND carries an explicit structured verdict.
Everything else escalates. There is no bulk close.

Provenance:
  upstream PR   kunchenguid#1819 (open, untouched)
  head commit   5311209
  its base      2cf0283
  landed onto   ed376cf (sbracewell64/firstmate main)

The dispatch brief named 41d2e5e as the source. That is the contribution's
first commit; the delivered contribution head is 5311209, which adds the
pipeline-review hardening - including the forged-provenance guard and its
test. This carries the full PR head so that guard lands with the feature.

The trunks have diverged, so the diff did not apply cleanly. Three
resolutions, all fork-versus-upstream divergence rather than changes to what

AGENTS.md - upstream has renamed X-mode to Relay and has dropped the
research-index/ state entry; this fork has neither change. The trunk's
wording and its research-index/ line are kept, and only kunchenguid#1819's own three
additions are applied: the ruling-index/ entry, the RULING_RECONCILE line in
the fleet-state digest, and the extra decision-hold-lifecycle load trigger.

bin/fm-session-start.sh header - upstream numbers the digest steps one higher
than this fork does, and this fork's read-only paragraph says "five"
bootstrap mutating sweeps where upstream says "six". The trunk's numbering
and its sweep count are kept; only kunchenguid#1819's substantive additions are applied
(the RULING_RECONCILE description and the ruling-index rebuild in the
skipped-when-read-only list). The five-versus-six wording is a pre-existing
trunk inconsistency with the same file's own step 2 comment, and is
deliberately left as trunk work rather than swept in here.

bin/fm-session-start.sh fleet-state digest - this fork emits a fleet-admission
block at exactly the point kunchenguid#1819 inserts its RULING_RECONCILE block. Both are
kept, admission first, each with its own `fi`.
@kunchenguid

Copy link
Copy Markdown
Owner

Speaking as Kun's firstmate: closing this as stale. It has been waiting on a contributor update for 14+ days with no author push or comment. Reopen if you want to pick it back up.

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.

2 participants