Skip to content

feat(bin): plumb the registered hardened quality posture into task records - #15

Open
BohnBawerick wants to merge 7 commits into
mainfrom
fm/fm-quality-wiring
Open

feat(bin): plumb the registered hardened quality posture into task records#15
BohnBawerick wants to merge 7 commits into
mainfrom
fm/fm-quality-wiring

Conversation

@BohnBawerick

Copy link
Copy Markdown
Owner

Intent

Plumb the hardened quality option from a project's registry line through to a task's durable record, and prove that projects without it are untouched. This is firstmate's own shared tracked material, so firstmate-coding-guidelines applies.

Source of truth: /home/paiva/labs/axi-sandbox/firstmate/implementation-thoughts.md - section 1 (settled decisions, not reopened), section 2 (the shape), section 3 items D5, D6, D7, D9 and D10, and section 8's firstmate table. This is the plumbing only: bin/fm-quality.sh does not exist yet, is not part of this task, and is referenced by name in generated text only.

D5 - bin/fm-project-mode.sh: add a --quality flag that prints exactly one word, standard or hardened, the same way --raw was added. The registry grammar already tokenizes bracket contents and scans for +yolo, so +hardened needs no new grammar - one more loop condition and one more output path. Do not change the two-word stdout contract: the script's own header states it and bin/fm-fleet-sync.sh, bin/fm-home-seed.sh and bin/fm-spawn.sh all parse it. Unknown tokens must keep being ignored rather than refused, so an older firstmate reading a newer registry does not break. The mode is read from the first token, so [+hardened local-only] would otherwise be read as an unknown mode; that must not be left as a silent trap - either tolerate the token in any position or document the ordering requirement in the header.

D6 - bin/fm-brief.sh: add --quality standard|hardened, refused on scout and secondmate scaffolds for the same reason --mode is, defaulting to standard when absent so every existing call site keeps working unchanged. Ship briefs already close with "Delivery contract: mode="; add the sibling line "Quality contract: quality=". When quality is hardened, the generated brief gains a short section stating, in the worker's own terms: the fixed base commit, that the clean loop runs before the harden loop and both run before validation, that the loop is driven by bin/fm-quality.sh and the worker does not hand-roll it, and that a surviving mutant which exposes a real defect is reported rather than tested around. Keep that section short - the mechanics belong in fm-quality.sh --help, and two copies will drift.

D7 - bin/fm-spawn.sh: it already refuses to launch a ship task whose explicit --mode disagrees with the brief's "Delivery contract:" line; add the identical check against "Quality contract:". Record quality= and base_sha= in state/.meta, capturing the base commit at spawn. Both must survive relaunch, because the whole loop is anchored on that base. Use the immutable base commit captured at spawn, never HEAD1: each loop round makes commits, so HEAD1 describes only the latest one and a gate using it silently narrows its view to the last round while continuing to report success. Section 3 of the design calls this the single easiest thing in the build to get wrong and the hardest to notice.

D9 - AGENTS.md section 7: one added sentence in the intake paragraph that already resolves mode and yolo - quality resolves at intake with the same precedence, explicit captain instruction first, then the project's registered posture, then standard, with a deviation reason recorded in the backlog note. One sentence, nothing else; that section has already grown from 585 to 958 lines from exactly this kind of inline addition.

D10 - tests: extend the existing suites that own this behavior. The load-bearing claim is that a project without +hardened, and a brief scaffolded without --quality, behave exactly as today: the resolved mode and yolo values, the two-word stdout, the generated brief content for a standard task, and every existing caller's parse must all be unchanged. Additional key=value lines in a task's metadata record are additive and acceptable. All four changed files sit on the path every task in this fleet flows through, so that claim has to be proven by executing the real interface, not assumed from reading the diff. Follow the repo's test-quality rule: execute the real interface and assert behavior. A test whose only evidence is grepping a script's source for a string proves nothing, because matching text can be dead code.

Presentation: the captain has been explicit that from where he sits this is not an orthogonal axis - when he starts a project he picks one thing from a list, and hardened is the fourth item on that list. Keep the internal separation of delivery and quality exactly as the design specifies, but where operator-facing help text describes the choice, write it as the fourth option rather than as a second independent dimension.

Decisions taken while implementing, and their reasons:

  • D5's bracket grammar is now position-tolerant: a "+"-prefixed token is a flag and never a mode, so the mode is the first non-flag token. That removes the trap for +hardened and for +yolo alike rather than documenting an ordering requirement. The one input whose resolution changes is the previously-broken, undocumented flag-first form "[+yolo ]", which used to silently drop the mode; no registry line uses it.
  • D6's "Quality contract:" line is emitted only when quality is hardened, so a standard brief stays byte-identical to the pre-quality scaffold, which the task names as the load-bearing requirement. An absent line therefore means standard, and bin/fm-spawn.sh reads it that way: --quality standard agrees silently with a quality-silent brief, while --quality hardened against one is refused.
  • fm-brief.sh also refuses --quality on --dreamer scaffolds, alongside scout and secondmate, for the same reason.
  • On relaunch, a ship task recorded before quality existed has its posture normalized to standard rather than left empty, so it is not refused against its own brief; its base_sha stays absent rather than being invented from a HEAD the worker has already moved.
  • Tests were extended in the suites that actually own this behavior rather than in the file names the design listed, which do not exist in this repo: tests/fm-task-delivery.test.sh owns bin/fm-project-mode.sh and the spawn delivery-contract checks, tests/fm-brief.test.sh owns the scaffold, and tests/fm-control-relaunch.test.sh owns relaunch record survival.

Out of scope and deliberately not built here: D1, D2, D3, D4 (bin/fm-quality.sh itself), D8 (bin/fm-crew-state.sh) and D11.

What Changed

  • bin/fm-project-mode.sh now parses +hardened from a project's registry bracket and prints standard or hardened under a new --quality flag, leaving the two-word stdout contract untouched. Bracket parsing became position-tolerant (a +-prefixed token is never the mode), an unknown mode token no longer drops a parsed +hardened, and +hardened beside the conditional no-mistakes-prod-only policy falls back to standard with a stderr warning.
  • bin/fm-brief.sh gained --quality standard|hardened, defaulting to standard and refused on scout, dreamer, and secondmate scaffolds. A hardened ship brief adds a Quality contract: quality=hardened line plus a short quality-gate section naming the fixed base commit, the clean-then-harden loop order before validation, bin/fm-quality.sh as the driver, and reporting rather than testing around a surviving mutant. A standard brief is byte-identical to the pre-quality scaffold.
  • bin/fm-spawn.sh accepts --quality on ship spawns, refuses a value that disagrees with the brief's quality line (an absent line reads as standard), and records quality= and base_sha= in state/<id>.meta, capturing the base commit once at spawn and reading both back on relaunch. bin/fm-promote.sh prints an advisory stderr notice when a promoted task carries no quality posture against a hardened standing posture. Docs, AGENTS.md intake precedence, and the README.md mode list were updated, and the existing suites in tests/fm-task-delivery.test.sh, tests/fm-brief.test.sh, and tests/fm-control-relaunch.test.sh were extended to execute these interfaces.

Risk Assessment

✅ Low: The plumbing is additive and well bounded - the two-word registry contract, the standard brief, and every existing meta key are provably unchanged, the base commit is captured after the worktree reset and read back rather than recaptured on relaunch, and the only surviving finding is one over-broad sentence in an architecture doc with no runtime effect.

Testing

Ran the three suites that own this behavior (fm-task-delivery, fm-brief, fm-control-relaunch) via bin/fm-test-run.sh; all pass with no failures or gate skips, and they cover the new --quality parsing, the closed-set validation, the brief scaffold, the spawn contract check, the durable quality=/base_sha= record, and relaunch survival by executing the real scripts. Because passing tests alone are not evidence of the end-user experience, I also drove the four changed CLIs end to end in a disposable sandbox firstmate home and captured the transcripts: a registry line with +hardened resolves through fm-project-mode.sh --quality, produces a hardened brief whose Quality gate section and machine-readable contract line are visible in a diff against the standard brief, spawns a task whose record carries quality=hardened and base_sha= pinned to the spawn-time commit, and keeps both across a relaunch after the worker committed twice past that base. The untouched-projects claim is shown two ways: a standard brief is byte-identical (matching sha256) to the base-commit scaffold's output under an identical home and FM_ROOT, and a differential run of the old vs new fm-project-mode.sh over 18 registry shapes returns the same two-word stdout on every shape that existed before the change. This is a shell CLI change with no rendered UI surface, so the reviewer-visible artifacts are CLI transcripts and generated-brief diffs rather than screenshots. I removed the sandbox and scratch scripts; the worktree is clean and evidence lives only in the dedicated evidence directory.

Evidence: End-to-end walk: registry +hardened through to the durable task record

Source: End-to-end walk: registry +hardened through to the durable task record

############ 1. The registry line the captain writes ############

$ cat '/tmp/fm-demo-sbx.PGLdWR/home/data/projects.md'
- plain [direct-PR] - an ordinary project (added 2026-01-01)
- guarded [direct-PR +hardened] - the fourth option picked at registration (added 2026-01-01)
- flipped [+hardened local-only] - same flag, written before the mode (added 2026-01-01)
- proj [direct-PR +hardened] - the sandbox project the spawns below use (added 2026-01-01)

############ 2. The two-word stdout its three callers parse ############
plain    -> direct-PR off
guarded  -> direct-PR off
flipped  -> local-only off

############ 3. The new one-word --quality output ############
plain                  -> standard
guarded                -> hardened
flipped                -> hardened
absent-from-registry   -> standard

############ 4. A standard brief is byte-identical to the pre-change scaffold ############

$ diff '/tmp/fm-demo-sbx.PGLdWR/brief-before.md' '/tmp/fm-demo-sbx.PGLdWR/brief-after.md' && echo 'IDENTICAL: base-commit scaffold vs new scaffold, no --quality'
IDENTICAL: base-commit scaffold vs new scaffold, no --quality

$ diff '/tmp/fm-demo-sbx.PGLdWR/brief-before.md' '/tmp/fm-demo-sbx.PGLdWR/brief-after-explicit.md' && echo 'IDENTICAL: base-commit scaffold vs new scaffold with --quality standard'
IDENTICAL: base-commit scaffold vs new scaffold with --quality standard

$ sha256sum '/tmp/fm-demo-sbx.PGLdWR/brief-before.md' '/tmp/fm-demo-sbx.PGLdWR/brief-after.md' '/tmp/fm-demo-sbx.PGLdWR/brief-after-explicit.md'
ae3adb2d46f907b006985653867ed5981e8e65397d02157cd2521faf024f5d68  /tmp/fm-demo-sbx.PGLdWR/brief-before.md
ae3adb2d46f907b006985653867ed5981e8e65397d02157cd2521faf024f5d68  /tmp/fm-demo-sbx.PGLdWR/brief-after.md
ae3adb2d46f907b006985653867ed5981e8e65397d02157cd2521faf024f5d68  /tmp/fm-demo-sbx.PGLdWR/brief-after-explicit.md

$ grep -ic quality <standard brief>
0 line(s) mentioning quality in a standard brief
############ 5. What the worker actually reads in a hardened brief ############

$ diff standard-brief hardened-brief
12c12
< You are in a disposable git worktree of plain, at a detached HEAD on a clean default branch.
---
> You are in a disposable git worktree of guarded, at a detached HEAD on a clean default branch.
20c20
< Then create your branch: `git checkout -b fm/demo-1`
---
> Then create your branch: `git checkout -b fm/ship-hard`
23c23
< 1. Never push to the default branch (push only your `fm/demo-1` branch). Never merge a PR.
---
> 1. Never push to the default branch (push only your `fm/ship-hard` branch). Never merge a PR.
27c27
<    `echo "{state}: {one short line}" >> '/tmp/fm-demo-sbx.PGLdWR/bhome/state/demo-1.status'`
---
>    `echo "{state}: {one short line}" >> '/tmp/fm-demo-sbx.PGLdWR/home/state/ship-hard.status'`
54a55,62
> # Quality gate
> This task ships **hardened**, so an extra quality pass runs before the definition of done below, and `/home/paiva/.no-mistakes/worktrees/3437026af8a8/01M0JW8XRQC62ZWQ2YVC83RZQG/bin/fm-quality.sh` drives it.
> 
> 1. The base commit is fixed when this task starts and is recorded in the task's durable record as `base_sha=`. Measure every phase as a diff against THAT commit, never against `HEAD~1`: each round of the loop commits, so `HEAD~1` would narrow the gate to the last round alone while still reporting success.
> 2. Run the clean loop first, then the harden loop, and finish both before you start on that definition of done - on a no-mistakes task, that means before the pipeline starts.
> 3. Do not hand-roll either loop. The rounds, the bounds, the receipt, and the outcome vocabulary belong to that script; `/home/paiva/.no-mistakes/worktrees/3437026af8a8/01M0JW8XRQC62ZWQ2YVC83RZQG/bin/fm-quality.sh --help` is authoritative for its mechanics.
> 4. Only a pass continues. If a surviving mutant exposes a real product defect rather than a missing test, report it (rule 6) instead of writing a test around it - a test that passes against a defect is exactly how a quality gate gets gamed.
> 
56a65
> Quality contract: quality=hardened


$ tail of the hardened brief (the contract lines the worker and fm-spawn.sh both read)
64:Delivery contract: mode=direct-PR
65:Quality contract: quality=hardened

############ 6. The spawn records it durably ############

$ the base commit at spawn time: d81b5a97cfb201b935b20f6e8f853e57c2455c4c

$ SPAWN ship-hard '/tmp/fm-demo-sbx.PGLdWR/proj' --mode direct-PR --yolo off --quality hardened
spawned ship-hard harness=claude kind=ship mode=direct-PR yolo=off window=firstmate:fm-ship-hard worktree=/tmp/fm-demo-sbx.PGLdWR/wt

$ state/ship-hard.meta
kind=ship
mode=direct-PR
yolo=off
quality=hardened
base_sha=d81b5a97cfb201b935b20f6e8f853e57c2455c4c
OK: base_sha equals the commit the worktree sat on at spawn

############ 7. The base commit is pinned, not re-derived from a moved HEAD ############

$ the worker has now made two commits; HEAD~1 no longer describes the base
HEAD    = 4f5d6f0430272f382183707b01a88c6eb36f5a4c
HEAD~1  = dabc5bba0402599113d193dc4b88c631e068c744  <- what a naive gate would use
base_sha= d81b5a97cfb201b935b20f6e8f853e57c2455c4c  <- what was recorded

$ git -C '/tmp/fm-demo-sbx.PGLdWR/wt' log --oneline 'd81b5a97cfb201b935b20f6e8f853e57c2455c4c'..HEAD
4f5d6f0 loop round 2
dabc5bb loop round 1

$ the agent has exited; the endpoint window is still there but runs no agent

$ SPAWN --relaunch ship-hard
spawned ship-hard harness=claude kind=ship mode=direct-PR yolo=off window=firstmate:fm-ship-hard worktree=/tmp/fm-demo-sbx.PGLdWR/wt

$ state/ship-hard.meta after relaunch
quality=hardened
base_sha=d81b5a97cfb201b935b20f6e8f853e57c2455c4c
OK: quality and the pinned base commit survived the relaunch unchanged

############ 8. A project without +hardened is untouched ############

$ the standard brief carries no Quality contract: line
0
0 - none, as before the change

$ SPAWN ship-plain '/tmp/fm-demo-sbx.PGLdWR/proj' --mode direct-PR --yolo off
notice: ship-plain ships quality=standard while the standing posture for proj is hardened - less rigor than the captain's standing posture; proceed only on a current explicit captain instruction or an intake judgment you can state
spawned ship-plain harness=claude kind=ship mode=direct-PR yolo=off window=firstmate:fm-ship-plain worktree=/tmp/fm-demo-sbx.PGLdWR/wt

$ state/ship-plain.meta
kind=ship
mode=direct-PR
yolo=off
quality=standard
base_sha=d81b5a97cfb201b935b20f6e8f853e57c2455c4c

############ 9. Brief and flag must agree, in both directions ############

$ a quality-silent brief + --quality hardened

$ SPAWN ship-plain2 '/tmp/fm-demo-sbx.PGLdWR/proj' --mode direct-PR --yolo off --quality hardened
error: quality mismatch for ship-plain2: the brief says quality=standard but this spawn passed --quality hardened; correct the flag or re-scaffold the brief so the worker's instructions and the task record agree

$ a hardened brief + --quality standard

$ SPAWN ship-plain2 '/tmp/fm-demo-sbx.PGLdWR/proj' --mode direct-PR --yolo off --quality standard
error: quality mismatch for ship-plain2: the brief says quality=hardened but this spawn passed --quality standard; correct the flag or re-scaffold the brief so the worker's instructions and the task record agree

$ and --quality standard against a quality-silent brief agrees silently

$ SPAWN ship-plain3 '/tmp/fm-demo-sbx.PGLdWR/proj' --mode direct-PR --yolo off --quality standard
notice: ship-plain3 ships quality=standard while the standing posture for proj is hardened - less rigor than the captain's standing posture; proceed only on a current explicit captain instruction or an intake judgment you can state
spawned ship-plain3 harness=claude kind=ship mode=direct-PR yolo=off window=firstmate:fm-ship-plain3 worktree=/tmp/fm-demo-sbx.PGLdWR/wt
Evidence: Old vs new fm-project-mode.sh: the two-word contract over 18 registry shapes

Source: Old vs new fm-project-mode.sh: the two-word contract over 18 registry shapes

project                old two-word             new two-word             new --quality verdict
-------------------------------------------------------------------------------------------------
legacy                 no-mistakes off          no-mistakes off          standard   same
flat-nm                no-mistakes off          no-mistakes off          standard   same
flat-dpr               direct-PR off            direct-PR off            standard   same
flat-lo                local-only off           local-only off           standard   same
policy                 no-mistakes off          no-mistakes off          standard   same
yolo-nm                no-mistakes on           no-mistakes on           standard   same
yolo-dpr               direct-PR on             direct-PR on             standard   same
yolo-lo                local-only on            local-only on            standard   same
yolo-policy            no-mistakes on           no-mistakes on           standard   same
typo                   no-mistakes off          no-mistakes off          standard   same
typo-yolo              no-mistakes off          no-mistakes off          standard   same
unknown-flag           direct-PR off            direct-PR off            standard   same
hardened-nm            no-mistakes off          no-mistakes off          hardened   same
hardened-dpr           direct-PR off            direct-PR off            hardened   same
hardened-lo            local-only off           local-only off           hardened   same
hardened-yolo          no-mistakes on           no-mistakes on           hardened   same
hardened-policy        no-mistakes off          no-mistakes off          standard   same
not-registered         no-mistakes off          no-mistakes off          standard   same

RESULT: the two-word contract is byte-identical on every registry shape that
existed before this change, including the unknown-mode typo, the conditional
policy, and an unrecognised future flag.

Two shapes resolve differently, and both are declared in the change:

1. The new flag-first form, which no old registry could carry:
  old -> warn: unknown mode "+hardened" for flag-first; defaulting to no-mistakes off no-mistakes off 
  new -> local-only off  (quality: hardened)

2. The previously-broken flag-first
shape '[+yolo <mode>]', which the old parser silently dropped the mode from:
  old -> no-mistakes on 
  new -> direct-PR on 

Registry lines in this repo that use the flag-first form (should be none):
  (no data/projects.md in a fresh checkout, and no flag-first line anywhere in tracked docs)
  none in tracked docs
Evidence: Operator-facing copy: fm-brief.sh --help, the AGENTS.md intake sentence, and hardened as the fourth option

Source: Operator-facing copy: fm-brief.sh --help, the AGENTS.md intake sentence, and hardened as the fourth option

======================================================================
$ bin/fm-brief.sh --help | sed -n 1,13p;51,64p   (the --quality choice)

Scaffold a crewmate brief or persistent secondmate charter at
data/<task-id>/brief.md under the active firstmate home.
For ordinary tasks, the standard Setup/Rules/Definition-of-done contract is
filled in. Firstmate then replaces the {TASK} placeholder with the task
description, acceptance criteria, and context, and may adjust other sections
when the task genuinely deviates (e.g. working an existing external PR instead
of shipping a new one).
Usage: fm-brief.sh <task-id> <repo-name> --mode <no-mistakes|direct-PR|local-only> [--quality <standard|hardened>] [--herdr-lab]
       fm-brief.sh <task-id> <repo-name> --scout [--herdr-lab]
       fm-brief.sh <task-id> <repo-name> --dreamer [--herdr-lab]
       fm-brief.sh <task-id> --secondmate {<project>...|--no-projects}
  --scout writes the scout contract instead: the deliverable is a report at
  data/<task-id>/report.md (no branch, no push, no PR) and the worktree is scratch.
   ...
section 7) from the project's registered "+hardened" annotation, and it defaults to
standard so every existing call site scaffolds exactly as before:
  standard  the ordinary path: implement, then the mode's definition of done
  hardened  a clean loop then a harden loop, both against the base commit fixed at
            spawn, both before validation, driven by bin/fm-quality.sh
A hardened brief carries the sibling machine-readable line
"Quality contract: quality=hardened" plus one short quality-gate section; a standard
brief carries neither, so an absent line means standard and a standard brief stays
byte-identical to what this scaffold produced before --quality existed. bin/fm-spawn.sh
checks that line against its own --quality exactly as it checks the mode line.
--quality is refused on scout, dreamer and secondmate scaffolds, for the same reason
--mode is.
Ship briefs begin with a worktree-isolation assertion before the branch step.
--mode is refused on scout, dreamer and secondmate scaffolds: a scout or dreamer

======================================================================
$ grep -n "quality posture resolves at intake" AGENTS.md   (D9: one added sentence, section 7)

289:A task's quality posture resolves at intake with the same precedence, a current explicit captain instruction first, then the project's registered posture, then `standard`, with the one-line reason for any deviation recorded in the same backlog note.

======================================================================
Operator-facing copy: hardened written as the fourth option on the list,
not as a second independent axis.

--- README.md ---
48:- **Explicit project modes** - each project ships via `no-mistakes`, `direct-PR`, `local-only`, or one of those plus `+hardened` for the highest-rigor quality gate, with an optional `+yolo` autonomy flag.

--- .agents/skills/project-management/SKILL.md ---
45:- `+hardened` is the highest-rigor choice on this list, adding the quality gate that runs before validation; it rides alongside one of the flat modes above rather than replacing it, so a hardened project is registered as `[no-mistakes +hardened]`, `[direct-PR +hardened]`, or `[local-only +hardened]`.
46-
Evidence: A standard brief is byte-identical to the pre-change scaffold
$ diff brief-before.md brief-after.md && echo IDENTICAL
IDENTICAL: base-commit scaffold vs new scaffold, no --quality

$ diff brief-before.md brief-after-explicit.md && echo IDENTICAL
IDENTICAL: base-commit scaffold vs new scaffold with --quality standard

$ sha256sum brief-before.md brief-after.md brief-after-explicit.md
71f708b14d39ed3cc0b8cfac0cf829f9e8b48cab756888f5b6a98a8fec39ee09 brief-before.md
71f708b14d39ed3cc0b8cfac0cf829f9e8b48cab756888f5b6a98a8fec39ee09 brief-after.md
71f708b14d39ed3cc0b8cfac0cf829f9e8b48cab756888f5b6a98a8fec39ee09 brief-after-explicit.md

0 line(s) mentioning quality in a standard brief
Evidence: The durable task record, and the base commit surviving a relaunch past two loop commits
$ fm-spawn.sh ship-hard <proj> --mode direct-PR --yolo off --quality hardened
spawned ship-hard harness=claude kind=ship mode=direct-PR yolo=off window=firstmate:fm-ship-hard

$ state/ship-hard.meta
kind=ship
mode=direct-PR
yolo=off
quality=hardened
base_sha=8cf1d45be3eb14189ce069d752439f8c39c3cb40

# two rounds of work land on the branch
HEAD = 56a7863d7ae9dc404be961ae9670557cefbaa180
HEAD~1 = 7972afcbe4595c082e36cbb658ccbc57d5ebf681 <- what a naive gate would use
base_sha = 8cf1d45be3eb14189ce069d752439f8c39c3cb40 <- what was recorded

$ fm-spawn.sh --relaunch ship-hard
spawned ship-hard harness=claude kind=ship mode=direct-PR yolo=off window=firstmate:fm-ship-hard

$ state/ship-hard.meta after relaunch
quality=hardened
base_sha=8cf1d45be3eb14189ce069d752439f8c39c3cb40

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

🔧 **Rebase** - 1 issue found → auto-fixed ✅
  • ⚠️ bin/fm-brief.sh - branch carries 2 commit(s) that exist on your local main branch but were never pushed to origin/main; rebasing would bundle this unrelated work (9 file(s)) into the PR:
  • 1b63cc8 fix(tests): make the suite honest, bounded, and leak-safe on loaded hosts
  • 7285a95 fix: show documented decision-key placement in brief status examples

Push main to origin, or rebase your branch onto origin/main, before gating.

🔧 Fix applied.
✅ Re-checked - no issues remain.

⚠️ **Review** - 1 info
  • ⚠️ bin/fm-project-mode.sh:122 - The header contract at lines 52-54 states "An unknown/missing project or unknown mode falls back to &fix(watcher): make check wakes lossless via watcher-side suppression kunchenguid/firstmate#34;no-mistakes off&fix(watcher): make check wakes lossless via watcher-side suppression kunchenguid/firstmate#34; ... --quality falls back to &fix(watcher): make check wakes lossless via watcher-side suppression kunchenguid/firstmate#34;standard&fix(watcher): make check wakes lossless via watcher-side suppression kunchenguid/firstmate#34; on the same inputs." The unknown-mode branch at line 122 resets only mode and yolo (mode=no-mistakes; yolo=off) and leaves quality at whatever the bracket scan produced. Concrete trace: registry line - qproj [no-mistakez +hardened] - fixture -> awk emits no-mistakez off hardened -> the case at line 122 warns and rewrites mode/yolo -> emit no-mistakes off hardened -> fm-project-mode.sh --quality qproj prints hardened, not the documented standard. The runtime behaviour is arguably the safer of the two (it keeps rigor rather than dropping it, and the mode typo is still warned about on stderr), so the defect is in the newly added header sentence rather than the code: reword lines 52-54 so the quality posture is described as resolved independently of the mode fallback. The existing suite pins the two-word output for this exact input (tests/fm-task-delivery.test.sh, test_project_mode_two_word_contract_survives_the_quality_posture) but never asks for --quality on it, so nothing catches the divergence.
  • ℹ️ bin/fm-spawn.sh:1791 - For the delivery mode, a ship spawn compares the task's --mode against the project's registered standing posture and prints a loud one-line notice when the task ships with less rigor (lines 1791-1797). The quality posture has no equivalent: a project registered [no-mistakes +hardened] can be spawned with a standard brief and --quality absent, and nothing says so - the brief agreement check at line 1783 only compares the spawn flag to the brief, and both agree at standard. The registry-to-task link for quality therefore relies entirely on the intake agent reading the new AGENTS.md sentence and remembering to pass the flag, with no mechanical backstop. The user intent scopes this change to D5/D6/D7/D9/D10 and does not ask for a downgrade notice, so this is an observation rather than a gap in the requested work; adding the symmetric notice is a scope decision for the author.

🔧 Fix: add quality standing-posture notice, correct fallback header
3 issues (1 warning, 2 infos) still open:

  • ⚠️ tests/fm-task-delivery.test.sh:35 - trap delivery_cleanup EXIT overrides the shared trap fm_test_cleanup EXIT armed at tests/lib.sh:307, and delivery_cleanup never calls fm_test_cleanup. tests/lib.sh:55-58 documents this exact contract: "A test file that needs extra teardown ... should define its own EXIT trap and call fm_test_cleanup from inside it so registered dirs are still removed." Concrete trace: the suite runs to completion -> EXIT fires -> only delivery_cleanup runs -> the /tmp/fm-task-delivery.XXXXXX tree created by fm_test_tmproot at line 23 (which now also holds real git repos and worktrees from make_spawning_home) and the /tmp/.fm-test-cleanup.$$.XXXXXX registry file both survive, plus fm_test_reap_tracked_pids never runs. Every normal run of this suite now leaks the temp tree it used to remove, which is the opposite of the comment at lines 25-27 ("so this suite leaks nothing on a shared host"). The INT/TERM traps still point at fm_test_cleanup, so the signal paths and the normal path now clean up different sets. Fix: append fm_test_cleanup as the last statement of delivery_cleanup.
  • ℹ️ tests/fm-task-delivery.test.sh:14 - The suite header still asserts "Every spawn case here stops before any endpoint exists: the delivery checks run ahead of backend creation, and a fake tmux that exits non-zero backstops the cases that are meant to get past them, so no window or worktree is ever created." The change adds make_spawning_home / run_spawning, whose cases deliberately run a full ship spawn to completion: they build a real git worktree via fm_git_worktree, install a fake tmux that exits 0 and answers #{pane_current_path}, let freshen_spawn_worktree_base reset the worktree, and create /tmp/fm-&lt;id&gt;. The stated invariant is now false for those cases, and it is exactly the invariant a future maintainer would rely on to skip cleanup. Reword the header to say the delivery-check cases stop before an endpoint exists while the metadata cases run a full spawn against fakes and own their own cleanup.
  • ℹ️ bin/fm-brief.sh:527 - The hardened path is now wireable end to end today: fm-brief.sh --quality hardened scaffolds a brief whose quality-gate section tells the worker that $FM_ROOT/bin/fm-quality.sh drives the loop and that $FM_ROOT/bin/fm-quality.sh --help is authoritative, fm-spawn.sh --quality hardened accepts it, and quality=hardened plus base_sha= land in the task record - while bin/fm-quality.sh does not exist in the tree. A worker handed that brief has no way to satisfy rules 1-4 and would have to report blocked. The user intent explicitly authorizes this ("bin/fm-quality.sh does not exist yet, is not part of this task, and is referenced by name in generated text only"), so this is a note on the interim state rather than a defect in the requested work; no action is needed unless you want an interim guard until D1-D4 land.

🔧 Fix: fix test suite teardown, header, typo-fallback coverage
3 infos still open:

  • ℹ️ bin/fm-brief.sh:62 - The newly added header line reads "--quality is refused on scout and secondmate scaffolds for the same reason --mode is." The code refuses it on dreamer scaffolds too: the guard at bin/fm-brief.sh:196 fires whenever KIND is not ship, and the error message itself names the dreamer ("a scout or dreamer delivers a report"). tests/fm-brief.test.sh pins the dreamer refusal in test_quality_is_closed_set_and_refused_where_it_does_not_apply. Since this header IS the operator-facing --help output (test_help_includes_entire_header prints it), the help under-reports where the flag is refused. The sibling --mode line at bin/fm-brief.sh:64 has the same omission, so fixing only the new line would leave the two inconsistent; add the dreamer to the quality line, or to both. No behavioural impact.
  • ℹ️ bin/fm-promote.sh:15 - bin/fm-promote.sh converts a scout record to a ship record: it strips kind=, mode=, yolo= (line 112) and writes back kind=ship plus the explicit --mode/--yolo (lines 114-116). It has no --quality flag and captures no base commit, so a ship task created by promotion carries neither quality= nor base_sha= in state/<id>.meta. Concrete consequence: after promotion the task is kind=ship, and on relaunch bin/fm-spawn.sh:1076 normalizes the absent quality to standard while base_sha stays empty, so a promoted task can never carry the hardened posture and would have no anchor for the loop if it did. Nothing breaks today - the relaunch normalization and the brief agreement check both resolve to standard consistently, and bin/fm-quality.sh does not exist yet - so this is a completeness gap, not a regression. The intent enumerates D5/D6/D7/D9/D10 and names bin/fm-project-mode.sh, bin/fm-brief.sh, bin/fm-spawn.sh, AGENTS.md and the tests as the changed files; promote is neither required nor listed out of scope, so closing this is a scope decision for the author rather than something to fold into this change.
  • ℹ️ tests/fm-task-delivery.test.sh:531 - test_spawn_records_the_quality_posture_and_base_commit reads base=$(git -C "$wt" rev-parse HEAD) before the spawn and asserts the recorded base_sha equals it. make_spawning_home (line 494) builds the worktree with fm_git_worktree, which branches from the single commit that is also origin's default tip, so the worktree's pre-spawn HEAD and the commit freshen_spawn_worktree_base resets it to (bin/fm-spawn.sh:2510) are the same object. The assertion therefore holds whether the capture at bin/fm-spawn.sh:2884 runs after the reset (correct) or anywhere before it (wrong base for a pooled worktree left advanced by a prior task). The code is correct as written, and the relaunch suite does prove the read-back-never-recapture half against a moved HEAD, so only this one property is unpinned. To close it, commit an extra commit on the fixture worktree's branch before the spawn so its HEAD differs from origin's default tip, then assert base_sha equals the origin default tip rather than the pre-spawn HEAD.

🔧 Fix: add promote quality notice, fix brief help, pin base capture
1 warning still open:

  • ⚠️ .agents/skills/project-management/SKILL.md:51 - The change adds a reader for the registry's +hardened token but no producer for it. .agents/skills/project-management/SKILL.md is the procedure firstmate follows to add or create a project and write its data/projects.md line: lines 40-45 enumerate the four delivery postures the captain chooses from, and line 51 documents +yolo as the one optional flag. Neither mentions +hardened. Concrete consequence, verified by running the real script: bin/fm-project-mode.sh --quality &lt;proj&gt; returns standard for every project registered through that documented path, so bin/fm-spawn.sh:1798 and bin/fm-promote.sh:137 can never fire their new standing-posture notices, and the intake precedence sentence added at AGENTS.md:289 ("then the project's registered posture") always falls through to standard. The only way a +hardened token enters the registry today is a captain hand-editing the file. README.md:48 and docs/architecture.md:253 likewise still describe the registry as a mode plus an optional +yolo. This also touches the intent's presentation clause ("where operator-facing help text describes the choice, write it as the fourth option rather than as a second independent dimension"): the two script headers changed here do present it that way, but the list the captain actually picks from when starting a project is the one in this skill, and hardened is absent from it. The intent enumerates D5/D6/D7/D9/D10 and names bin/fm-project-mode.sh, bin/fm-brief.sh, bin/fm-spawn.sh, AGENTS.md and the tests as the changed files, and lists D1-D4, D8 and D11 as out of scope; the registration skill is neither required nor excluded, so whether to close this here or in a follow-up is your call. Nothing in the code is wrong.

🔧 Fix: document hardened registration, refuse it on conditional policy
1 info still open:

  • ℹ️ docs/architecture.md:253 - The change makes +hardened a real token in data/projects.md, read by bin/fm-project-mode.sh --quality and acted on by bin/fm-spawn.sh:1799 and bin/fm-promote.sh:137, and .agents/skills/project-management/SKILL.md now documents how a captain registers it. Two summary docs still describe the registry grammar as a delivery mode plus an optional +yolo flag only: docs/architecture.md:253 ("records each project's standing posture and optional +yolo flag ... a ship spawn that drops below the registered rigor prints a deviation notice") and README.md:48 ("with an optional +yolo autonomy flag"). Both are now incomplete in a verifiable way: the registry carries a third annotation, and a ship spawn now prints a second, quality deviation notice that architecture.md's sentence does not cover. Nothing in the code is wrong, and the intent enumerates D5/D6/D7/D9/D10 and names bin/fm-project-mode.sh, bin/fm-brief.sh, bin/fm-spawn.sh, AGENTS.md and the tests as the changed files, so these two docs are neither required nor excluded; whether to close this here or in a follow-up is your call. Every prior round in this branch was the same class of header-versus-code divergence, which is the argument for closing it in the same commit.

🔧 Fix: document hardened token in architecture and README grammar
1 info still open:

  • ℹ️ docs/architecture.md:253 - The reworded sentence claims "a ship spawn or a promotion that drops below either registered posture prints a deviation notice and continues". bin/fm-promote.sh prints only ONE notice, and only for the quality posture (bin/fm-promote.sh:139). It never reads the registered delivery mode and never compares it against the caller's --mode, so the "or a promotion ... either registered posture" half is false for the mode axis. Concrete trace: register '- proj [no-mistakes] - fixture' in data/projects.md, then run bin/fm-promote.sh <id> --mode local-only --yolo off on a scout whose meta carries project=<...>/proj. local-only ranks below the registered no-mistakes (delivery_rigor_rank in bin/fm-spawn.sh:1755-1758 ranks local-only 1 vs no-mistakes 3), yet promote emits no deviation notice at all: its only notice guard is [ "$STANDING_QUALITY" = hardened ]. bin/fm-spawn.sh:1793 does print the mode notice, so the sentence is accurate for a spawn and wrong for a promotion. Nothing in the code is broken; the doc claim was widened past what the code does in the same round that added promote's quality notice. Fix: narrow the clause, for example say a ship spawn that drops below the registered delivery rigor or either spawn-or-promotion path that drops below the registered quality posture prints a deviation notice, rather than promising both axes on both paths.
✅ **Test** - passed

✅ No issues found.

  • bash bin/fm-test-run.sh tests/fm-task-delivery.test.sh tests/fm-brief.test.sh tests/fm-control-relaunch.test.sh - all 3 suites pass, including the 12 quality/base_sha cases
  • bin/fm-project-mode.sh &lt;name&gt; and bin/fm-project-mode.sh --quality &lt;name&gt; driven against a registry carrying [direct-PR], [direct-PR +hardened], [+hardened local-only] and an unregistered name
  • Differential run of the base-commit bin/fm-project-mode.sh vs the target one over 18 registry shapes (legacy no-bracket, three flat modes, the conditional policy, +yolo variants, an unknown-mode typo, an unrecognised future flag, +hardened variants, unregistered)
  • bin/fm-brief.sh demo-1 plain --mode direct-PR run with the base-commit script, the target script, and the target script plus --quality standard, all under one identical FM_HOME and FM_ROOT_OVERRIDE, then compared with diff and sha256sum
  • bin/fm-brief.sh ship-hard guarded --mode direct-PR --quality hardened then diff against the standard brief to show the added Quality gate section and Quality contract: quality=hardened line
  • bin/fm-spawn.sh ship-hard &lt;proj&gt; --mode direct-PR --yolo off --quality hardened through a fake tmux backend, then read state/ship-hard.meta for quality= and base_sha=
  • Committed two rounds of work into the task worktree so HEAD~1 no longer describes the base, then bin/fm-spawn.sh --relaunch ship-hard and re-read the record to confirm base_sha was read back, not recaptured
  • bin/fm-spawn.sh ship-plain &lt;proj&gt; --mode direct-PR --yolo off with no --quality - records quality=standard, brief carries no Quality contract line
  • bin/fm-spawn.sh ... --quality hardened against a quality-silent brief and --quality standard against a hardened brief - both refused; --quality standard against a quality-silent brief accepted
  • bin/fm-brief.sh --help and the README / project-management SKILL.md copy, to check hardened is presented as the fourth option on the registration list
  • grep -n &#34;quality posture resolves at intake&#34; AGENTS.md - the single D9 sentence at line 289
⚠️ **Document** - 1 info
  • ℹ️ AGENTS.md:85 - AGENTS.md:85 describes data/projects.md as recording "each project's standing delivery posture", which now also holds the +hardened quality token. I left it as-is: that map line already summarised (it never named +yolo either), and the user intent says AGENTS.md must not grow by inline addition beyond D9's one sentence. The registry grammar's owner (bin/fm-project-mode.sh header) and the procedure owner (.agents/skills/project-management/SKILL.md) both state the quality token correctly. Flagging so a reviewer can overrule the judgment call.
✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

…sk record

Reads a project's registered "+hardened" annotation and carries it to the
worker's instructions and the task's durable record, so the quality loop that
bin/fm-quality.sh will drive has a posture and a fixed base commit to work from.
That script is not part of this change; it is referenced by name only.

- bin/fm-project-mode.sh: --quality prints one word, standard or hardened. The
  two-word stdout its three callers parse is untouched, so it gets its own
  output path. The bracket grammar is now position-tolerant: a "+"-prefixed
  token is a flag and never a mode, so "[+hardened local-only]" resolves the
  mode behind it instead of reading the flag as an unknown mode. Unrecognized
  flags are still ignored rather than refused.
- bin/fm-brief.sh: --quality standard|hardened, defaulting to standard and
  refused on scout, dreamer, and secondmate scaffolds. A hardened brief records
  the sibling "Quality contract: quality=hardened" line and one short quality
  gate section; a standard brief records neither and stays byte-identical to
  the pre-quality scaffold.
- bin/fm-spawn.sh: the brief's quality line must agree with --quality, the same
  check the delivery line already gets, in both directions. quality= and
  base_sha= land in the task record; the base commit is captured once at spawn
  and read back on relaunch, never recaptured, because the loop commits each
  round and a later capture would narrow the gate while still reporting success.
- AGENTS.md: one sentence placing quality resolution at intake.

Tests execute the real interfaces. The load-bearing ones prove a project
without "+hardened" and a brief scaffolded without --quality behave exactly as
before: the two-word stdout is pinned across every annotation form, the two
scaffolds are compared byte for byte, and the task record's key set is pinned
so only quality= and base_sha= are additive.
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