Repository navigation
feat: stamp task milestones, gate promptly and report review coverage - #48
Merged
Merged
Conversation
…. The rule that broke: every test fixture that runs the real bin/fm-teardown.sh from a hand-built fake bin/ must include every sibling script teardown calls. This PR made teardown call bin/fm-task-milestones.sh (the milestone archive step). tests/fm-gotmp.test.sh builds that fake bin/ in two places, make_fake_root and the separate fixture in test_teardown_skips_gracefully_without_tasktmp, and neither had the new script, so teardown exited non-zero. Fix: symlink the real bin/fm-task-milestones.sh into both fixtures. No other test builds a per-file fake bin/ like this. Verified: tests/fm-gotmp.test.sh failed before the fix ("teardown exited non-zero with a valid tasktmp") and passes after it; shellcheck is clean. ci-1 (Behavior portable serial 5): tests/fm-backend.test.sh failed in its symlinked-prefix spawn case with "could not resolve the shared Treehouse project lock". The function behind that error (fm_treehouse_project_lock_path in bin/fm-wake-lib.sh) and its caller bin/fm-spawn.sh are not changed by this PR. The test passed on the base commit's CI, and it passes locally both on its own and in a clean checkout after replaying shard 5's exact test order up to fm-backend. I could not reproduce the failure and nothing in this diff reaches that path, so I made no code change for it. Re-running the shard should settle it; the cause on that CI runner is unconfirmed. In that local replay, fm-secondmate-restart and fm-startup-network also exited 1. Both passed in CI on this shard, so this looks like a difference in the local environment; I did not investigate it
…ng test-isolation bug. The PR's new shard order exposed it; the PR's code did not cause it. Fixed in the test fixture only; no production code changed. Root cause: in tests/fm-backend.test.sh, the spawn cases never set FM_HOME. fm-spawn then falls back to the checkout itself as the home, and fm_treehouse_project_lock_path (bin/fm-wake-lib.sh:1213) requires that home to have a state/ directory. state/ is untracked, so a fresh CI checkout does not have it. The symlinked-prefix case (test_spawn_symlinked_project_prefix_avoids_false_refusal) runs before the FM_HOME='' refusal cases, and those are what happen to create $ROOT/state. So it hit "could not resolve the shared Treehouse project lock". It only passed before because of what earlier tests in the old shard 6 left behind. The earlier rounds could not reproduce it because the local shell exports FM_HOME=~/.firstmate, the operator's real home. That masked the bug and also leaked the real home into the test. Reproducer: with the checkout's state/ removed, `env -u FM_HOME bash tests/fm-backend.test.sh` fails the same way as CI ("not ok - fm-spawn.sh should succeed for a project reached through a symlinked prefix ... could not resolve the shared Treehouse project lock"). Invariant: every fm-spawn call in this suite that reaches the Treehouse project lock must run against an isolated FM_HOME that has a state/ directory. Four places need it: run_spawn_case, used by both symlink cases, and the three inline spawns in test_spawn_default_backend_writes_no_meta_field, test_spawn_explicit_backend_flag_beats_autodetect_herdr_env and test_spawn_autodetect_nesting_resolves_tmux_silently. Those three only passed because of test order. Fix: one throwaway SPAWN_FM_HOME ($TMP_ROOT/fm-home with state/) is created next to SPAWN_HOME and passed as FM_HOME at all four sites. The FM_HOME='' refusal tests exit before the lock and are unchanged. Verified: - The test now passes with FM_HOME unset and no checkout state/ (rc=0, 28 ok). - It also passes with the operator's FM_HOME exported (rc=0). - `shellcheck -x tests/fm-backend.test.sh` is clean. - Only tests/fm-backend.test.sh changed
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
The captain asked to implement all changes proposed in the report-development speed analysis (~/.firstmate/data/geris-report-dev-speed-analysis/report.md, section 3). This task covers the two recommendations that live in firstmate tooling: recommendation 3 (gate promptly, avoid stale runs) and recommendation 5 (instrumentation of milestone timestamps). Recommendations 1, 2 and 4 go to a separate fabric_monorepo task.
3. Firstmate/tooling: gate promptly, avoid stale runs. Start after local proof; proposal: dispatch ≤15m. Isolate browser session/tab selection and unique E2E ports. Escalate base drift ≤30m instead of leaving obsolete runs overnight; preserve every managed fix via authorized custody transition. Fix stale coverage reporting; never restart shared no-mistakes daemon or silently skip coverage.
5. Instrumentation: stamp actual brief, local proof, gate start/end/round count, PR creation/merge, deployed SHA, data-ready snapshot, first live proof, UAT merge/deploy/acceptance. Keep status schema strict. Measure next week brief→accepted DEV and accepted DEV→accepted UAT medians, failures and rework count. Proposed targets above are not evidence of savings.
Authorize a Firstmate-side reader in bin/ (in scope) that reports review coverage from the existing status/outcome records; do not touch the external daemon. Proceed and ship.
What Changed
Adds
bin/fm-task-milestones.sh. It can stamp, list, time, archive and check deadlines for task milestones:stamp,records,durations,coverage,deadlines,archive. Milestones go in as strictmilestone [at=] [name=] ...lines in the existing status stream, andstatus_milestone_recordinbin/fm-classify-lib.shparses them: a closed set of names and fields, full SHAs, forge URLs required for PR and merge, and acceptance counts only withresult=passed. The classifier ignores milestone lines when it works out a worker's state or captain relevance.fm-brief.shstamps abriefmilestone when it creates a scaffold. Teardown archives milestone lines todata/<id>/milestones.statusso phases stamped after teardown still measure against the brief.Adds
fm_nm_review_coveragetobin/fm-nm-run-lib.sh. It reads the no-mistakes run record read-only (state.sqlite,mode=ro) and returnscoveredonly when the run's head and its review-approved head both match the given SHA. Otherwise it returnsstale, orunverifiedif the record can't be read. The daemon is never restarted or written to.The no-mistakes definition of done in
bin/fm-dod-lib.shchanges. Briefs and scout promotion now tell workers to:/no-mistakesthemselves within 15 minutes of local proofgate-obsoleteon base drift and escalate within 30 minutes, keeping managed fixes through an authorized custody transitionfm-watch.shwakes Firstmate once per overdue dispatch or escalation deadline on no-mistakes tasks. Tests, the test-run family mapping,AGENTS.mdanddocs/scripts.mdare updated to match.🤖 Generated with Claude Code
Risk Assessment
Testing
I ran the milestones behavior test (green) and the watcher triage file; its two milestone-deadline cases passed, and the remaining unrelated cases were still running when I stopped reading. I then drove the real CLI end-to-end in a throwaway FM_HOME: brief scaffold stamp, local proof, the 15-minute dispatch deadline, the 30-minute obsolete-run escalation and its clearing, strict-schema refusals, the archive step with late DEV/UAT stamps and readback of both durations, and review coverage read from this run's real daemon record. Everything driven live passed. The watcher deadline wakes, the full fm-teardown.sh path and a watcher under a live Herdr session were not driven live; I exercised them only through the watcher's fake-pane behavior test and the archive subcommand. The temp home was removed.
milestone [name=brief] [at=...]: ship brief scaffold created, and records prints the brief rowlive-demo 1 2 2 unknown ...(gate rounds unknown because run-A never ended, as designed)archivesubcommand that teardown invokes was driven. A full teardown needs a live task window/worktree; provide it by running it in a bin/fm-he…Evidence: Live CLI transcript: brief stamp, deadlines, schema refusals, archive + late stamps, durations, real-daemon coverage
Source: Live CLI transcript: brief stamp, deadlines, schema refusals, archive + late stamps, durations, real-daemon coverage
Evidence: Milestones behavior test log
Source: Milestones behavior test log
Evidence: Watcher triage test log (milestone deadline cases at lines 19-20)
Source: Watcher triage test log (milestone deadline cases at lines 19-20)
Evidence: Real daemon coverage results
Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
bin/fm-task-milestones.sh:125- Milestones are only stored in$STATE/<id>.status, and teardown deletes that file (bin/fm-teardown.sh:945/3851->status_retire_presentation_task,bin/fm-classify-lib.sh:1647rm -f "$state/$task.status"). In a normal no-mistakes ship, Firstmate tears the task down after the PR merges. DEV deploy, data-ready, first live proof, dev-accepted and every UAT phase happen after that. The DoD tells Firstmate or the release/proof owner to stamp those 'after your delivery stop point using the same helper' (bin/fm-dod-lib.sh:213). Concrete sequence: brief stamped at scaffold, gate stamps by the worker, merge, then teardown removes the status file. A laterstamp ... dev-acceptedre-creates an orphan status log (stamponly checks that the parent dir exists, line ~81) with no brief record.durationsthen printsunknownfor brief_to_accepted_dev and accepted_dev_to_accepted_uat for every task, so the intent's REQUIRED measurement ('Measure next week brief->accepted DEV and accepted DEV->accepted UAT medians, failures and rework count') cannot be produced in the intended flow. The re-created file also shows up as an 'Orphan status log' infm-session-start.sh:871. The same orphan effect applies to briefs scaffolded but never spawned, becausefm-brief.sh:266stamps before spawn and only teardown retires the file. The remedy is a durable milestone record that survives teardown (for example data/<id>/ or an archive step in teardown), which adds new durable state, so it needs authorization.bin/fm-nm-run-lib.sh:296-fm_nm_review_coveragereports a verdict only when the captured AXI output containshead_sha,review_coverage,review_coverage_runandreview_coverage_head_sha. The existing no-mistakes readers in this repo useid,branch,head,outcomeandstatus(bin/fm-teardown.sh:1904-1911,bin/fm-nm-run-lib.sh:339), neverhead_sha, and no code or doc outside the new test fixture shows the daemon emitting the review_coverage* fields. With realaxi statusor outcome output, thehead_shacount check returnsunverifiedon every run. The status-milestone path can only becomecoveredif a worker stampsreview-coverage result=covered, and the brief tells the worker to take that verdict from this same reader, so the loop never yieldscovered. The intent asks for 'a Firstmate-side reader in bin/ ... that reports review coverage from the existing status/outcome records'. This reader reads fields the existing records apparently lack, so every gated task will be reported 'not covered' and escalated. It fails closed, but it carries no information. Deciding which real record fields prove coverage is a product and contract decision.bin/fm-task-milestones.sh:166- Thedeadlinessubcommand (15-minute dispatch and 30-minute obsolete-run escalation) has no caller: no watcher, wake path or brief references it (rg finds it only in the script, its test and the docs row). The failure the intent targets, obsolete runs left overnight, happens when the worker is idle. Escalation still depends on that same worker stampinggate-obsoleteand escalating by itself (bin/fm-dod-lib.sh:261), so this reader enforces nothing. Either wire it into Firstmate supervision so stale runs are escalated without the worker, or remove it as an unrequired component.bin/fm-dod-lib.sh:353-fm_dod_blockrecomputesroot,helperandstatus(lines 352-354), andfm_ship_milestone_contractalready computes the same values (lines 308-310). Two copies of the helper and status-path rule can drift apart. Havefm_ship_milestone_contractbe the only owner (for example also print or export the coverage line) and drop the duplicate locals.🔧 Fix applied.
5 issues (1 error, 3 warnings, 1 info) still open:
bin/fm-task-milestones.sh:125- Milestones are only stored in$STATE/<id>.status, and teardown deletes that file (bin/fm-teardown.sh:945/3851->status_retire_presentation_task,bin/fm-classify-lib.sh:1647rm -f "$state/$task.status"). In a normal no-mistakes ship, Firstmate tears the task down after the PR merges. DEV deploy, data-ready, first live proof, dev-accepted and every UAT phase happen after that. The DoD tells Firstmate or the release/proof owner to stamp those 'after your delivery stop point using the same helper' (bin/fm-dod-lib.sh:213). Concrete sequence: brief stamped at scaffold, gate stamps by the worker, merge, then teardown removes the status file. A laterstamp ... dev-acceptedre-creates an orphan status log (stamponly checks that the parent dir exists, line ~81) with no brief record.durationsthen printsunknownfor brief_to_accepted_dev and accepted_dev_to_accepted_uat for every task, so the intent's REQUIRED measurement ('Measure next week brief->accepted DEV and accepted DEV->accepted UAT medians, failures and rework count') cannot be produced in the intended flow. The re-created file also shows up as an 'Orphan status log' infm-session-start.sh:871. The same orphan effect applies to briefs scaffolded but never spawned, becausefm-brief.sh:266stamps before spawn and only teardown retires the file. The remedy is a durable milestone record that survives teardown (for example data/<id>/ or an archive step in teardown), which adds new durable state, so it needs authorization.bin/fm-task-milestones.sh:166- Thedeadlinessubcommand (15-minute dispatch and 30-minute obsolete-run escalation) has no caller: no watcher, wake path or brief references it (rg finds it only in the script, its test and the docs row). The failure the intent targets, obsolete runs left overnight, happens when the worker is idle. Escalation still depends on that same worker stampinggate-obsoleteand escalating by itself (bin/fm-dod-lib.sh:261), so this reader enforces nothing. Either wire it into Firstmate supervision so stale runs are escalated without the worker, or remove it as an unrequired component.bin/fm-dod-lib.sh:353-fm_dod_blockrecomputesroot,helperandstatus(lines 352-354), andfm_ship_milestone_contractalready computes the same values (lines 308-310). Two copies of the helper and status-path rule can drift apart. Havefm_ship_milestone_contractbe the only owner (for example also print or export the coverage line) and drop the duplicate locals.bin/fm-watch.sh:2694- This is the watcher wake that fix round 1 added. It runsdeadlinesfor every task whose status contains alocal-proofmilestone, whatever the task's delivery mode. The shared milestone contract (bin/fm-dod-lib.sh:225,fm_ship_milestone_contract) is rendered into the direct-PR and local-only DoD blocks too (bin/fm-dod-lib.sh:368,380), and it tells those workers to stamp local proof. Those modes never produce agate-start. Concrete sequence: a direct-PR worker stampslocal-proof, then works on its PR for 15 minutes. The watcher queues acheck: <id> gate dispatch overduewake, and Firstmate chases a gate that should never run. Thedeadlinesdispatch rule inbin/fm-task-milestones.sh:186has the same mode-blindness. Fix: run the dispatch check only when the task meta recordsmode=no-mistakes(fm-promote writesmode=into meta, line 287), or skip the dispatch rule for other modes.bin/fm-task-milestones.sh:177-deadlinesaborts as soon as any milestone line in the file is malformed, becauserecordsreturns 1 on the first bad line. The watcher (bin/fm-watch.sh:2695) discards that failure with2>/dev/null || due=, so the 15-minute and 30-minute escalation is silently turned off for that task. Concrete sequence: a worker writes a milestone by hand instead of using the helper, for examplemilestone [name=gate-start] [sha=abc1234] ...with a short SHA. Then a validgate-obsoletestamp never produces the 30-minute wake. This contradicts the intent's 'escalate base drift ≤30m'. Fail closed instead: eitherdeadlinesskips malformed records and still evaluates the valid ones, or the watcher raises a check wake that names the malformed milestone instead of swallowing the error. This is fix round 1 code.🔧 No changes applied.
7 issues (1 error, 4 warnings, 2 infos) still open:
bin/fm-task-milestones.sh:125- Milestones are only stored in$STATE/<id>.status, and teardown deletes that file (bin/fm-teardown.sh:945/3851->status_retire_presentation_task,bin/fm-classify-lib.sh:1647rm -f "$state/$task.status"). In a normal no-mistakes ship, Firstmate tears the task down after the PR merges. DEV deploy, data-ready, first live proof, dev-accepted and every UAT phase happen after that. The DoD tells Firstmate or the release/proof owner to stamp those 'after your delivery stop point using the same helper' (bin/fm-dod-lib.sh:213). Concrete sequence: brief stamped at scaffold, gate stamps by the worker, merge, then teardown removes the status file. A laterstamp ... dev-acceptedre-creates an orphan status log (stamponly checks that the parent dir exists, line ~81) with no brief record.durationsthen printsunknownfor brief_to_accepted_dev and accepted_dev_to_accepted_uat for every task, so the intent's REQUIRED measurement ('Measure next week brief->accepted DEV and accepted DEV->accepted UAT medians, failures and rework count') cannot be produced in the intended flow. The re-created file also shows up as an 'Orphan status log' infm-session-start.sh:871. The same orphan effect applies to briefs scaffolded but never spawned, becausefm-brief.sh:266stamps before spawn and only teardown retires the file. The remedy is a durable milestone record that survives teardown (for example data/<id>/ or an archive step in teardown), which adds new durable state, so it needs authorization.bin/fm-task-milestones.sh:166- Thedeadlinessubcommand (15-minute dispatch and 30-minute obsolete-run escalation) has no caller: no watcher, wake path or brief references it (rg finds it only in the script, its test and the docs row). The failure the intent targets, obsolete runs left overnight, happens when the worker is idle. Escalation still depends on that same worker stampinggate-obsoleteand escalating by itself (bin/fm-dod-lib.sh:261), so this reader enforces nothing. Either wire it into Firstmate supervision so stale runs are escalated without the worker, or remove it as an unrequired component.bin/fm-dod-lib.sh:353-fm_dod_blockrecomputesroot,helperandstatus(lines 352-354), andfm_ship_milestone_contractalready computes the same values (lines 308-310). Two copies of the helper and status-path rule can drift apart. Havefm_ship_milestone_contractbe the only owner (for example also print or export the coverage line) and drop the duplicate locals.bin/fm-watch.sh:2694- This is the watcher wake that fix round 1 added. It runsdeadlinesfor every task whose status contains alocal-proofmilestone, whatever the task's delivery mode. The shared milestone contract (bin/fm-dod-lib.sh:225,fm_ship_milestone_contract) is rendered into the direct-PR and local-only DoD blocks too (bin/fm-dod-lib.sh:368,380), and it tells those workers to stamp local proof. Those modes never produce agate-start. Concrete sequence: a direct-PR worker stampslocal-proof, then works on its PR for 15 minutes. The watcher queues acheck: <id> gate dispatch overduewake, and Firstmate chases a gate that should never run. Thedeadlinesdispatch rule inbin/fm-task-milestones.sh:186has the same mode-blindness. Fix: run the dispatch check only when the task meta recordsmode=no-mistakes(fm-promote writesmode=into meta, line 287), or skip the dispatch rule for other modes.bin/fm-task-milestones.sh:177-deadlinesaborts as soon as any milestone line in the file is malformed, becauserecordsreturns 1 on the first bad line. The watcher (bin/fm-watch.sh:2695) discards that failure with2>/dev/null || due=, so the 15-minute and 30-minute escalation is silently turned off for that task. Concrete sequence: a worker writes a milestone by hand instead of using the helper, for examplemilestone [name=gate-start] [sha=abc1234] ...with a short SHA. Then a validgate-obsoletestamp never produces the 30-minute wake. This contradicts the intent's 'escalate base drift ≤30m'. Fail closed instead: eitherdeadlinesskips malformed records and still evaluates the valid ones, or the watcher raises a check wake that names the malformed milestone instead of swallowing the error. This is fix round 1 code.bin/fm-task-milestones.sh:63- This is in fix round 2 code (milestones-lost-at-teardown).archive_forfinds the durable archive from the caller's environment (FM_DATA_OVERRIDE, else FM_HOME/data, else the script's repo root). It does not use the FILE argument the DoD hands out. When that lookup misses,stampquietly falls back to the status path and recreates the orphan status log the fix was meant to stop (lines 95 and 105-108). Concrete sequence: the Geris home has nobin/of its own, so its briefs render the helper as~/.firstmate/bin/fm-task-milestones.shand the status file as~/.firstmate-geris/state/<id>.status. Teardown archives to~/.firstmate-geris/data/<id>/milestones.status. A release or proof owner then runs the exact rendered command after teardown withoutFM_HOME=~/.firstmate-gerisexported.archive_forresolves to~/.firstmate/data/<id>/..., which does not exist.stampappendsdev-acceptedto a new~/.firstmate-geris/state/<id>.statusthat has no brief record.durationsthen prints unknown, and session-start lists the file as an orphan. The same env-derived lookup is used byrecords,durationsanddeadlines(line 82). The fix-round test pinsFM_DATA_OVERRIDE(tests/fm-task-milestones.test.sh:9), so it cannot catch this. It also callsarchivedirectly and never runs the real teardown. Fix: derive the archive from FILE itself ($(dirname FILE)/../data/<id>/milestones.statusunless an override is set). Also makestamprefuse when neither the status file nor the archive exists, instead of creating a new status log.tests/fm-watch-triage.test.sh:2142- This is the fix round 2 test for deadlines-unwired. It only covers the 15-minute dispatch wake through the watcher. The user's fix instruction asked for proof that the 30-minute gate-obsolete escalation is raised without the worker, and that a fresh task produces no wake. Neither is checked at the watcher level. Thedeadlinessubcommand logic itself is exercised in tests/fm-task-milestones.test.sh. Add a watcher case with a 31-minute-old gate-obsolete stamp, and one with a fresh local-proof that must not wake.🔧 Fix applied.
3 issues (2 warnings, 1 info) still open:
bin/fm-dod-lib.sh:353-fm_dod_blockrecomputesroot,helperandstatus(lines 352-354), andfm_ship_milestone_contractalready computes the same values (lines 308-310). Two copies of the helper and status-path rule can drift apart. Havefm_ship_milestone_contractbe the only owner (for example also print or export the coverage line) and drop the duplicate locals.bin/fm-watch.sh:2694- This is the watcher wake that fix round 1 added. It runsdeadlinesfor every task whose status contains alocal-proofmilestone, whatever the task's delivery mode. The shared milestone contract (bin/fm-dod-lib.sh:225,fm_ship_milestone_contract) is rendered into the direct-PR and local-only DoD blocks too (bin/fm-dod-lib.sh:368,380), and it tells those workers to stamp local proof. Those modes never produce agate-start. Concrete sequence: a direct-PR worker stampslocal-proof, then works on its PR for 15 minutes. The watcher queues acheck: <id> gate dispatch overduewake, and Firstmate chases a gate that should never run. Thedeadlinesdispatch rule inbin/fm-task-milestones.sh:186has the same mode-blindness. Fix: run the dispatch check only when the task meta recordsmode=no-mistakes(fm-promote writesmode=into meta, line 287), or skip the dispatch rule for other modes.bin/fm-watch.sh:2697- Fix round 2 added a grep pre-filter in the watcher, and it only matches milestone lines wherenameis the first tag (^milestone \[name=(local-proof|gate-obsolete)\]). The strict parser accepts tags in any order, and the schema comment documentsatfirst (bin/fm-classify-lib.sh:417:milestone [at=<epoch>] [name=<name>] ...). Concrete sequence: a worker or release owner writes a schema-valid line by hand, such asmilestone [at=1759400000] [name=gate-obsolete] [sha=<40hex>] [run=r1]: base moved.records/deadlinesparse that line correctly, but the grep never matches it, so the watcher never runsdeadlinesand the 30-minute obsolete-run escalation silently never fires. This is a sibling of the fail-closed deadline fix from round 2 (deadlines-fail-open-on-malformed), which that round left behind. Fix: loosen the pre-filter to^milestone .*\[name=(local-proof|gate-obsolete)\], or drop it and rely ondeadlinesprinting nothing when no relevant record exists.🔧 Fix applied.
2 issues (1 warning, 1 info) still open:
bin/fm-dod-lib.sh:353-fm_dod_blockrecomputesroot,helperandstatus(lines 352-354), andfm_ship_milestone_contractalready computes the same values (lines 308-310). Two copies of the helper and status-path rule can drift apart. Havefm_ship_milestone_contractbe the only owner (for example also print or export the coverage line) and drop the duplicate locals.bin/fm-task-milestones.sh:84- Round 1 wireddeadlinesinto the watcher (bin/fm-watch.sh:2696), and round 2 widened its pre-filter. It now runs on every poll (POLL=15s) for every no-mistakes task whose status holds a local-proof or gate-obsolete line, for the rest of that task's life, because the line is never removed.recordsforks a command-substitution subshell forstatus_line_verbon every status line.status_milestone_recordadds another fork per milestone line, with nested$(status_line_at_epoch ...)forks inside it. Concrete case: a few gated tasks with append-only status files of a few hundred lines cost several hundred forks per task, every cycle, long after the deadline is satisfied. That adds up to seconds of watcher cycle time. Fix: use the existing out-var formstatus_line_verb "$line" verbat line 84 and at the identical archive loop at line 123, so non-milestone lines cost no fork. Optionally skip callingdeadlinesonce the stored deadline-marker state shows nothing is pending.🔧 Fix applied.
1 info still open:
bin/fm-dod-lib.sh:353-fm_dod_blockrecomputesroot,helperandstatus(lines 352-354), andfm_ship_milestone_contractalready computes the same values (lines 308-310). Two copies of the helper and status-path rule can drift apart. Havefm_ship_milestone_contractbe the only owner (for example also print or export the coverage line) and drop the duplicate locals.✅ **Test** - passed
✅ No issues found.
milestone [name=brief] [at=...]: ship brief scaffold created, and records prints the brief rowlive-demo 1 2 2 unknown ...(gate rounds unknown because run-A never ended, as designed)archivesubcommand that teardown invokes was driven. A full teardown needs a live task window/worktree; provide it by running it in a bin/fm-he…bash tests/fm-task-milestones.test.sh(all 8 behavior cases ok)bash tests/fm-watch-triage.test.sh- the casestest_milestone_deadline_surfaced_onceandtest_milestone_deadline_casespassed (log lines 19-20)Manual:FM_HOME=<tmp> bin/fm-brief.sh live-demo repo --mode no-mistakes, then checked the status file andrecordsManual:fm-task-milestones.sh stamp/deadlinesat 899s, 1000s, 850s and 1800s, before and after gate-start and the successor runManual adversarial: merge without a URL, short SHA, acceptance with a waived result, stamp for a task that was never briefedManual:archive+ remove the status file + 9 late DEV/UAT stamps +durationsManual:fm-task-milestones.sh coverage 01M3XN1FX4Y825QBEGKG6GJXEE <target|base>against the real ~/.no-mistakes/state.sqlite (read-only), plus an unknown run and a missing NM_HOME✅ **Document** - passed
✅ No issues found.
🔧 **Lint** - 1 issue found → auto-fixed ✅
🔧 Fix applied.
✅ Re-checked - no issues remain.
✅ **Push** - passed
✅ No issues found.