feat(bin): detect harness build drift and repair instruction-surface consistency - #1043
yomi202703 wants to merge 6 commits into
Conversation
…ft detection Six consistency defects in firstmate's own shared instruction surface, plus the mechanism that keeps the largest one from recurring. Stow routed through a named structure that did not exist. All four sites deferred to "AGENTS.md section 6, Knowledge routing" and to "that table", but AGENTS.md has no such heading and no table rows anywhere. Rewrote the pointer rather than adding a heading and table to AGENTS.md: AGENTS.md is loaded by every session of every fleet member, and a correct pointer costs nothing. All four sites now name section 6's "Route durable knowledge to its most specific owner" list, which is greppable in AGENTS.md. Bearings had no inbound reference from the always-loaded contract, unlike every sibling captain-invocable skill. Added one inline trigger line in section 9, which owns captain-facing reporting, stated as a condition rather than a pointer. The opencode busy-queued-Enter contract was stated in full in both harness-adapters and afk. harness-adapters keeps it: it is the harness-knowledge owner, the contract is a harness behavior rather than an away-mode behavior, and every other consumer (fm-send, the daemon) already reads harness facts from there. afk now carries a one-line cross-reference. The surviving copy is unchanged, including its regression-test names. bin/fm-tmux-lib.sh's header keeps the mechanics, which is its own tier. afk stated two of its own contracts twice. The max-defer escape and the verified type-once submit model each survive once, merged so no fact is lost from either copy: the earlier max-defer copy carried the defense-in-depth framing and the wedge-alarm evidence pointers, the later one carried the "300s" unit; the later submit-model copy carried the "reports empty as its caller-facing success verdict" precision and "shared-ghost-aware". The Injection hardening bullets are now cross-references. firstmate-coding-guidelines:15 carried a historical measurement (585 to 958 lines) inside a forward instruction. AGENTS.md is now 500 lines, so the figure read ambiguously as a ceiling, and it changed no reader's behavior. The line now states the rule and the reason without it. pi is documented as verified but resolves nothing on this machine. Per the captain's decision the surface is kept and marked, not removed: the pi section now records the absence as a dated observation and states that its facts are unverified against any present build. Recorded facts, AGENTS.md section 4's harness list, and the tracked .pi/extensions/ files are untouched, and no spawn-time refusal was added. Harness facts had no drift check, which is why the others matter less. Every fact was stamped against one build and nothing ever compared those stamps to the installed runtimes, so a fact stopped describing reality the moment a harness updated and no signal fired. Measured on 2026-07-25, all five had drifted. The stamps are now machine-readable: one fenced fm-harness-builds block in harness-adapters/SKILL.md owns the build each harness section's facts were checked against, keeping one owner per stamp while the doc stays readable. The per-fact dated stamps in the prose remain historical observation records. bin/fm-harness-drift.sh parses that block and distinguishes three outcomes per harness: stamp equals installed build (silent), stamp differs in either direction (drift, so a doc ahead of the machine is drift too), and harness absent. It is detect-only by construction - never edits, never installs, always exits 0 - and bin/fm-bootstrap.sh runs it among the read-only detect checks, so it also runs in a lock-refused session. bootstrap-diagnostics owns the response and AGENTS.md carries the one-line trigger. Drift is normal; the defect fixed is that it was invisible, so this is deliberately not a gate. tests/fm-harness-drift.test.sh covers the three outcomes, the doc-ahead case, the real --version shapes of all five harnesses, the malformed and missing stamp-source reports, and the read-only property. docs/verification/harness-builds.md records the mechanism with the date, exact commands, and exact output. Hermetic suites that assert bootstrap silence control PATH, so they had no harnesses at all and would have failed on five drift lines. tests/lib.sh gains one shared fm_fake_stamped_harnesses helper - a single owner rather than four copies - that declares each recorded harness present at its recorded build, so the real check still runs and silence still means silence. Verified: bin/fm-lint.sh clean, bin/fm-doc-audience-check.sh ok, the pure-contract-unit family green, and every bootstrap-invoking suite green. tests/fm-fleet-sync.test.sh's "provably-stale lock" case fails identically on the base commit and is unrelated to this change.
Mark the documentation placeholder at line 275 with leak-ok so the pre-push gate accepts the instruction-surface fix branch. The scanner treats <world>/home/state as a rooted home path; the line is a comment placeholder, not a real path.
|
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#1043 at |
|
Automated reminder: this PR still looks blocked on a rebase or merge conflict fix. If you are still interested, please rebase onto the current base branch, resolve the conflict, and push. If I do not hear back, I may close this as inactive. |
|
I am closing this because it has been waiting on a rebase or merge-conflict fix since 2026-07-26, and I have not seen a comment or push since then. If you still want to keep working on this, please reopen it or open a new PR and mention this one. Happy to take another look when there is an update. |
Intent
Clear the pre-push leak-check gate that blocked the already-complete instruction-surface fix branch (fm/instr-fix at 357a778). That branch carries six instruction-surface fixes plus a detect-only harness build-drift check; that content is done and must not be redone or expanded. The only new change is a single leak-ok end-of-line marker on the one remaining HOMEPATH false positive in tests/fm-secondmate-harness.test.sh:275 (a documentation placeholder /home/state that the scanner misreads because > is not path-blocklisted). Do not add more markers, do not change the detector, do not amend 357a778, do not merge - open a PR for the upstream maintainer. Push must succeed without --no-verify.
What Changed
bin/fm-harness-drift.sh, a detect-only check that parses a new machine-readablefm-harness-buildsstamp block in.agents/skills/harness-adapters/SKILL.mdand reports, per harness, whether the recorded stamp matches the installed binary, drifts in either direction, is absent, or is unreadable. It never edits or installs and always exits 0.bin/fm-bootstrap.shruns it as aBOOTSTRAP_INFO: HARNESS_DRIFT:fact only underFM_BOOTSTRAP_VERBOSE_FACTS=1, after review demoted it from an unconditional session-start probe; otherwise it is run on demand.stowknowledge-routing pointer now names a section and list that actually exist in AGENTS.md, AGENTS.md section 9 gains an inlinebearingstrigger, the opencode busy-queued-Enter contract and afk's two self-duplicated contracts collapse to one owner each with cross-references,firstmate-coding-guidelinesdrops a stale line-count measurement from a forward instruction, and the pi adapter record carries a dated currency caveat instead of an unqualified verified header.tests/fm-harness-drift.test.shcovering the outcome cases, the real--versionshapes of all five harnesses, malformed and missing stamp sources, the read-only property, and a guard that bootstrap invokes the script exactly once behind the verbose flag; adds a sharedfm_fake_stamped_harnesseshelper intests/lib.shso hermetic silence-asserting suites still exercise the check; records the mechanism indocs/verification/harness-builds.mdand indexes it in README,docs/scripts.md, anddocs/documentation-audiences.json; marks one known HOMEPATH false positive intests/fm-secondmate-harness.test.shwithleak-okso the pre-push leak gate passes without--no-verify.Risk Assessment
✅ Low: Both round-2 findings are fully resolved with honest documentation and a strictly stronger, less brittle regression guard that I verified extracts the correct lines, leaving only one informational note about the test also pinning the call site as mandatory.
Testing
I reproduced the end-user push experience by running the actual leak-check pre-push hook with the stdin git supplies for a new-branch push: at the pre-marker commit it prints GATE: fail with the HOMEPATH hit on tests/fm-secondmate-harness.test.sh:275 and exits 1, and at the branch tip it prints GATE: pass over 17 scanned objects with one suppressed line and exits 0, so the push clears without --no-verify. I confirmed the branch adds exactly one leak-ok marker, that it suppresses a real hit (no stale-suppression warning), and that no scanner or detector file is modified. The behavior test owning the edited file passes, and because the two review commits after the marker also touched the bootstrap and drift-check surfaces I ran those two suites as well; all passed. Working tree left clean, with evidence written only to the evidence directory.
Evidence: Real pre-push hook transcript (blocked before marker, passes after)
--- BEFORE the marker: HEAD = 617b22c --- [HOMEPATH] 絶対ホームパス ── ~ / $HOME へ相対化 (1) tests/fm-secondmate-harness.test.sh:275 /home/s*** [SCAN-SET] 列挙 18 件 / 本文走査 18 GATE: fail (巻き込み危険 1 件 ...) leak-check(push): push を中止 ── 上の hit を処置してから再 push hook exit = 1 --- AFTER the marker: HEAD = 9f8daa6 --- [SCAN-SET] 列挙 17 件 / 本文走査 17leak-okで全検出器を通さなかった行: 1 GATE: pass (PII/secret/実データの巻き込みなし ── 17 件を全数走査) hook exit = 0Evidence: leak_scan.sh --records-from before/after comparison
Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
.agents/skills/harness-adapters/SKILL.md:276- A machine-local observation is baked into a shared tracked instruction surface: "Pi is not installed on this machine (observed 2026-07-25):command -v piresolves nothing.". Per AGENTS.md section 12,.agents/skills/is loaded verbatim by every running firstmate and secondmate home, so on any home where pi is installed this bolded lead sentence is simply false, and the following sentence ("Every fact in this section ... is therefore unverified against any currently present Pi build") inherits that falsehood. Line 279 acknowledges the drift check "self-corrects on a home where pi is present" - which is the argument for lettingfm-harness-drift.shreport the absence at session start and not freezing it into the skill text. This is the same rotting-fact problem this branch deliberately removed from firstmate-coding-guidelines (the "grew from 585 to 958 lines" claim)..agents/skills/harness-adapters/SKILL.md:46- All five recorded stamps ship known-drifted (docs/verification/harness-builds.md:549-554 records the check emitting a drift line for every one of claude, codex, grok, opencode, and pi on introduction). Combined with AGENTS.md:472 and bootstrap-diagnostics/SKILL.md:95, which addHARNESS_DRIFT:to the list of actionable lines that mandate loading thebootstrap-diagnosticsskill, every session start of every fleet member now prints five drift lines and is required to load that skill - only to read "This never blocks work and needs no captain report on its own" (bootstrap-diagnostics/SKILL.md:104). The signal is on from day one and can only be cleared by manually re-verifying five harnesses, so the one failure mode it exists to catch (a drifted busy signature) arrives inside permanent noise, and every session pays the skill-load context cost that firstmate-coding-guidelines exists to prevent. Consider either re-stamping to the currently installed builds as part of this change, or exempting HARNESS_DRIFT from the mandatory bootstrap-diagnostics load trigger.bin/fm-bootstrap.sh:874- The drift check is invoked synchronously in the session-start bootstrap path and probes every recorded harness with<harness> --versionon every run, with no caching. Measured on this machine:bin/fm-harness-drift.shtook ~0.94s wall (opencode alone ~0.45s), added to every session start. Worst case is bounded only by FM_HARNESS_DRIFT_TIMEOUT (default 10s) per harness, so five hanging or slow-starting harnesses can add ~50s to session start before the digest appears. The same cost is multiplied across the test suite - fm-x-mode.test.sh alone invokes fm-bootstrap.sh a dozen-plus times with the ambient PATH, where the probes hit real binaries. A build stamp changes at most once per harness upgrade, so a cached result (e.g. a state marker keyed on the harness binaries' mtimes, refreshed daily) would preserve the signal at near-zero recurring cost.tests/fm-secondmate-harness.test.sh:275- leak_scan.sh's suppression is line-scoped and category-wide (SUPPRESS = 'leak-ok'... "この文字列を含む行は全カテゴリで除外"): the markercontinues past SECRET, EMAIL, HOMEPATH, and DENY for that whole line, not just the HOMEPATH detector that false-positived. Here the suppressed line is a fixed prose comment, so the exposure is negligible, and the scanner's own STALE-SUPPRESSION audit reports the marker is actively suppressing a hit rather than sitting dead. Noting the property only so a future edit to that comment line is not assumed to be re-scanned.tests/fm-secondmate-harness.test.sh:275- The intent says "do not amend 357a778". The branch head's parent is 617b22c, not 357a778: same author timestamp (2026-07-25 21:12:43 +0900), new committer timestamp, reparented from 3f71cdd onto 34213e6. The rewrite is a clean rebase that absorbs upstream docs: remove superseded interim quota-window rule #1039 (dropping the superseded interim quota-window rule from AGENTS.md:171 and its assertion phrase from tests/fm-instruction-owners.test.sh:118); all six instruction-surface fixes and the drift check are byte-identical. No content was redone or expanded, but the branch no longer carries the commit id named in the intent.bin/fm-harness-drift.sh:25- The either-direction drift rationale is restated near-verbatim in four places: this header (lines 24-27), harness-adapters/SKILL.md:151, docs/verification/harness-builds.md:499, and bootstrap-diagnostics/SKILL.md:103. The owner split is otherwise clean (script owns flags and line formats, skill owns stamps, verification doc owns evidence, bootstrap-diagnostics owns the response), so a single owner for the rationale with pointers from the other three would match the single-ownership discipline the rest of this branch is enforcing.🔧 Fix: demote harness drift check to opt-in informational fact
4 infos still open:
bin/fm-bootstrap.sh:47- Two surfaces still assert the pre-demotion guarantee. bin/fm-bootstrap.sh:47 says the comparison runs "so a documented harness fact cannot silently expire", and docs/verification/harness-builds.md:5 says "This record supports the current guarantee that a documented harness fact cannot silently expire". After this commit nothing runs the comparison by default: on a home that never sets FM_BOOTSTRAP_VERBOSE_FACTS=1 and never invokes bin/fm-harness-drift.sh, a harness update expires a fact with no signal at all - precisely the state bin/fm-harness-drift.sh:7-9 describes as the defect the check was written to fix. The demotion is the right call and harness-adapters:43 states the new contract correctly ("Run it deliberately, before relying on a harness fact you have not re-verified yourself"); only these two leftover "cannot silently expire"/"guarantee" phrasings now overstate what ships.tests/fm-harness-drift.test.sh:212- The new regression guard proves that a guarded invocation exists (guardednon-empty, containing BOOTSTRAP_INFO) but never proves that no unguarded invocation exists. A future edit that adds a second, unconditional"$SCRIPT_DIR/fm-harness-drift.sh"outside the FM_BOOTSTRAP_VERBOSE_FACTS block leaves this test green while reintroducing the exact per-session-start probe cost this commit removed - and tests/fm-bootstrap.test.sh's silence assertions would not catch it either, because fm_fake_stamped_harnesses seeds the harnesses at their recorded stamps so the check stays silent there. Inverting the awk to collect occurrences whereguardis 0 and failing on any would close the hole. Related brittleness: the extractor keys on the exact source shape (^if [ "${FM_BOOTSTRAP_VERBOSE_FACTS:-0}" = 1 ]at column 0, terminated by^fi$), so an indentation or nesting change fails the test with a misleading message even when behavior is correct.tests/lib.sh:114- With the drift check gated behind FM_BOOTSTRAP_VERBOSE_FACTS=1, fm_fake_stamped_harnesses is load-bearing at only one of its four call sites. No script under bin/ ever sets that variable (it is caller-supplied only), and tests/fm-bootstrap.test.sh:743 is the sole test that passes it, so the seeding at tests/fm-session-start.test.sh:86, tests/fm-secondmate-harness.test.sh:780, and tests/fm-secondmate-sync.test.sh:353 now installs five fake harness binaries into hermetic fakebins for a check that never runs in those suites. It is inert rather than harmful - no bin/ code doescommand -v <harness>- and the helper's updated comment already narrows its stated purpose to the verbose path. Recording it only so the three redundant call sites are a known cleanup, not a lost invariant; not proposing removal in this tightly scoped round..agents/skills/harness-adapters/SKILL.md:42- The stated intent says the six instruction-surface fixes plus the drift check "is done and must not be redone or expanded" and that "The only new change is a single leak-ok end-of-line marker". Commit 0a44d94 edits eight files of that content (harness-adapters, bootstrap-diagnostics, AGENTS.md, fm-bootstrap.sh, fm-harness-drift.sh, harness-builds.md, and two test files). This is not treated as a contradiction because the round-1 review loop carries explicit user direction to make exactly these three corrections ("All three are defects this branch itself introduced, so fix them; do not expand scope beyond correcting them"), which is a later and more specific authorization from the same user. The commit stays inside that authorization: it touches only the drift-check surfaces, and bootstrap-diagnostics/SKILL.md is now byte-identical to the base commit. Recorded for traceability only.🔧 Fix: state drift check gap honestly, harden invocation guard test
1 info still open:
tests/fm-harness-drift.test.sh:220- The hardened guard asserts the invocation count is exactly 1, which encodes two invariants: no unguarded call (the requested one) and at least one call. The second half means a future maintainer who takes the drift check fully on-demand by deleting the bootstrap call site entirely - the direction round 1 explicitly preferred ("Removing the call site is preferred over making the probe faster") - gets a test failure reading "bootstrap must invoke fm-harness-drift.sh exactly once" even though that state is strictly cheaper and still correct. It fails closed with a legible message rather than silently, and it accurately pins today's design, so this is a note rather than a defect; changing-eq 1to-le 1would keep the anti-regression half without forbidding removal.✅ **Test** - passed
✅ No issues found.
sh ~/.claude/skills/leak-check/hooks/pre-pushfed the real git pre-push stdin record for a new-branch push of fm/instr-fix, at 617b22c (fail, exit 1) and at 9f8daa6 (pass, exit 0)sh ~/.claude/skills/leak-check/leak_scan.sh --records-from <git diff --raw -z 34213e6..<sha>>for both commits, confirming the single HOMEPATH hit disappears and one line is recorded as suppressed with no STALE-SUPPRESSION warningbash bin/fm-test-run.sh tests/fm-secondmate-harness.test.shbash bin/fm-test-run.sh tests/fm-harness-drift.test.sh tests/fm-bootstrap.test.shgrep -rn "leak-ok"over the tracked tree plusgit diff 34213e6..9f8daa6 -U0 | grep -c '^+.*leak-ok'to confirm exactly one marker was added and only one existsgit diff --name-only 34213e6..9f8daa6 | grep -iE 'leak|scan'to confirm the detector is untouched🔧 **Document** - 1 issue found → auto-fixed ✅
.agents/skills/harness-adapters/SKILL.md:278- The sixth instruction-surface fix described in 617b22c's commit message - marking the pi section as unverified against any present build, kept-and-marked per an explicit captain decision - is no longer in the tree. Review commit 0a44d94 removed all four sentences of that marking because one of them ("reports this absence at session start") became false when the drift check went opt-in. The pi section now reads "## pi (VERIFIED 2026-06-11)" with no currency caveat, while docs/verification/harness-builds.md, added by this same change, recordspi: NOT INSTALLED. I did not restore it: the user intent freezes the branch's six fixes as done and not to be redone, and there is a real placement argument for the removal - "pi is not installed on this machine" is a home-local observation, and its owner is now the dated evidence in docs/verification/harness-builds.md plus the drift check itself, not the always-shipped shared skill. The residual gap is only that the pi header claims currency the generic "Recorded build stamps" caveat softens rather than states. Maintainer call: accept the generic caveat as sufficient, or re-add a one-line pi caveat without the false session-start claim.🔧 Fix: restore dated currency caveat on pi adapter record
✅ Re-checked - no issues remain.
✅ **Lint** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.