feat(bin): plumb the registered hardened quality posture into task records - #15
Open
BohnBawerick wants to merge 7 commits into
Open
feat(bin): plumb the registered hardened quality posture into task records#15BohnBawerick wants to merge 7 commits into
BohnBawerick wants to merge 7 commits into
Conversation
…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.
…ture and scripts inventory
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 HEAD
1: 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:
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.shnow parses+hardenedfrom a project's registry bracket and printsstandardorhardenedunder a new--qualityflag, 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+hardenedbeside the conditionalno-mistakes-prod-onlypolicy falls back tostandardwith a stderr warning.bin/fm-brief.shgained--quality standard|hardened, defaulting tostandardand refused on scout, dreamer, and secondmate scaffolds. A hardened ship brief adds aQuality contract: quality=hardenedline plus a short quality-gate section naming the fixed base commit, the clean-then-harden loop order before validation,bin/fm-quality.shas 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.shaccepts--qualityon ship spawns, refuses a value that disagrees with the brief's quality line (an absent line reads asstandard), and recordsquality=andbase_sha=instate/<id>.meta, capturing the base commit once at spawn and reading both back on relaunch.bin/fm-promote.shprints an advisory stderr notice when a promoted task carries no quality posture against a hardened standing posture. Docs,AGENTS.mdintake precedence, and theREADME.mdmode list were updated, and the existing suites intests/fm-task-delivery.test.sh,tests/fm-brief.test.sh, andtests/fm-control-relaunch.test.shwere 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
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
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
Evidence: A standard brief is byte-identical to the pre-change scaffold
Evidence: The durable task record, and the base commit surviving a relaunch past two loop commits
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:Push main to origin, or rebase your branch onto origin/main, before gating.
🔧 Fix applied.
✅ Re-checked - no issues remain.
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 leavesqualityat whatever the bracket scan produced. Concrete trace: registry line- qproj [no-mistakez +hardened] - fixture-> awk emitsno-mistakez off hardened-> the case at line 122 warns and rewrites mode/yolo ->emit no-mistakes off hardened->fm-project-mode.sh --quality qprojprintshardened, not the documentedstandard. 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 atstandard. 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 EXIToverrides the sharedtrap fm_test_cleanup EXITarmed at tests/lib.sh:307, anddelivery_cleanupnever callsfm_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 -> onlydelivery_cleanupruns -> the/tmp/fm-task-delivery.XXXXXXtree created byfm_test_tmprootat line 23 (which now also holds real git repos and worktrees frommake_spawning_home) and the/tmp/.fm-test-cleanup.$$.XXXXXXregistry file both survive, plusfm_test_reap_tracked_pidsnever 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 atfm_test_cleanup, so the signal paths and the normal path now clean up different sets. Fix: appendfm_test_cleanupas the last statement ofdelivery_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 faketmuxthat exits non-zero backstops the cases that are meant to get past them, so no window or worktree is ever created." The change addsmake_spawning_home/run_spawning, whose cases deliberately run a full ship spawn to completion: they build a real git worktree viafm_git_worktree, install a fake tmux that exits 0 and answers#{pane_current_path}, letfreshen_spawn_worktree_basereset the worktree, and create/tmp/fm-<id>. 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 hardenedscaffolds a brief whose quality-gate section tells the worker that$FM_ROOT/bin/fm-quality.shdrives the loop and that$FM_ROOT/bin/fm-quality.sh --helpis authoritative,fm-spawn.sh --quality hardenedaccepts it, andquality=hardenedplusbase_sha=land in the task record - whilebin/fm-quality.shdoes 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+hardenedtoken but no producer for it..agents/skills/project-management/SKILL.mdis the procedure firstmate follows to add or create a project and write itsdata/projects.mdline: lines 40-45 enumerate the four delivery postures the captain chooses from, and line 51 documents+yoloas the one optional flag. Neither mentions+hardened. Concrete consequence, verified by running the real script:bin/fm-project-mode.sh --quality <proj>returnsstandardfor every project registered through that documented path, sobin/fm-spawn.sh:1798andbin/fm-promote.sh:137can 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 tostandard. The only way a+hardenedtoken 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+hardeneda real token indata/projects.md, read bybin/fm-project-mode.sh --qualityand acted on bybin/fm-spawn.sh:1799andbin/fm-promote.sh:137, and.agents/skills/project-management/SKILL.mdnow documents how a captain registers it. Two summary docs still describe the registry grammar as a delivery mode plus an optional+yoloflag only:docs/architecture.md:253("records each project's standing posture and optional+yoloflag ... a ship spawn that drops below the registered rigor prints a deviation notice") andREADME.md:48("with an optional+yoloautonomy 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 casesbin/fm-project-mode.sh <name>andbin/fm-project-mode.sh --quality <name>driven against a registry carrying[direct-PR],[direct-PR +hardened],[+hardened local-only]and an unregistered nameDifferential run of the base-commitbin/fm-project-mode.shvs 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-PRrun 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 withdiffandsha256sumbin/fm-brief.sh ship-hard guarded --mode direct-PR --quality hardenedthendiffagainst the standard brief to show the added Quality gate section andQuality contract: quality=hardenedlinebin/fm-spawn.sh ship-hard <proj> --mode direct-PR --yolo off --quality hardenedthrough a fake tmux backend, then readstate/ship-hard.metaforquality=andbase_sha=Committed two rounds of work into the task worktree so HEAD~1 no longer describes the base, thenbin/fm-spawn.sh --relaunch ship-hardand re-read the record to confirm base_sha was read back, not recapturedbin/fm-spawn.sh ship-plain <proj> --mode direct-PR --yolo offwith no --quality - recordsquality=standard, brief carries no Quality contract linebin/fm-spawn.sh ... --quality hardenedagainst a quality-silent brief and--quality standardagainst a hardened brief - both refused;--quality standardagainst a quality-silent brief acceptedbin/fm-brief.sh --helpand the README / project-management SKILL.md copy, to check hardened is presented as the fourth option on the registration listgrep -n "quality posture resolves at intake" AGENTS.md- the single D9 sentence at line 289AGENTS.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.