Skip to content

fix: reduce home-summary refresh overhead - #61

Merged
MrGTV-love merged 6 commits into
mainfrom
fm/fm-home-summary-refresh-deadline
Oct 9, 2026
Merged

MrGTV-love merged 6 commits into
mainfrom
fm/fm-home-summary-refresh-deadline

Conversation

@MrGTV-love

@MrGTV-love MrGTV-love commented Oct 9, 2026 •

Copy link
Copy Markdown
Owner

Intent

I would like all bugs to be fixed so tomorrow can be focused entirely to Vernant and not problems preventning Vernant from getting built.

Context: the captain earlier reported that session start took over 120 s and that the home-summary refresh had failed hundreds of times with nothing done. PR #51 (merged 2026-10-08 16:44Z) stopped session start, spawn and teardown from waiting on the refresh and added an alarm for repeated failures. The refresh itself still exceeds its 60-second deadline: 5 failures between 16:45Z and 16:55Z, so state/home-summary.json is still not republished. Main timed one run at 51 s while host load was 170 on 18 CPUs; an earlier measurement was 117 s at load 54.

What Changed

  • Reduce snapshot subprocess overhead with in-shell metadata reads, consolidated jq processing, and bounded rolling windows for observations and task composition while preserving ID-sorted output.
  • Compute contribution coverage alongside observations with safe child-job cleanup; validate saved contribution records from a single size-bounded copy and reject symlink data roots, including paths with trailing slashes.
  • Add regression coverage for fleet subprocess cost, rolling-window ordering and failures, contribution-job cleanup, and saved-record boundaries; document concurrency and read guarantees.

Timing: before and after

The 60-second deadline in bin/fm-home-summary-refresh.sh is unchanged. This change cuts the cost of the producer (bin/fm-fleet-snapshot.sh --secondmate-home-summary) so it finishes well inside that deadline at the current fleet size. Every published field and meaning is kept: the summary bytes are identical between the two versions on the same live fleet (39,893 bytes in all six runs below).

Live fleet (27 tasks, 53 contribution records, 18 CPUs). Each pair ran back to back, main first, on the same host in the same minute. "Load" is the 1-minute load average read just before each run.

Pair main (origin/main, 5ea3f5b): wall load this PR (d97cc9c): wall load
1 36.3 s 71.5 13.7 s 78.3
2 36.8 s 77.0 14.1 s 86.7
3 35.8 s 82.7 15.4 s 79.3

Earlier live runs on the same fleet at heavier load: main 29.7 to 35.4 s and this change 19.4 to 23.0 s at load 103 to 177. At the heaviest load the wall time rises for both, because the producer's cost on a busy host is process creation (system CPU is about twice user CPU), not computation.

Process starts on the fixture fleet of the same size (tests/fm-fleet-snapshot-fleet-cost.test.sh): 518 jq starts on main, 115 here. The test bounds jq starts at 200 and fails on main.

Reading these numbers honestly:

  • At load 70 to 90, main's producer already fit inside 60 s. The refresh failures happened when the load rose past about 150, where one measured main run took 51 s and another 117 s. This change removes about 60% of the producer's wall time at load 70 to 90 and about 35 to 45% at load 100 to 177, so the failing region moves to a much higher load. It does not remove the load dependence.
  • What remains: fm-crew-state.sh still makes no-mistakes axi calls for each task (about 60% of the remaining CPU); done ships still make one GitHub read each; and a cold fold of the two large status logs costs 17 to 24 s if a decision checkpoint (.open-decisions-cursor) becomes invalid.

Risk Assessment

✅ Low: The change is a bounded optimization with targeted correctness fixes, and the full source review found no substantiated material defects or intent conflicts.

Testing

Base/target comparison, targeted executable regressions, and isolated live CLI scenarios demonstrated reduced snapshot cost, timely valid publication, prompt detached triggering, completion-order scheduling, safe contribution reads, and owned-child cleanup. Disposable-driver setup and assertion issues were documented and corrected or isolated. CLI transcripts and public JSON outputs were retained; no visual UI changed, so screenshots were not applicable.

  • Live validation: ✅ go - 11 of 11 scenarios driven live against the product
Scenario Result Live Evidence
Summarize a 27-task, 53-contribution fleet with lower process cost and unchanged output ✅ pass live fleet-performance-results.json and live-cli-transcript.txt: base took 86.661 seconds and 521 jq starts; target took 10.813 seconds and 116 starts, below the 200-start bound. Summary bytes were identic…
Refresh an active fleet and publish a valid ledger before the 60-second deadline ✅ pass live active-published-home-summary.json and active-refresh-transcript.txt: publication took 26.484 seconds under four CPU workers, with valid=true, active_children=27, endpoints=27, and 53 known contributi…
Trigger a detached refresh without waiting for publication ✅ pass live fleet-performance-results.json: --detach returned in 0.054 seconds, and the ledger subsequently republished after a contribution-cache change.
Start observation task 9 while the first observation remains blocked ✅ pass live rolling-window-evidence.txt: task 9 started before task 1 ended; traced observation-copy concurrency peaked at 5, below the bound of 8.
Start composition task 9 while the first composition remains blocked ✅ pass live rolling-window-evidence.txt: task 9 started before task 1 ended; composition concurrency peaked at 7. Serial, parallel, and base outputs were byte-identical and ID-sorted.
Fail an observation or composition child without publishing partial JSON ✅ pass live rolling-window-evidence.txt: both injected dependency failures returned exit 1, emitted zero stdout bytes, and retained the appropriate observation or task-snapshot diagnostic.
Accept a contribution record at 1 MiB and refuse one byte above the cap ✅ pass live contribution-cli-results.json: exactly capped records preserved clear coverage; oversized records made pending fail and snapshot report unreadable_records=1, complete=false, and proven_clear=false.
Read safely through searchable but unreadable task directories ✅ pass live contribution-cli-results.json: mode 0111 directories accepted capped records and refused oversized records; an unreadable file also refused verified coverage.
Reject symlink data roots even when trailing slashes are supplied ✅ pass live contribution-cli-results.json: symlink roots with no slash, one slash, and four slashes were refused; an ordinary root with trailing slashes retained unchanged clear coverage.
Refuse an oversized contribution replacement made after listing but before reading ✅ pass live contribution-cli-results.json and contribution-cli-transcript.txt: pending refused the replacement and snapshot disclosed incomplete, unreadable coverage.
Clean up only a still-running contribution child ✅ pass live cleanup-live-transcript.txt: early failure signalled only the active contribution PID; completed-child failure and successful --json/--secondmate-home-summary runs sent no cleanup signal.
Evidence: Measured base/target cost and refresh results

Source: Measured base/target cost and refresh results

[
  {
    "baseline_seconds": 86.66122891695704,
    "target_seconds": 10.813038541004062,
    "baseline_jq_starts": 521,
    "target_jq_starts": 116,
    "jq_bound": 200,
    "byte_identical": true,
    "endpoints": 27,
    "contribution_records": 53
  },
  {
    "elapsed_seconds": 12.124333125015255,
    "deadline_seconds": 60,
    "cpu_pressure_workers": 4,
    "published_schema": "fm-secondmate-home-summary.v1",
    "endpoints": 27,
    "contribution_records": 53,
    "failure_streak_cleared": true,
    "ledger_mode": "0600"
  },
  {
    "trigger_seconds": 0.054437292041257024,
    "republished": true
  }
]
Evidence: Fleet benchmark, publication, and detached-trigger transcript

Source: Fleet benchmark, publication, and detached-trigger transcript

$ tmux -S .lv/s -f /dev/null new-session -d -s fm-lab-summary -x 120 -y 40 -n anchor -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=0.626s

$ tmux -S .lv/s display-message -p -t fm-lab-summary:anchor #{socket_path},#{pid},0
exit=0 elapsed=1.214s

$ tmux -S .lv/s new-window -d -t fm-lab-summary: -n fm-task-01 -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=1.932s

$ tmux -S .lv/s set-window-option -t fm-lab-summary:fm-task-01 automatic-rename off
exit=0 elapsed=1.073s

$ tmux -S .lv/s new-window -d -t fm-lab-summary: -n fm-task-02 -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=0.700s

$ tmux -S .lv/s set-window-option -t fm-lab-summary:fm-task-02 automatic-rename off
exit=0 elapsed=0.451s

$ tmux -S .lv/s new-window -d -t fm-lab-summary: -n fm-task-03 -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=0.206s

$ tmux -S .lv/s set-window-option -t fm-lab-summary:fm-task-03 automatic-rename off
exit=0 elapsed=0.568s

$ tmux -S .lv/s new-window -d -t fm-lab-summary: -n fm-task-04 -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=2.947s

$ tmux -S .lv/s set-window-option -t fm-lab-summary:fm-task-04 automatic-rename off
exit=0 elapsed=0.473s

$ tmux -S .lv/s new-window -d -t fm-lab-summary: -n fm-task-05 -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=2.808s

$ tmux -S .lv/s set-window-option -t fm-lab-summary:fm-task-05 automatic-rename off
exit=0 elapsed=1.703s

$ tmux -S .lv/s new-window -d -t fm-lab-summary: -n fm-task-06 -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=0.914s

$ tmux -S .lv/s set-window-option -t fm-lab-summary:fm-task-06 automatic-rename off
exit=0 elapsed=1.335s

$ tmux -S .lv/s new-window -d -t fm-lab-summary: -n fm-task-07 -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=0.977s

$ tmux -S .lv/s set-window-option -t fm-lab-summary:fm-task-07 automatic-rename off
exit=0 elapsed=0.108s

$ tmux -S .lv/s new-window -d -t fm-lab-summary: -n fm-task-08 -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=0.157s

$ tmux -S .lv/s set-window-option -t fm-lab-summary:fm-task-08 automatic-rename off
exit=0 elapsed=0.053s

$ tmux -S .lv/s new-window -d -t fm-lab-summary: -n fm-task-09 -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=0.083s

$ tmux -S .lv/s set-window-option -t fm-lab-summary:fm-task-09 automatic-rename off
exit=0 elapsed=0.047s

$ tmux -S .lv/s new-window -d -t fm-lab-summary: -n fm-task-10 -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=0.212s

$ tmux -S .lv/s set-window-option -t fm-lab-summary:fm-task-10 automatic-rename off
exit=0 elapsed=0.195s

$ tmux -S .lv/s new-window -d -t fm-lab-summary: -n fm-task-11 -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=1.002s

$ tmux -S .lv/s set-window-option -t fm-lab-summary:fm-task-11 automatic-rename off
exit=0 elapsed=0.409s

$ tmux -S .lv/s new-window -d -t fm-lab-summary: -n fm-task-12 -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=2.082s

$ tmux -S .lv/s set-window-option -t fm-lab-summary:fm-task-12 automatic-rename off
exit=0 elapsed=1.460s

$ tmux -S .lv/s new-window -d -t fm-lab-summary: -n fm-task-13 -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=0.594s

$ tmux -S .lv/s set-window-option -t fm-lab-summary:fm-task-13 automatic-rename off
exit=0 elapsed=1.721s

$ tmux -S .lv/s new-window -d -t fm-lab-summary: -n fm-task-14 -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=0.353s

$ tmux -S .lv/s set-window-option -t fm-lab-summary:fm-task-14 automatic-rename off
exit=0 elapsed=0.153s

$ tmux -S .lv/s new-window -d -t fm-lab-summary: -n fm-task-15 -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=0.244s

$ tmux -S .lv/s set-window-option -t fm-lab-summary:fm-task-15 automatic-rename off
exit=0 elapsed=0.277s

$ tmux -S .lv/s new-window -d -t fm-lab-summary: -n fm-task-16 -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=0.510s

$ tmux -S .lv/s set-window-option -t fm-lab-summary:fm-task-16 automatic-rename off
exit=0 elapsed=0.193s

$ tmux -S .lv/s new-window -d -t fm-lab-summary: -n fm-task-17 -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=0.196s

$ tmux -S .lv/s set-window-option -t fm-lab-summary:fm-task-17 automatic-rename off
exit=0 elapsed=0.122s

$ tmux -S .lv/s new-window -d -t fm-lab-summary: -n fm-task-18 -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=0.140s

$ tmux -S .lv/s set-window-option -t fm-lab-summary:fm-task-18 automatic-rename off
exit=0 elapsed=0.115s

$ tmux -S .lv/s new-window -d -t fm-lab-summary: -n fm-task-19 -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=0.096s

$ tmux -S .lv/s set-window-option -t fm-lab-summary:fm-task-19 automatic-rename off
exit=0 elapsed=0.081s

$ tmux -S .lv/s new-window -d -t fm-lab-summary: -n fm-task-20 -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=0.177s

$ tmux -S .lv/s set-window-option -t fm-lab-summary:fm-task-20 automatic-rename off
exit=0 elapsed=0.026s

$ tmux -S .lv/s new-window -d -t fm-lab-summary: -n fm-task-21 -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=0.032s

$ tmux -S .lv/s set-window-option -t fm-lab-summary:fm-task-21 automatic-rename off
exit=0 elapsed=0.314s

$ tmux -S .lv/s new-window -d -t fm-lab-summary: -n fm-task-22 -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=0.032s

$ tmux -S .lv/s set-window-option -t fm-lab-summary:fm-task-22 automatic-rename off
exit=0 elapsed=0.031s

$ tmux -S .lv/s new-window -d -t fm-lab-summary: -n fm-task-23 -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=0.038s

$ tmux -S .lv/s set-window-option -t fm-lab-summary:fm-task-23 automatic-rename off
exit=0 elapsed=0.273s

$ tmux -S .lv/s new-window -d -t fm-lab-summary: -n fm-task-24 -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=0.088s

$ tmux -S .lv/s set-window-option -t fm-lab-summary:fm-task-24 automatic-rename off
exit=0 elapsed=0.148s

$ tmux -S .lv/s new-window -d -t fm-lab-summary: -n fm-task-25 -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=0.037s

$ tmux -S .lv/s set-window-option -t fm-lab-summary:fm-task-25 automatic-rename off
exit=0 elapsed=0.044s

$ tmux -S .lv/s new-window -d -t fm-lab-summary: -n fm-task-26 -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=0.213s

$ tmux -S .lv/s set-window-option -t fm-lab-summary:fm-task-26 automatic-rename off
exit=0 elapsed=0.118s

$ tmux -S .lv/s new-window -d -t fm-lab-summary: -n fm-task-27 -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=0.049s

$ tmux -S .lv/s set-window-option -t fm-lab-summary:fm-task-27 automatic-rename off
exit=0 elapsed=0.123s

$ .lv/baseline/bin/fm-fleet-snapshot.sh --secondmate-home-summary
exit=0 elapsed=86.661s

$ bin/fm-fleet-snapshot.sh --secondmate-home-summary
exit=0 elapsed=10.813s

{"baseline_seconds": 86.66122891695704, "target_seconds": 10.813038541004062, "baseline_jq_starts": 521, "target_jq_starts": 116, "jq_bound": 200, "byte_identical": true, "endpoints": 27, "contribution_records": 53}

$ bin/fm-home-summary-refresh.sh --best-effort
exit=0 elapsed=12.124s

{"elapsed_seconds": 12.124333125015255, "deadline_seconds": 60, "cpu_pressure_workers": 4, "published_schema": "fm-secondmate-home-summary.v1", "endpoints": 27, "contribution_records": 53, "failure_streak_cleared": true, "ledger_mode": "0600"}

$ bin/fm-home-summary-refresh.sh --detach
exit=0 elapsed=0.054s

{"trigger_seconds": 0.054437292041257024, "republished": true}

$ bin/fm-fleet-snapshot.sh --home-input
exit=0 elapsed=67.717s

$ bin/fm-fleet-snapshot.sh --home-input
exit=0 elapsed=49.819s

Private tmux server teardown exit=0
Evidence: Valid published active-fleet summary

Source: Valid published active-fleet summary

{
  "schema": "fm-secondmate-home-summary.v1",
  "hold_classifier_schema": "fm-captain-hold-buckets.v1",
  "contributions": {
    "known": 53,
    "checked": 53,
    "counts": {
      "captain": 0,
      "fleet": 0,
      "maintainer": 0,
      "nobody": 53
    },
    "unmeasured": 0,
    "complete": true,
    "proven_clear": true,
    "stale_verdicts": 0,
    "missing_verdicts": 0,
    "unreadable_records": 0,
    "valid_until": 1791548100,
    "captain": [],
    "captain_omitted": 0
  },
  "generated": "2026-10-09T12:00:00Z",
  "generated_epoch": 1791547200,
  "home": "~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y/.lv/home",
  "valid": true,
  "reason": null,
  "invalidity": {
    "kind": null,
    "ids": []
  },
  "state": "active_child_work",
  "active_children": [
    {
      "id": "task-01",
      "kind": "ship",
      "state": "working",
      "repo": "alpha",
      "name": "Task 1",
      "source": "pane",
      "doing": "harness busy (claude-hook)"
    },
    {
      "id": "task-02",
      "kind": "ship",
      "state": "working",
      "repo": "alpha",
      "name": "Task 2",
      "source": "pane",
      "doing": "harness busy (claude-hook)"
    },
    {
      "id": "task-03",
      "kind": "ship",
      "state": "working",
      "repo": "alpha",
      "name": "Task 3",
      "source": "pane",
      "doing": "harness busy (claude-hook)"
    },
    {
      "id": "task-04",
      "kind": "scout",
      "state": "working",
      "repo": "alpha",
      "name": "Task 4",
      "source": "pane",
      "doing": "harness busy (claude-hook)"
    },
    {
      "id": "task-05",
      "kind": "ship",
      "state": "working",
      "repo": "alpha",
      "name": "Task 5",
      "source": "pane",
      "doing": "harness busy (claude-hook)"
    },
    {
      "id": "task-06",
      "kind": "ship",
      "state": "working",
      "repo": "alpha",
      "name": "Task 6",
      "source": "pane",
      "doing": "harness busy (claude-hook)"
    },
    {
      "id": "task-07",
      "kind": "ship",
      "state": "working",
      "repo": "alpha",
      "name": "Task 7",
      "source": "pane",
      "doing": "harness busy (claude-hook)"
    },
    {
      "id": "task-08",
      "kind": "scout",
      "state": "working",
      "repo": "alpha",
      "name": "Task 8",
      "source": "pane",
      "doing": "harness busy (claude-hook)"
    },
    {
      "id": "task-09",
      "kind": "ship",
      "state": "working",
      "repo": "alpha",
      "name": "Task 9",
      "source": "pane",
      "doing": "harness busy (claude-hook)"
    },
    {
      "id": "task-10",
      "kind": "ship",
      "state": "working",
      "repo": "alpha",
      "name": "Task 10",
      "source": "pane",
      "doing": "harness busy (claude-hook)"
    },
    {
      "id": "task-11",
      "kind": "ship",
      "state": "working",
      "repo": "alpha",
      "name": "Task 11",
      "source": "pane",
      "doing": "harness busy (claude-hook)"
    },
    {
      "id": "task-12",
      "kind": "scout",
      "state": "working",
      "repo": "alpha",
      "name": "Task 12",
      "source": "pane",
      "doing": "harness busy (claude-hook)"
    },
    {
      "id": "task-13",
      "kind": "ship",
      "state": "working",
      "repo": "alpha",
      "name": "Task 13",
      "source": "pane",
      "doing": "harness busy (claude-hook)"
    },
    {
      "id": "task-14",
      "kind": "ship",
      "state": "working",
      "repo": "alpha",
      "name": "Task 14",
      "source": "pane",
      "doing": "harness busy (claude-hook)"
    },
    {
      "id": "task-15",
      "kind": "ship",
      "state": "working",
      "repo": "alpha",
      "name": "Task 15",
      "source": "pane",
      "doing": "harness busy (claude-hook)"
    },
    {
      "id": "task-16",
      "kind": "scout",
      "state": "working",
      "repo": "alpha",
      "name": "Task 16",
      "source": "pane",
      "doing": "harness busy (claude-hook)"
    },
    {
      "id": "task-17",
      "kind": "ship",
      "state": "working",
      "repo": "alpha",
      "name": "Task 17",
      "source": "pane",
      "doing": "harness busy (claude-hook)"
    },
    {
      "id": "task-18",
      "kind": "ship",
      "state": "working",
      "repo": "alpha",
      "name": "Task 18",
      "source": "pane",
      "doing": "harness busy (claude-hook)"
    },
    {
      "id": "task-19",
      "kind": "ship",
      "state": "working",
      "repo": "alpha",
      "name": "Task 19",
      "source": "pane",
      "doing": "harness busy (claude-hook)"
    },
    {
      "id": "task-20",
      "kind": "scout",
      "state": "working",
      "repo": "alpha",
      "name": "Task 20",
      "source": "pane",
      "doing": "harness busy (claude-hook)"
    }
  ],
  "decisions_open": [],
  "holds": [],
  "queued": [],
  "landed": [],
  "endpoints": [
    {
      "id": "task-01",
      "state": "working",
      "source": "pane",
      "endpoint": {
        "target": "fm-lab-summary:fm-task-01",
        "exists": true,
        "agent_alive": "ambiguous",
        "status": "unknown",
        "observed_at": "2026-10-09T12:00:00Z",
        "freshness": "fresh"
      }
    },
    {
      "id": "task-02",
      "state": "working",
      "source": "pane",
      "endpoint": {
        "target": "fm-lab-summary:fm-task-02",
        "exists": true,
        "agent_alive": "ambiguous",
        "status": "unknown",
        "observed_at": "2026-10-09T12:00:00Z",
        "freshness": "fresh"
      }
    },
    {
      "id": "task-03",
      "state": "working",
      "source": "pane",
      "endpoint": {
        "target": "fm-lab-summary:fm-task-03",
        "exists": true,
        "agent_alive": "ambiguous",
        "status": "unknown",
        "observed_at": "2026-10-09T12:00:00Z",
        "freshness": "fresh"
      }
    },
    {
      "id": "task-04",
      "state": "working",
      "source": "pane",
      "endpoint": {
        "target": "fm-lab-summary:fm-task-04",
        "exists": true,
        "agent_alive": "ambiguous",
        "status": "unknown",
        "observed_at": "2026-10-09T12:00:00Z",
        "freshness": "fresh"
      }
    },
    {
      "id": "task-05",
      "state": "working",
      "source": "pane",
      "endpoint": {
        "target": "fm-lab-summary:fm-task-05",
        "exists": true,
        "agent_alive": "ambiguous",
        "status": "unknown",
        "observed_at": "2026-10-09T12:00:00Z",
        "freshness": "fresh"
      }
    },
    {
      "id": "task-06",
      "state": "working",
      "source": "pane",
      "endpoint": {
        "target": "fm-lab-summary:fm-task-06",
        "exists": true,
        "agent_alive": "ambiguous",
        "status": "unknown",
        "observed_at": "2026-10-09T12:00:00Z",
        "freshness": "fresh"
      }
    },
    {
      "id": "task-07",
      "state": "working",
      "source": "pane",
      "endpoint": {
        "target": "fm-lab-summary:fm-task-07",
        "exists": true,
        "agent_alive": "ambiguous",
        "status": "unknown",
        "observed_at": "2026-10-09T12:00:00Z",
        "freshness": "fresh"
      }
    },
    {
      "id": "task-08",
      "state": "working",
      "source": "pane",
      "endpoint": {
        "target": "fm-lab-summary:fm-task-08",
        "exists": true,
        "agent_alive": "ambiguous",
        "status": "unknown",
        "observed_at": "2026-10-09T12:00:00Z",
        "freshness": "fresh"
      }
    },
    {
      "id": "task-09",
      "state": "working",
      "source": "pane",
      "endpoint": {
        "target": "fm-lab-summary:fm-task-09",
        "exists": true,
        "agent_alive": "ambiguous",
        "status": "unknown",
        "observed_at": "2026-10-09T12:00:00Z",
        "freshness": "fresh"
      }
    },
    {
      "id": "task-10",
      "state": "working",
      "source": "pane",
      "endpoint": {
        "target": "fm-lab-summary:fm-task-10",
        "exists": true,
        "agent_alive": "ambiguous",
        "status": "unknown",
        "observed_at": "2026-10-09T12:00:00Z",
        "freshness": "fresh"
      }
    },
    {
      "id": "task-11",
      "state": "working",
      "source": "pane",
      "endpoint": {
        "target": "fm-lab-summary:fm-task-11",
        "exists": true,
        "agent_alive": "ambiguous",
        "status": "unknown",
        "observed_at": "2026-10-09T12:00:00Z",
        "freshness": "fresh"
      }
    },
    {
      "id": "task-12",
      "state": "working",
      "source": "pane",
      "endpoint": {
        "target": "fm-lab-summary:fm-task-12",
        "exists": true,
        "agent_alive": "ambiguous",
        "status": "unknown",
        "observed_at": "2026-10-09T12:00:00Z",
        "freshness": "fresh"
      }
    },
    {
      "id": "task-13",
      "state": "working",
      "source": "pane",
      "endpoint": {
        "target": "fm-lab-summary:fm-task-13",
        "exists": true,
        "agent_alive": "ambiguous",
        "status": "unknown",
        "observed_at": "2026-10-09T12:00:00Z",
        "freshness": "fresh"
      }
    },
    {
      "id": "task-14",
      "state": "working",
      "source": "pane",
      "endpoint": {
        "target": "fm-lab-summary:fm-task-14",
        "exists": true,
        "agent_alive": "ambiguous",
        "status": "unknown",
        "observed_at": "2026-10-09T12:00:00Z",
        "freshness": "fresh"
      }
    },
    {
      "id": "task-15",
      "state": "working",
      "source": "pane",
      "endpoint": {
        "target": "fm-lab-summary:fm-task-15",
        "exists": true,
        "agent_alive": "ambiguous",
        "status": "unknown",
        "observed_at": "2026-10-09T12:00:00Z",
        "freshness": "fresh"
      }
    },
    {
      "id": "task-16",
      "state": "working",
      "source": "pane",
      "endpoint": {
        "target": "fm-lab-summary:fm-task-16",
        "exists": true,
        "agent_alive": "ambiguous",
        "status": "unknown",
        "observed_at": "2026-10-09T12:00:00Z",
        "freshness": "fresh"
      }
    },
    {
      "id": "task-17",
      "state": "working",
      "source": "pane",
      "endpoint": {
        "target": "fm-lab-summary:fm-task-17",
        "exists": true,
        "agent_alive": "ambiguous",
        "status": "unknown",
        "observed_at": "2026-10-09T12:00:00Z",
        "freshness": "fresh"
      }
    },
    {
      "id": "task-18",
      "state": "working",
      "source": "pane",
      "endpoint": {
        "target": "fm-lab-summary:fm-task-18",
        "exists": true,
        "agent_alive": "ambiguous",
        "status": "unknown",
        "observed_at": "2026-10-09T12:00:00Z",
        "freshness": "fresh"
      }
    },
    {
      "id": "task-19",
      "state": "working",
      "source": "pane",
      "endpoint": {
        "target": "fm-lab-summary:fm-task-19",
        "exists": true,
        "agent_alive": "ambiguous",
        "status": "unknown",
        "observed_at": "2026-10-09T12:00:00Z",
        "freshness": "fresh"
      }
    },
    {
      "id": "task-20",
      "state": "working",
      "source": "pane",
      "endpoint": {
        "target": "fm-lab-summary:fm-task-20",
        "exists": true,
        "agent_alive": "ambiguous",
        "status": "unknown",
        "observed_at": "2026-10-09T12:00:00Z",
        "freshness": "fresh"
      }
    }
  ],
  "counts": {
    "active_children": 27,
    "decisions_open": 0,
    "holds": 0,
    "queued": 0,
    "landed": 0,
    "endpoints": 27
  },
  "omitted": [
    {
      "surface": "active_children",
      "count": 7
    },
    {
      "surface": "endpoints",
      "count": 7
    }
  ]
}
Evidence: Active-fleet refresh under CPU pressure

Source: Active-fleet refresh under CPU pressure

$ tmux -S .lv/a -f /dev/null new-session -d -s fm-lab-summary -x 120 -y 40 -n anchor -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=0.301s
stdout=
stderr=

$ tmux -S .lv/a display-message -p -t fm-lab-summary:anchor #{socket_path},#{pid},0
exit=0 elapsed=0.025s
stdout=~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y/.lv/a,36691,0

stderr=

$ tmux -S .lv/a set-window-option -g automatic-rename off
exit=0 elapsed=0.028s
stdout=
stderr=

$ tmux -S .lv/a new-window -d -t fm-lab-summary: -n fm-task-01 -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=0.036s
stdout=
stderr=

$ bin/fm-busy-event.sh arm .lv/home/state task-01 --state busy --source claude-hook --event user-prompt-submit
exit=0 elapsed=0.214s
stdout=g1791529133.37976.10186

stderr=

$ tmux -S .lv/a new-window -d -t fm-lab-summary: -n fm-task-02 -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=0.028s
stdout=
stderr=

$ bin/fm-busy-event.sh arm .lv/home/state task-02 --state busy --source claude-hook --event user-prompt-submit
exit=0 elapsed=0.144s
stdout=g1791529133.38708.15058

stderr=

$ tmux -S .lv/a new-window -d -t fm-lab-summary: -n fm-task-03 -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=0.036s
stdout=
stderr=

$ bin/fm-busy-event.sh arm .lv/home/state task-03 --state busy --source claude-hook --event user-prompt-submit
exit=0 elapsed=0.219s
stdout=g1791529133.39339.23198

stderr=

$ tmux -S .lv/a new-window -d -t fm-lab-summary: -n fm-task-04 -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=0.033s
stdout=
stderr=

$ bin/fm-busy-event.sh arm .lv/home/state task-04 --state busy --source claude-hook --event user-prompt-submit
exit=0 elapsed=0.179s
stdout=g1791529134.40236.5181

stderr=

$ tmux -S .lv/a new-window -d -t fm-lab-summary: -n fm-task-05 -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=0.028s
stdout=
stderr=

$ bin/fm-busy-event.sh arm .lv/home/state task-05 --state busy --source claude-hook --event user-prompt-submit
exit=0 elapsed=0.219s
stdout=g1791529134.40968.10053

stderr=

$ tmux -S .lv/a new-window -d -t fm-lab-summary: -n fm-task-06 -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=0.028s
stdout=
stderr=

$ bin/fm-busy-event.sh arm .lv/home/state task-06 --state busy --source claude-hook --event user-prompt-submit
exit=0 elapsed=0.122s
stdout=g1791529134.41586.28670

stderr=

$ tmux -S .lv/a new-window -d -t fm-lab-summary: -n fm-task-07 -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=0.028s
stdout=
stderr=

$ bin/fm-busy-event.sh arm .lv/home/state task-07 --state busy --source claude-hook --event user-prompt-submit
exit=0 elapsed=0.091s
stdout=g1791529134.41995.1490

stderr=

$ tmux -S .lv/a new-window -d -t fm-lab-summary: -n fm-task-08 -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=0.028s
stdout=
stderr=

$ bin/fm-busy-event.sh arm .lv/home/state task-08 --state busy --source claude-hook --event user-prompt-submit
exit=0 elapsed=0.105s
stdout=g1791529134.42435.4776

stderr=

$ tmux -S .lv/a new-window -d -t fm-lab-summary: -n fm-task-09 -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=0.021s
stdout=
stderr=

$ bin/fm-busy-event.sh arm .lv/home/state task-09 --state busy --source claude-hook --event user-prompt-submit
exit=0 elapsed=0.094s
stdout=g1791529134.42860.17632

stderr=

$ tmux -S .lv/a new-window -d -t fm-lab-summary: -n fm-task-10 -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=0.047s
stdout=
stderr=

$ bin/fm-busy-event.sh arm .lv/home/state task-10 --state busy --source claude-hook --event user-prompt-submit
exit=0 elapsed=0.120s
stdout=g1791529135.43324.15893

stderr=

$ tmux -S .lv/a new-window -d -t fm-lab-summary: -n fm-task-11 -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=0.044s
stdout=
stderr=

$ bin/fm-busy-event.sh arm .lv/home/state task-11 --state busy --source claude-hook --event user-prompt-submit
exit=0 elapsed=0.118s
stdout=g1791529135.43791.15062

stderr=

$ tmux -S .lv/a new-window -d -t fm-lab-summary: -n fm-task-12 -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=0.043s
stdout=
stderr=

$ bin/fm-busy-event.sh arm .lv/home/state task-12 --state busy --source claude-hook --event user-prompt-submit
exit=0 elapsed=0.149s
stdout=g1791529135.44297.15565

stderr=

$ tmux -S .lv/a new-window -d -t fm-lab-summary: -n fm-task-13 -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=0.052s
stdout=
stderr=

$ bin/fm-busy-event.sh arm .lv/home/state task-13 --state busy --source claude-hook --event user-prompt-submit
exit=0 elapsed=0.121s
stdout=g1791529135.45068.21771

stderr=

$ tmux -S .lv/a new-window -d -t fm-lab-summary: -n fm-task-14 -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=0.034s
stdout=
stderr=

$ bin/fm-busy-event.sh arm .lv/home/state task-14 --state busy --source claude-hook --event user-prompt-submit
exit=0 elapsed=0.134s
stdout=g1791529135.45523.15489

stderr=

$ tmux -S .lv/a new-window -d -t fm-lab-summary: -n fm-task-15 -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=0.042s
stdout=
stderr=

$ bin/fm-busy-event.sh arm .lv/home/state task-15 --state busy --source claude-hook --event user-prompt-submit
exit=0 elapsed=0.197s
stdout=g1791529135.46061.30529

stderr=

$ tmux -S .lv/a new-window -d -t fm-lab-summary: -n fm-task-16 -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=0.070s
stdout=
stderr=

$ bin/fm-busy-event.sh arm .lv/home/state task-16 --state busy --source claude-hook --event user-prompt-submit
exit=0 elapsed=0.103s
stdout=g1791529136.46710.30917

stderr=

$ tmux -S .lv/a new-window -d -t fm-lab-summary: -n fm-task-17 -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=0.029s
stdout=
stderr=

$ bin/fm-busy-event.sh arm .lv/home/state task-17 --state busy --source claude-hook --event user-prompt-submit
exit=0 elapsed=0.149s
stdout=g1791529136.47181.31903

stderr=

$ tmux -S .lv/a new-window -d -t fm-lab-summary: -n fm-task-18 -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=0.075s
stdout=
stderr=

$ bin/fm-busy-event.sh arm .lv/home/state task-18 --state busy --source claude-hook --event user-prompt-submit
exit=0 elapsed=0.290s
stdout=g1791529136.47796.5

stderr=

$ tmux -S .lv/a new-window -d -t fm-lab-summary: -n fm-task-19 -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=0.067s
stdout=
stderr=

$ bin/fm-busy-event.sh arm .lv/home/state task-19 --state busy --source claude-hook --event user-prompt-submit
exit=0 elapsed=0.097s
stdout=g1791529136.48677.23417

stderr=

$ tmux -S .lv/a new-window -d -t fm-lab-summary: -n fm-task-20 -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=0.026s
stdout=
stderr=

$ bin/fm-busy-event.sh arm .lv/home/state task-20 --state busy --source claude-hook --event user-prompt-submit
exit=0 elapsed=0.091s
stdout=g1791529137.49105.21252

stderr=

$ tmux -S .lv/a new-window -d -t fm-lab-summary: -n fm-task-21 -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=0.036s
stdout=
stderr=

$ bin/fm-busy-event.sh arm .lv/home/state task-21 --state busy --source claude-hook --event user-prompt-submit
exit=0 elapsed=0.110s
stdout=g1791529137.49491.16845

stderr=

$ tmux -S .lv/a new-window -d -t fm-lab-summary: -n fm-task-22 -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=0.017s
stdout=
stderr=

$ bin/fm-busy-event.sh arm .lv/home/state task-22 --state busy --source claude-hook --event user-prompt-submit
exit=0 elapsed=0.073s
stdout=g1791529137.49838.27033

stderr=

$ tmux -S .lv/a new-window -d -t fm-lab-summary: -n fm-task-23 -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=0.014s
stdout=
stderr=

$ bin/fm-busy-event.sh arm .lv/home/state task-23 --state busy --source claude-hook --event user-prompt-submit
exit=0 elapsed=0.088s
stdout=g1791529137.50118.23167

stderr=

$ tmux -S .lv/a new-window -d -t fm-lab-summary: -n fm-task-24 -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=0.034s
stdout=
stderr=

$ bin/fm-busy-event.sh arm .lv/home/state task-24 --state busy --source claude-hook --event user-prompt-submit
exit=0 elapsed=0.419s
stdout=g1791529137.50532.14642

stderr=

$ tmux -S .lv/a new-window -d -t fm-lab-summary: -n fm-task-25 -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=0.032s
stdout=
stderr=

$ bin/fm-busy-event.sh arm .lv/home/state task-25 --state busy --source claude-hook --event user-prompt-submit
exit=0 elapsed=0.115s
stdout=g1791529137.51269.5402

stderr=

$ tmux -S .lv/a new-window -d -t fm-lab-summary: -n fm-task-26 -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=0.036s
stdout=
stderr=

$ bin/fm-busy-event.sh arm .lv/home/state task-26 --state busy --source claude-hook --event user-prompt-submit
exit=0 elapsed=0.148s
stdout=g1791529138.51774.5905

stderr=

$ tmux -S .lv/a new-window -d -t fm-lab-summary: -n fm-task-27 -c ~/.no-mistakes/worktrees/32d18ed9638d/01M4EXCPRHTRH93K0CGS9YW44Y sleep 600
exit=0 elapsed=0.041s
stdout=
stderr=

$ bin/fm-busy-event.sh arm .lv/home/state task-27 --state busy --source claude-hook --event user-prompt-submit
exit=0 elapsed=0.225s
stdout=g1791529138.52439.29490

stderr=

$ bin/fm-home-summary-refresh.sh --best-effort
exit=0 elapsed=26.484s
stdout=
stderr=

Private tmux teardown exit=0

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

🔧 **Rebase** - 1 issue found → auto-fixed ✅
  • ⚠️ bin/fm-fleet-snapshot.sh - merge conflict rebasing onto origin/main

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

🔧 **Review** - 1 issue found → auto-fixed (4) ✅
  • ⚠️ bin/fm-fleet-snapshot.sh:2196 - The contribution PID remains tracked after wait completes. During intended execution, contributions can finish before the slower observations; if its PID is subsequently recycled, the EXIT cleanup sends SIGTERM to an unrelated process, potentially another fleet worker. Clear CONTRIBUTIONS_PID after consuming the child's exit status, and restrict early-error cleanup to the still-running contribution job rather than an unchecked saved PID. Related changed sites: bin/fm-fleet-snapshot.sh:2168 stores the handle; bin/fm-fleet-snapshot.sh:2196 consumes it without clearing it; bin/fm-fleet-snapshot.sh:1635 unconditionally signals the retained number.

🔧 Fix applied.
2 warnings still open:

  • ⚠️ bin/fm-fleet-snapshot.sh:2196 - The contribution PID remains tracked after wait completes. During intended execution, contributions can finish before the slower observations; if its PID is subsequently recycled, the EXIT cleanup sends SIGTERM to an unrelated process, potentially another fleet worker. Clear CONTRIBUTIONS_PID after consuming the child's exit status, and restrict early-error cleanup to the still-running contribution job rather than an unchecked saved PID. Related changed sites: bin/fm-fleet-snapshot.sh:2168 stores the handle; bin/fm-fleet-snapshot.sh:2196 consumes it without clearing it; bin/fm-fleet-snapshot.sh:1635 unconditionally signals the retained number.
  • ⚠️ bin/fm-fleet-snapshot.sh:817 - The rolling windows still block on the oldest child rather than replenish a freed slot. With concurrency 8, if the first crew-state read reaches its 10-second timeout while reads 2–8 finish after one second, task 9 cannot start for another nine seconds despite seven idle slots. Slow reads at successive window boundaries repeat this delay during the intended fleet refresh, retaining the slow-task slot-idling problem this optimization claims to remove. The same policy appears in task composition at bin/fm-fleet-snapshot.sh:1053. Collect completed children independently of launch order and replenish available slots while preserving the concurrency bound, exit-status handling, and final ID ordering.

🔧 Fix applied.
1 warning still open:

  • ⚠️ bin/fm-contributions.sh:150 - The bulk size scan can silently bypass the existing 1 MiB record cap. For example, a searchable but non-readable task directory (mode 0111) containing a readable, valid contributions.json padded beyond the cap is reachable through the glob and readable by jq, but find cannot enumerate it. Suppressing find's failure leaves the record absent from oversized, so pending succeeds instead of refusing and snapshot unreadable_records/complete/proven_clear can report falsely clean coverage. The same bypass occurs with a trailing-slash symlink data root: normalization removes the slash, physical find does not traverse the symlink, but the glob does. Related changed sites: bin/fm-contributions.sh:149 normalizes the root after the symlink guard at :144; :151 independently discovers records; :159-160 treats absence from the scan as size approval; :164 slurps those records without another size check. Preserve scan failure status and fall back to per-file size checks when enumeration is incomplete; apply the existing root-symlink guard to the normalized path.

🔧 Fix applied.
2 warnings still open:

  • ⚠️ bin/fm-contributions.sh:150 - The bulk size scan can silently bypass the existing 1 MiB record cap. For example, a searchable but non-readable task directory (mode 0111) containing a readable, valid contributions.json padded beyond the cap is reachable through the glob and readable by jq, but find cannot enumerate it. Suppressing find's failure leaves the record absent from oversized, so pending succeeds instead of refusing and snapshot unreadable_records/complete/proven_clear can report falsely clean coverage. The same bypass occurs with a trailing-slash symlink data root: normalization removes the slash, physical find does not traverse the symlink, but the glob does. Related changed sites: bin/fm-contributions.sh:149 normalizes the root after the symlink guard at :144; :151 independently discovers records; :159-160 treats absence from the scan as size approval; :164 slurps those records without another size check. Preserve scan failure status and fall back to per-file size checks when enumeration is incomplete; apply the existing root-symlink guard to the normalized path.
  • ⚠️ bin/fm-contributions.sh:161 - Round 3 fixes incomplete enumeration but leaves stale size approval in the successful-scan path. During the supported overlap between a detached refresh and contribution polling, find can observe a record below 1 MiB, then poll atomically replaces it with an oversized record before read_saved reaches that file. This is reachable because poll retains unbounded review arrays (:296), updates records (:439), and publishes without a size cap (:226). Since find succeeded and the path is absent from oversized, the replacement is slurped and accepted without a size check; pending can succeed where the previous per-file check would refuse, and snapshot omits the required unreadable-record disclosure. Enforce the cap on the record actually consumed at the shared read_saved boundary, rather than using an earlier scan's absence as approval. Related changed sites: bin/fm-contributions.sh:148 captures sizes before processing; :159 rejects only previously oversized paths; :169 consumes the potentially replaced record. docs/architecture.md:147 claims the cap is enforced.

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

✅ **Test** - passed

✅ No issues found.

  • Live validation: ✅ go - 11 of 11 scenarios driven live against the product
Scenario Result Live Evidence
Summarize a 27-task, 53-contribution fleet with lower process cost and unchanged output ✅ pass live fleet-performance-results.json and live-cli-transcript.txt: base took 86.661 seconds and 521 jq starts; target took 10.813 seconds and 116 starts, below the 200-start bound. Summary bytes were identic…
Refresh an active fleet and publish a valid ledger before the 60-second deadline ✅ pass live active-published-home-summary.json and active-refresh-transcript.txt: publication took 26.484 seconds under four CPU workers, with valid=true, active_children=27, endpoints=27, and 53 known contributi…
Trigger a detached refresh without waiting for publication ✅ pass live fleet-performance-results.json: --detach returned in 0.054 seconds, and the ledger subsequently republished after a contribution-cache change.
Start observation task 9 while the first observation remains blocked ✅ pass live rolling-window-evidence.txt: task 9 started before task 1 ended; traced observation-copy concurrency peaked at 5, below the bound of 8.
Start composition task 9 while the first composition remains blocked ✅ pass live rolling-window-evidence.txt: task 9 started before task 1 ended; composition concurrency peaked at 7. Serial, parallel, and base outputs were byte-identical and ID-sorted.
Fail an observation or composition child without publishing partial JSON ✅ pass live rolling-window-evidence.txt: both injected dependency failures returned exit 1, emitted zero stdout bytes, and retained the appropriate observation or task-snapshot diagnostic.
Accept a contribution record at 1 MiB and refuse one byte above the cap ✅ pass live contribution-cli-results.json: exactly capped records preserved clear coverage; oversized records made pending fail and snapshot report unreadable_records=1, complete=false, and proven_clear=false.
Read safely through searchable but unreadable task directories ✅ pass live contribution-cli-results.json: mode 0111 directories accepted capped records and refused oversized records; an unreadable file also refused verified coverage.
Reject symlink data roots even when trailing slashes are supplied ✅ pass live contribution-cli-results.json: symlink roots with no slash, one slash, and four slashes were refused; an ordinary root with trailing slashes retained unchanged clear coverage.
Refuse an oversized contribution replacement made after listing but before reading ✅ pass live contribution-cli-results.json and contribution-cli-transcript.txt: pending refused the replacement and snapshot disclosed incomplete, unreadable coverage.
Clean up only a still-running contribution child ✅ pass live cleanup-live-transcript.txt: early failure signalled only the active contribution PID; completed-child failure and successful --json/--secondmate-home-summary runs sent no cleanup signal.
  • /bin/bash tests/fm-fleet-snapshot-fleet-cost.test.sh
  • /bin/bash tests/fm-fleet-snapshot-rolling-window.test.sh
  • /bin/bash tests/fm-fleet-snapshot-contribution-cleanup.test.sh
  • /bin/bash tests/fm-contributions-read-saved.test.sh
  • /bin/bash tests/fm-fleet-snapshot-view.test.sh
  • python3 .lv/drive.py: compared base and target producers against 27 real private-tmux windows and 53 contribution records; exercised fm-home-summary-refresh.sh --best-effort and --detach. Its initial tracing assertion was not counted as passing and was replaced by the focused scheduling proof.
  • python3 .lv/rolling.py: exercised slow-first scheduling, serial/parallel/base output equivalence, ID ordering, and both child-failure paths; passed with the default crew-state timeout.
  • python3 .lv/contributions.py: exercised inclusive size boundaries, permission-limited reads, symlink roots, trailing slashes, and oversized replacement immediately before consumption.
  • python3 .lv/cleanup.py: exercised real contribution-job cleanup on active-child failure, completed-child failure, and successful snapshot publication.
  • python3 .lv/active.py: published a valid 27-active-child summary under four disposable CPU workers. An incorrect throwaway assertion expected the state name working; the captured product output correctly reported active_child_work, and its validity, counts, and deadline were verified directly.
  • Stopped all private tmux servers and disposable CPU workers, removed .lv, and confirmed the fixture directory was absent.
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

…stages

The home-summary refresh overran its 60-second deadline on a busy host
because the producer (fm-fleet-snapshot.sh --secondmate-home-summary)
spends its time starting processes, not computing.
On the live fleet (27 tasks, 53 contribution records) a run costs about
12 s user and 25 s system CPU, and the system share is process creation.

Stages whose cost grows with the fleet, and the fix for each:

- Per-task record composition ran about 16 jq processes and about 25
  metadata subshells per task, serially. Each task is now one jq program
  fed by in-shell reads (metadata keys, last status line, first PR URL,
  path tests), and records are composed in a rolling window.
- Observation prefetch waited for a whole batch of 8 before starting the
  next, so one slow task idled seven slots. It now keeps a rolling window
  of the same size.
- Contribution coverage validated each record with three jq runs and a
  wc. It now reads each record with one jq run, finds oversized records
  with one find, and runs beside the observation stages because it
  depends only on the backlog and task metadata.
- The contribution task list resolved merge authority and ran jq once per
  task. Merge authority is a property of the away record, so it is
  resolved once, and the list is built by one jq.

Every published field and its meaning are unchanged: the summary, home
input, contribution input and full snapshot are byte-identical to the
previous producer on a fixed fixture fleet.

Measured on the live fleet, producer only, old vs new wall time:
  load 103-115: 29.7 s -> 19.4 s
  load 129-159: 33.1 s -> 23.0 s
  load 166-177: 35.4 s -> 22.0 s, 33.3 s -> 19.4 s
A fixture fleet of the same size starts 518 jq processes on the previous
producer and 115 now; tests/fm-fleet-snapshot-fleet-cost.test.sh bounds
that and fails on the previous producer.
@MrGTV-love
MrGTV-love merged commit 2c25099 into main Oct 9, 2026
23 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant