Conversation
Adds bin/fm-verify-delivered.sh with two modes: brand-identity, which greps a fetched rev for files referencing a brand-identity helper with no graph identifier, and <task-id>, which extracts a brief's acceptance claims as a checklist. The verdict is the exit status, and "inspected nothing" (3) and "search failed" (4) are their own non-clean outcomes so an inspection that examined no files can never be reported as clean. The ERR trap fires inside the mode functions, a failed fetch fails closed, and the help and violation wording no longer overclaim what the check proves. AGENTS.md hard rule 5 now points at the script, and docs/scripts.md carries its toolbelt row. Source branch: fm/fm-verify-delivered-false-clean (PR #3).
… briefs Gives every crewmate a durable working memory it maintains itself at data/<id>/log.md, so a worker whose context fills can be relaunched and resume from disk instead of losing its understanding of the task. The log is deliberately not the status file: status appends wake firstmate and must stay sparse, while the log never wakes anything and can be as long and as candid as the task needs. It carries what the supervisor channel must not - established facts, rejected approaches, and steers received after dispatch - and it survives teardown with the task's other data. stuck-crewmate-recovery now reads the log before relaunching and appends a brief note only for what the log cannot hold, rather than reconstructing the worker's understanding by hand. Source branch: fm/crew-worklog (PR #4).
Every scaffolded status instruction is now one copy-pasteable line that stamps the append with a UTC timestamp, and fm-classify-lib.sh owns the stamp format and the strip used by every parser, so legacy untimestamped lines stay first-class and never read as an error or a guess. fm-crew-state.sh reports the age of the evidence behind the state it reports, so a done from two hours ago never reads the same as one from ten seconds ago: a run-step or pane read is live, a status-log verdict carries the reported line's own age, and an unstamped line reads unknown. The evidence-age test is adapted to main's current busy-verdict contract: reaching the status-log fallback now requires an armed idle record, and asserting the live pane path requires a semantic busy record that outranks it. Both were introduced on main after this work branched. Source branch: fm/status-timestamps.
Adds bin/fm-completeness-check.sh with its Z3 engine (fm-completeness.py) and rules file, wired into fm-teardown.sh and fm-merge-local.sh so a done, teardown, or merge claim is proved consistent with the directives before the irreversible step. Hard rules gate and soft rules score, so a blocked claim names the invariant it violated instead of refusing anonymously. The gate is optional and steps aside when python3 cannot import z3, leaving the existing bash safety checks as the hard guarantee. FM_COMPLETENESS_GATE=0 disables it; FM_COMPLETENESS_STRICT=1 refuses instead of stepping aside. Bootstrap reports the capability as a BOOTSTRAP_INFO fact under FM_BOOTSTRAP_VERBOSE_FACTS=1 and never as a missing tool to install. Two hardening properties carry over from the review rounds on the source branches. Invalid facts exit 64 rather than falling through the engine's error path as a pass, and both call sites treat 64 as blocking. The gate no longer re-derives whether remote-backed ship work has landed: fm-teardown.sh owns that test, and the duplicate git-only derivation false-blocked squash-merged PRs whose branches were deleted. Local-only work, which has no PR to squash, keeps its own merge derivation with a master fallback and treats an undeterminable default branch as unmerged. A local merge asserts the captain's approval through FM_CAPTAIN_APPROVED (granted|yes|1|true, or not_required under yolo). Source branches: feat-completeness-gate, feat-cross-repo-grounding, fm/fm-rebase-upstream, rebase-onto-upstream (consolidated; see PR body).
Adds bin/fm-ground.sh, which resolves a repo's established facts from data/repos/<key>.md by name or path, and has fm-brief.sh inject them into every ship and scout scaffold as a binding do-not-re-derive section. Workers start from the repo's real architecture, studied tool choices, and hard constraints instead of re-improvising them and reaching for whatever tool is cheapest to reach. A `repo-path:` line makes a repo outside projects/ first-class, so firstmate can orchestrate it in place without cloning it. Grounding is additive and never blocks a dispatch: a repo with no ground-truth file scaffolds normally and warns on stderr, so the gap is audible rather than silently guessed. Adds the brief-scaffold coverage this feature never had: ground truth reaching both ship and scout briefs, and the ungrounded path still scaffolding while warning. Source branch: feat-cross-repo-grounding (also open as PR #1).
Two consolidated features arrived without tests, and --changed selection refused bin/fm-ground.sh, bin/fm-completeness.py, and bin/fm-completeness.rules.json for having no consuming suite. Adds tests/fm-ground.test.sh: verbatim resolution, a name and a path resolving to the same key, .git and trailing-slash normalization, --path, --list, --check, the usage error, and the two silent non-fatal paths that keep grounding from ever blocking a dispatch. Also asserts that the completeness gate ships its default rules file and engine. Without them every verdict in that suite that does not override FM_COMPLETENESS_RULES would fail open and still print ok, so the suite could pass while proving nothing.
…p fact gating FM_VERIFY_REPO and FM_VERIFY_REV were missing from the runtime env-var reference that this branch's other new variables were added to, and architecture.md implied the completeness-gate bootstrap fact prints on a normal run when it is emitted only under FM_BOOTSTRAP_VERBOSE_FACTS=1.
…ight error routing
…p dedup, and test map
Author
|
Closing: opened against the wrong repository by a misconfigured local gate. The work belongs on the tomharper/firstmate fork and is being reopened there. The branch is left in place. |
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
Consolidate every outstanding firstmate fix from seven open branches into ONE branch and ONE PR, so the captain's fork rebases onto a moving upstream once instead of seven times. The PR MUST open on tomharper/firstmate with base main. Earlier attempts opened on kunchenguid/firstmate because the local gate was configured fork-contribution style (push to fork, PR against origin=upstream); the managed clone's origin has since been corrected to the fork, and the opened PR's owner must still be read back and verified because that failure mode is silent and has already bitten several PRs.
Four of the seven branches were lineage variants of ONE feature (the Z3 completeness gate) across three SHA lineages, not four features; fm/fm-rebase-upstream even applies the same five gate commits twice on itself. The result is five genuinely distinct fixes:
DELIBERATE DECISIONS a reviewer reading only the diff would not know. The gate version was chosen by content, not recency: two branches carry byte-identical gate files and a third a near-identical copy, and feat-cross-repo-grounding's 9568dac won because it is the only lineage where invalid facts exit 64 and BLOCK (in the others a typo or corrupted record falls through the engine's error path as a PASS) and the only one whose fm-merge-local.sh caller treats 64 as blocking. Three branches fix the SAME bug (the gate false-blocking teardown of squash-merged work) three different ways; the in-gate fix was taken so every caller benefits, not just teardown, and the local-only landedness derivation was deliberately KEPT because local-only work has no PR to squash-merge so plain git can decide it correctly - that is what lets the gate block an unmerged local-only branch before fm-merge-local.sh runs. For remote-backed ship work the gate defers COMPLETELY to fm-teardown.sh per AGENTS.md hard rule 3. The call-site split from fm/fm-rebase-upstream is intentionally NOT carried because it would add a duplicate invocation changing no outcome and its call sites predate exit 64.
PR #1 was explicitly evaluated: the captain called it out of date, which describes the branch (265 commits behind, failing check) and not the feature, so the ground-truth work is included in its newest form with the test coverage it never had.
TWO DELIBERATE DROPS: bin/pycache/fm-completeness.cpython-313.pyc, a compiled build artifact committed on all four gate branches, and the redundant .gitignore pycache/ addition, because main already carries both patterns. Nothing else from the seven branches is absent.
ADAPTATIONS forced by main's drift, all deliberate: the bootstrap capability line is emitted as a BOOTSTRAP_INFO fact behind FM_BOOTSTRAP_VERBOSE_FACTS=1 matching main's current convention; README toolbelt/env/test-list rows are not carried because main moved those surfaces into docs/; AGENTS.md additions were kept minimal per firstmate-coding-guidelines size discipline.
REVIEW FIXES ALREADY APPLIED AND ACCEPTED on this branch across earlier runs of this same pipeline - do not re-litigate them. The gate's dirty-worktree filter now matches fm-teardown.sh's residue filter including the grok/kimi turn-end markers, so the gate cannot refuse a teardown the guarded script considers clean. The pending-reply dedup was restored from substring back to whole-line semantics via status_line_body under the stamp contract. Both fm-completeness-check.sh invocations forward FM_HOME/FM_STATE_OVERRIDE/FM_DATA_OVERRIDE explicitly like their neighbours. AGENTS.md hard rule 5 was SOFTENED BY EXPLICIT CAPTAIN DECISION to offer bin/fm-verify-delivered.sh as available evidence rather than a mandatory precondition, because that script's only verdict-producing mode is hardcoded to one private downstream project while this repo ships to other users; generalizing the script was explicitly ruled OUT OF SCOPE. fm-procevent-remote-reply.sh's validator now folds incoming lines through status_line_body so stamped and legacy unstamped remote-secondmate replies both validate, keeping the stamp contract's single owner rather than copying its glob. And a malformed rules-file weight now fails open as a rules error instead of exiting 64, keeping exit 64 strictly for invalid FACTS.
HARD CONSTRAINT: all seven source branches must be left exactly as found, never deleted, force-pushed, or rewritten, because they are the only copy of this work and the recovery path. Verified untouched at c98385b, 9568dac, 39c5b05, e3581a5, 8c81c17, 7c8ced1, 469cec8.
A further accepted review round is also already applied and must not be re-litigated: the remote-reply continuity-broken escalation is now stamped through fm-classify-lib.sh's writer so no firstmate-side append is left unstamped; both gate call sites now treat rc 0 as the ONLY proceed case and block on every other rc, so a missing or non-executable gate wrapper can no longer be read as approval (the documented fail-open behaviour is unchanged because the gate itself exits 0 for SAT, for the off-switch, and for fail_open); the blocked-verdict extraction no longer anchors on the dead 'counterexample' key so a blocked claim still names its violated invariant; and the unused 'unpushed' revision walk moved inside the local-only branch that is its only reader, a cost and placement change that alters no resolved value.
PIPELINE CONTEXT: review and test already PASSED at an earlier head in this same pipeline. Two subsequent runs failed for purely ENVIRONMENTAL reasons, never a defect in this branch: one hit a Claude session limit at the document step, and one lost the claude binary from the daemon's PATH mid-run while that binary was being reinstalled. Both have cleared, the agent is verified runnable, and custody was returned through the gate's own recovery path each time.
KNOWN PRE-EXISTING FAILURES, not regressions: fm-teardown, fm-session-start, fm-pi-watch-extension and fm-bearings-snapshot fail identically against a pristine main extract. fm-teardown matters most because this branch adds a gate call to fm-teardown.sh, but it is not the cause: the same assertion fails with FM_COMPLETENESS_GATE=0 and on pristine main where the gate does not exist. They are environment-dependent and out of scope.
What Changed
bin/:fm-verify-delivered.sh, which checks a task's delivery claims against merged code and treats "inspected nothing" and "search failed" as their own non-clean exits so an inspection that examined no files can never report clean;fm-ground.sh, which resolves per-repo ground truth fromdata/repos/<key>.md; and an optional Z3-backed completeness gate (fm-completeness-check.sh,fm-completeness.py,fm-completeness.rules.json) that derives task facts and solves them against a rules file, exiting 64 on invalid facts, failing open on rules-file breakage, and deferring entirely tofm-teardown.shfor remote-backed ship work. The gate is wired intofm-teardown.shandfm-merge-local.sh, both of which now treat rc 0 as the only proceed case and print cause-specific remediation when the gate blocks.fm-brief.shscaffolds an agent-maintained working log atdata/<id>/log.md(deliberately separate from the status file, which wakes firstmate and must stay sparse) and injects the resolved per-repo ground truth into ship and scout briefs;fm-classify-lib.shnow stamps every status append with a timestamp and owns the stamp contract, withfm-crew-state.sh,fm-fleet-snapshot.sh,fm-fleet-view.shandfm-secondmate-report.shreporting evidence age from it, andfm-pending-reply-lib.sh/fm-procevent-remote-reply.shfolding lines throughstatus_line_bodyso stamped and legacy unstamped replies both validate.bin/__pycache__/*.pycbuild artifact and a redundant.gitignoreentry, both already covered by main.Risk Assessment
Testing
I ran the nine targeted suites covering this branch's own surfaces (completeness gate, verify-delivered, brief, ground, crew-state, pending-reply, remote-reply, watch-triage, fleet-snapshot-view) and they all pass, with the z3 solver present so the gate's real SAT/UNSAT matrix executed rather than skipping. Because passing suites alone are not evidence of the intent, I also drove all five consolidated fixes through their actual operator-facing CLI into one transcript: fm-verify-delivered returning CLEAN, VIOLATIONS, INSPECTED NOTHING and SEARCH FAILED as four distinct verdicts against purpose-built repos; a real ship brief scaffolded with the repo's ground truth injected as a binding do-not-re-derive section, the working-log section, and the line stating the log never wakes firstmate; timestamped status appends read back as ages 0s, 19h1m and unknown for a legacy line; the gate blocking and clearing scout, ship and merge claims, exiting 64 on an invalid fact while a malformed rules weight fails open (and refuses with exit 3 under strict mode); a real three-step fm-teardown.sh run against the live gate that refuses a dirty worktree with "commit or stash", refuses the committed-but-unmerged local-only branch with the fm-merge-local.sh remediation instead, then completes once the work is merged; and fm-merge-local.sh blocking on gate rc 126 and 127. I additionally confirmed the two deliberate drops (no tracked .pyc, .gitignore unchanged from main). The only failure anywhere is the pre-existing fm-teardown herdr-preflight assertion the intent declares out of scope, and I re-confirmed it is independent of this branch by reproducing it with FM_COMPLETENESS_GATE=0. No visual artifacts apply: every surface this change touches is a shell CLI with no rendered UI, so CLI transcripts are the end-user experience. PR-owner verification is the push/PR phase's responsibility and was not performed here.
Evidence: End-to-end CLI transcript of all five consolidated fixes
Evidence: Reproducible driver that generated the transcript
Evidence: The gate in the lifecycle: fm-teardown.sh refusing by cause, then clearing
Evidence: fm-verify-delivered.sh: unknown is never reported as clean
Evidence: Exit 64 is strictly for invalid FACTS; a rules defect fails open
Evidence: Timestamped status appends read back with evidence age
Evidence: Ground truth and working log land in the dispatched brief
Pipeline
Updates from git push no-mistakes
... (3 earlier update rounds omitted to keep the PR body within GitHub's 65536-char limit; full history is in the run log.)
🔧 **Review** - 5 issues found → auto-fixed (2) ✅
🔧 Fix: fix review findings across gate, checklist, stamp dedup, and test map
3 issues (1 warning, 2 infos) still open:
bin/fm-teardown.sh:1787- The new remediation branch matches on*NO_UNLANDED_AT_TEARDOWN*and always prints the local-only merge/push text, but that invariant has two distinct causes and only one of them is the local-only unmerged case. Concrete reachable path: a ship task withmode=no-mistakes(the default) and uncommitted changes in its worktree - bin/fm-completeness-check.sh:107 prints 'uncommitted changes present in <wt>' and lines 148-152 set WORKTREE=holds_unlanded_work fromdirtyalone, so the gate exits 2 naming NO_UNLANDED_AT_TEARDOWN. bin/fm-teardown.sh:1788 then prints 'Merge the branch into local main first (bin/fm-merge-local.sh after the captain approves), or push to a fork/remote, or get the captain's explicit OK to discard, then --force.' That advice is wrong for this cause: bin/fm-merge-local.sh:31 refuses any task that is not mode=local-only, and pushing does not clear uncommitted changes either, so the only clause the operator can actually act on is--force, which discards the uncommitted work. That is precisely the outcome the new comment at bin/fm-teardown.sh:1781-1785 says the remediation exists to prevent, and the pre-empted check would have printed the correct text at bin/fm-teardown.sh:1019 ('Commit them (or get the captain's explicit OK to discard, then --force).'). The same mis-advice hits a local-only task that is merely dirty with nothing unpushed, since teardown's own path for that is also theelif [ -n "$dirty" ]branch. The new test test_blocked_completeness_gate_names_the_remediation stubs the gate's output, so it never exercises this cause. Minimal fix consistent with the single-owner rule already established: branch on the gate's own evidence line (*uncommitted changes present*is already inside$gate_out) and extract bin/fm-teardown.sh:1019 into a sibling function next to unlanded_work_remediation.bin/fm-completeness-check.sh:246- read_rules_axes validates thataxesis a non-empty dict and that no axis name or value contains a space, but never that each axis's values are a list. A rules file with a scalar such as"kind": "ship"makes[str(value) for value in values]iterate the string into ['s','h','i','p'], so the reader succeeds and emitskind s h i p; validate_axis at line 221 then rejects the correctly-derived valueshipwith exit 64, which bin/fm-teardown.sh:1791 and bin/fm-merge-local.sh:65 both treat as blocking. That contradicts the rule this commit's own comment states at line 253-254 ('A rules file that cannot be read or parsed is a rules error, never invalid facts') and the intent's 'keeping exit 64 strictly for invalid FACTS', since the facts here are valid and the rules file is the broken thing. It is reachable through the documented FM_COMPLETENESS_RULES override. Fix: addnot isinstance(values, list)to the guard at line 247 so a structurally-wrong axes object raises SystemExit(1) and takes the fail_open path at line 256 like every other rules-file breakage.bin/fm-completeness-check.sh:276- The wrapper validates and emits exactly the five axes it knows (lines 276-280), and bin/fm-completeness.py's _verify_hard only asserts axes present in the submitted facts, so any axis the rules file declares beyond those five stays a free Z3 variable and every hard rule constraining it is trivially satisfiable - the rule silently never blocks. This commit makes the rules file the authoritative vocabulary and line 272 already fails open when the file is MISSING an axis the wrapper needs, but the symmetric direction is unchecked, so an operator extending the file through the documented FM_COMPLETENESS_RULES override gets a hard rule that reads as enforced and is not. This is a pre-existing engine property rather than a regression, and it is out of the scope the fix instruction set, but it is the natural next failure now that extending the vocabulary is supported. Fix: after the five validate_rules_axis calls, fail_open when rules_axes names an axis the wrapper cannot supply, mirroring line 272.🔧 Fix: branch gate remediation by cause and harden rules-axis reads
✅ Re-checked - no issues remain.
🔧 **Test** - 2 issues found → auto-fixed ✅
tests/fm-brief.test.sh:388- tests/fm-brief.test.sh failed: five of the branch's new ship-brief invocations omitted the--modeflag that bin/fm-brief.sh has required since before this branch (present at base 3089a57), so fm-brief.sh refused with "ship briefs require --mode" and no brief was scaffolded. Beyond the hard failure, this meant the new working-log, resume-awareness, ground-truth-injection and stamped-status assertions never exercised the ship path at all. Fixed by adding--mode no-mistakesto the five ship call sites (worklog scaffold, resume-awareness, ground-truth ship brief, ungrounded-warning brief, and the stamped-status matrix); the suite now passes with all new assertions actually running.tests/fm-teardown.test.sh- tests/fm-teardown.test.sh fails onherdr-preflight-missing-adapter: the retryable pre-return refusal was not explained visibly. Confirmed pre-existing and unrelated to this branch: I extracted pristine main (3089a57) into a temp tree and the identical assertion fails there. Every completeness-gate assertion in that suite passes on this branch. Left unfixed as out of scope, matching the intent's declared known failures.bash tests/fm-completeness.test.sh— 44 assertions, z3 4.15.2 present so the solver-dependent tier ran, not skippedbash tests/fm-verify-delivered.test.sh— one regression test per outcome including both false-clean pathsbash tests/fm-ground.test.shbash tests/fm-brief.test.sh— failed on the missing--mode; fixed the five ship call sites and re-ran greenbash tests/fm-crew-state.test.shbash tests/fm-pending-reply.test.sh— whole-line dedup via status_line_bodybash tests/fm-remote-reply.test.sh— stamped and legacy unstamped replies both validatebash tests/fm-teardown.test.sh— all gate assertions pass; herdr-preflight assertion fails (pre-existing)bash tests/fm-watch-triage.test.shbash tests/fm-fleet-snapshot-view.test.shbash tests/fm-bootstrap.test.sh— exit 0, 23 ok / 0 not-okPre-existing-failure control:git archive 3089a57 | tar -x -C <tmp> && bash tests/fm-teardown.test.shin the pristine main extract — identical herdr-preflight failureManual e2e:fm-verify-delivered.sh brand-identity --no-fetchagainst a real git fixture across CLEAN / VIOLATIONS / missing-repo / unresolvable-rev (exits 0, 1, 4, 4)Manual e2e:fm-ground.sh --list,--path acme,acmeManual e2e:fm-brief.sh e2e-ship acme --mode no-mistakesandfm-brief.sh e2e-ungrounded otherproj --mode no-mistakes, inspecting the rendered ground-truth and# Working logsectionsManual e2e: executed the brief's own scaffolded status-append command, then read it back throughstatus_line_stamp/status_line_verb/status_line_age_secsfor a fresh and a 34-day-old lineManual e2e:fm-completeness-check.shacross scout-no-report, scout-with-report, unlanded ship, pushed+clean ship,--landed pusehd(exit 64), malformed soft-rule weight under--mode graded(fail-open 0, strict 3), andFM_COMPLETENESS_GATE=0Manual e2e:fm-completeness-check.sh --gate teardown --id e2e-localagainst a real local-only git repo, before and after merging the branch into local main (exit 2 then 0)Deliberate-drop checks:git ls-tree -r HEAD --name-only | grep -cE '__pycache__|\.pyc$',git diff origin/main..HEAD -- .gitignore,git show origin/main:.gitignore🔧 Fix: pass --mode to ship brief tests
✅ Re-checked - no issues remain.
bash tests/fm-completeness.test.sh(44 assertions, solver tier active — z3 4.15.2)bash tests/fm-verify-delivered.test.shbash tests/fm-brief.test.shbash tests/fm-ground.test.shbash tests/fm-crew-state.test.shbash tests/fm-pending-reply.test.shbash tests/fm-remote-reply.test.shbash tests/fm-watch-triage.test.shbash tests/fm-fleet-snapshot-view.test.shbash tests/fm-teardown.test.sh— all 3 new gate assertions pass; fails only on the pre-existingherdr-preflight-missing-adapterassertionFM_COMPLETENESS_GATE=0 bash tests/fm-teardown.test.sh— same failure with the gate disabled, confirming it is not caused by this branchManual e2e:bin/fm-verify-delivered.sh brand-identity --no-fetchagainst four purpose-built repos, exercising CLEAN(0), VIOLATIONS(1), INSPECTED NOTHING(3), SEARCH FAILED(4)Manual e2e:bin/fm-ground.sh --list/bin/fm-ground.sh <repo-path>thenbin/fm-brief.sh e2e-ship inception --mode no-mistakes, inspecting the generated brief for the ground-truth section and the working-log sectionManual e2e:bin/fm-brief.sh e2e-unknown mystery-repo --mode local-onlyto confirm an ungrounded dispatch still scaffolds and warnsManual e2e: status file written with the brief's own copy-pasteable stamp command, then read back throughstatus_line_age_secs/fm_format_age/status_line_bodyManual e2e:bin/fm-completeness-check.shacross scout/ship/merge gates,--landed pushd(exit 64), a malformed soft-rule weight (fail-open, and exit 3 underFM_COMPLETENESS_STRICT=1), andFM_COMPLETENESS_GATE=0Manual e2e: three sequentialbin/fm-teardown.sh task-x1runs against a synthetic project/worktree/state sandbox with the real gate — dirty worktree, unmerged local-only branch, then merged into local mainManual e2e:bin/fm-merge-local.sh ship-bwith the gate wrapper non-executable (rc 126) and missing (rc 127)Manual e2e:fm_status_append_oncewhole-line stamped dedup, andstatus_line_bodyfolding of stamped vs legacy unstamped remote-secondmate repliesgit ls-files | grep pycacheandgit diff 3089a57 f5efaaa -- .gitignoreto confirm the two deliberate dropsgit status --porcelainto confirm no transient testing artifacts were left in the worktreedocs/fm-test-portable-shards.md:71- The portable-serial shard table (15/18/17/19 scripts, 69 total, measured on CI run 30725985757 dated 2026-08-02) no longer matches reality: the lane now selects 87 scripts, three of which (fm-completeness.test.sh, fm-ground.test.sh, fm-verify-delivered.test.sh) this branch added. I did not refresh it because the numbers are measured evidence that can only come from per-shard timing artifacts of a green CI run of this branch, and because the drift is overwhelmingly pre-existing main churn (84 scripts before this branch), not something this change introduced. The doc itself states stale hints cost shard balance, never coverage, and --check-coverage still passes. Follow-up: refresh the hints and table from the next green run using the documented gh run download procedure.docs/configuration.md:163- Judgment call worth a follow-up, deliberately left out of scope here. configuration.md gives every other hand-authored data/ file its own operator-facing section (captain.md, captain-shared.md, learnings.md, secondmates.md), but the new data/repos/<key>.md ground-truth file has none. Its owner chain is currently AGENTS.md's data tree line -> bin/fm-ground.sh's header (key derivation, the repo-path: external-repo form, the silent-when-absent contract), plus the architecture paragraph I added. That is complete for the agent audience, so I did not open a new section rather than risk duplicating the script header. If ground truth is meant to be captain-authored setup material, a short configuration.md section pointing at the script header would be the right home.✅ **Lint** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.