fix(bin): coalesce watcher signals into one wake - #4
Conversation
A crewmate's final status write and the same turn's turn-end hook land seconds apart, producing two wakes for one logical event - each costing a full firstmate turn. On the first changed signal the watcher now lingers FM_SIGNAL_GRACE (default 30s), re-scans, and reports every changed file in a single wake line. Seen-signatures are written only when a wake is actually reported, so a watcher killed mid-cycle can no longer mark a signal seen without delivering it.
|
Reviewed clean by fresh reviewer (kb-i4-rev-s2: e2e maintenance CLI + demo-run inspection + full harness green) and merged into integration branch fm/npt-build-s8 (merge 2254afa; additive conflicts vs #3 union-resolved). Lands in the single batch PR to main. |
The approval-ledger helper (agent-registry gap #4) was committed by the 6h auto-backup as 100644; fm-merge-local.sh invokes it directly, so it needs the exec bit like every other bin/ script. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Check state - correction to the Intent section aboveThe Intent section above, carried over from the task brief, states that GitHub Actions is permanently billing-refused on this repository and that The verified state on head
The single failure, Two honest caveats worth recording:
No check was relaxed, disabled, or deleted. Merge evidence remains the three SHA-256 verifications, the supersession sweep, and the local suite counts. |
Add cursor-agent CLI as a verified firstmate crewmate/secondmate adapter. Phase 1 verification (2026-08-04): - Hooks: no hooks could be established with any tested manifest format (phase 3, pending correct upstream manifest key discovery). - Raw-launch smoke: reproduced all 4 defects (composer, liveness, CLAUDECODE leak, no busy source). - Steering: Enter-while-busy queues the key (same as opencode 1.18.4, no separate submit core branch needed). /no-mistakes lands on single Enter with popup settle. Phase 2 implementation (this PR): - Detection: CURSOR_AGENT=1 env marker ordered before CLAUDECODE (fixes defect kunchenguid#3: CLAUDECODE leak). Ancestry backstop for MainThread/cursor-agent args. - Liveness: cursor-agent in FM_HARNESS_RE, FM_HARNESS_NAMES, tmux agent classification (fixes defect kunchenguid#2: liveness). - Composer: → glyph, Add a follow-up idle regex, reverse-video cursor ghost-gap fix (fixes defect kunchenguid#1: pending idle pane). - Busy state: unknown cursor-unverified with empty version gate (captain decision 2026-08-04, option a). No rendered-signal fallback (fixes defect kunchenguid#4: no busy source). - Launch: cursor in allowlists, launch template with --force --trust --model, env sanitization (unset CLAUDECODE), fail-closed binary check, effort recorded in meta but not emitted. - Interrupt: C-c (not Escape, verified). - Docs: harness-adapters/SKILL.md cursor section, AGENTS.md §4, docs/configuration.md, docs/turnend-guard.md, verification evidence. - Tests: fm-cursor-harness.test.sh (new), extensions to fm-composer-lib, fm-composer-ghost, fm-tmux-agent-liveness, fm-busy-state, fm-busy-adapter-wiring. Out of scope: firstmate primary on cursor, herdr-backend cursor, cursor --worktree, phase 3 hooks, effort-suffix composition. Reference: kunchenguid#771
Add cursor-agent CLI as a verified firstmate crewmate/secondmate adapter. Phase 1 verification (2026-08-04): - Hooks: no hooks could be established with any tested manifest format (phase 3, pending correct upstream manifest key discovery). - Raw-launch smoke: reproduced all 4 defects (composer, liveness, CLAUDECODE leak, no busy source). - Steering: Enter-while-busy queues the key (same as opencode 1.18.4, no separate submit core branch needed). /no-mistakes lands on single Enter with popup settle. Phase 2 implementation (this PR): - Detection: CURSOR_AGENT=1 env marker ordered before CLAUDECODE (fixes defect kunchenguid#3: CLAUDECODE leak). Ancestry backstop for MainThread/cursor-agent args. - Liveness: cursor-agent in FM_HARNESS_RE, FM_HARNESS_NAMES, tmux agent classification (fixes defect kunchenguid#2: liveness). - Composer: → glyph, Add a follow-up idle regex, reverse-video cursor ghost-gap fix (fixes defect kunchenguid#1: pending idle pane). - Busy state: unknown cursor-unverified with empty version gate (captain decision 2026-08-04, option a). No rendered-signal fallback (fixes defect kunchenguid#4: no busy source). - Launch: cursor in allowlists, launch template with --force --trust --model, env sanitization (unset CLAUDECODE), fail-closed binary check, effort recorded in meta but not emitted. - Interrupt: C-c (not Escape, verified). - Docs: harness-adapters/SKILL.md cursor section, AGENTS.md §4, docs/configuration.md, docs/turnend-guard.md, verification evidence. - Tests: fm-cursor-harness.test.sh (new), extensions to fm-composer-lib, fm-composer-ghost, fm-tmux-agent-liveness, fm-busy-state, fm-busy-adapter-wiring. Out of scope: firstmate primary on cursor, herdr-backend cursor, cursor --worktree, phase 3 hooks, effort-suffix composition. Reference: kunchenguid#771
* fix(watch): coalesce near-simultaneous signals into one wake A crewmate's final status write and the same turn's turn-end hook land seconds apart, producing two wakes for one logical event - each costing a full firstmate turn. On the first changed signal the watcher now lingers FM_SIGNAL_GRACE (default 30s), re-scans, and reports every changed file in a single wake line. Seen-signatures are written only when a wake is actually reported, so a watcher killed mid-cycle can no longer mark a signal seen without delivering it. * no-mistakes(document): Document watcher signal grace
Add cursor-agent CLI as a verified firstmate crewmate/secondmate adapter. Phase 1 verification (2026-08-04): - Hooks: no hooks could be established with any tested manifest format (phase 3, pending correct upstream manifest key discovery). - Raw-launch smoke: reproduced all 4 defects (composer, liveness, CLAUDECODE leak, no busy source). - Steering: Enter-while-busy queues the key (same as opencode 1.18.4, no separate submit core branch needed). /no-mistakes lands on single Enter with popup settle. Phase 2 implementation (this PR): - Detection: CURSOR_AGENT=1 env marker ordered before CLAUDECODE (fixes defect kunchenguid#3: CLAUDECODE leak). Ancestry backstop for MainThread/cursor-agent args. - Liveness: cursor-agent in FM_HARNESS_RE, FM_HARNESS_NAMES, tmux agent classification (fixes defect kunchenguid#2: liveness). - Composer: → glyph, Add a follow-up idle regex, reverse-video cursor ghost-gap fix (fixes defect kunchenguid#1: pending idle pane). - Busy state: unknown cursor-unverified with empty version gate (captain decision 2026-08-04, option a). No rendered-signal fallback (fixes defect kunchenguid#4: no busy source). - Launch: cursor in allowlists, launch template with --force --trust --model, env sanitization (unset CLAUDECODE), fail-closed binary check, effort recorded in meta but not emitted. - Interrupt: C-c (not Escape, verified). - Docs: harness-adapters/SKILL.md cursor section, AGENTS.md §4, docs/configuration.md, docs/turnend-guard.md, verification evidence. - Tests: fm-cursor-harness.test.sh (new), extensions to fm-composer-lib, fm-composer-ghost, fm-tmux-agent-liveness, fm-busy-state, fm-busy-adapter-wiring. Out of scope: firstmate primary on cursor, herdr-backend cursor, cursor --worktree, phase 3 hooks, effort-suffix composition. Reference: kunchenguid#771
Add cursor-agent CLI as a verified firstmate crewmate/secondmate adapter. Phase 1 verification (2026-08-04): - Hooks: no hooks could be established with any tested manifest format (phase 3, pending correct upstream manifest key discovery). - Raw-launch smoke: reproduced all 4 defects (composer, liveness, CLAUDECODE leak, no busy source). - Steering: Enter-while-busy queues the key (same as opencode 1.18.4, no separate submit core branch needed). /no-mistakes lands on single Enter with popup settle. Phase 2 implementation (this PR): - Detection: CURSOR_AGENT=1 env marker ordered before CLAUDECODE (fixes defect kunchenguid#3: CLAUDECODE leak). Ancestry backstop for MainThread/cursor-agent args. - Liveness: cursor-agent in FM_HARNESS_RE, FM_HARNESS_NAMES, tmux agent classification (fixes defect kunchenguid#2: liveness). - Composer: → glyph, Add a follow-up idle regex, reverse-video cursor ghost-gap fix (fixes defect kunchenguid#1: pending idle pane). - Busy state: unknown cursor-unverified with empty version gate (captain decision 2026-08-04, option a). No rendered-signal fallback (fixes defect kunchenguid#4: no busy source). - Launch: cursor in allowlists, launch template with --force --trust --model, env sanitization (unset CLAUDECODE), fail-closed binary check, effort recorded in meta but not emitted. - Interrupt: C-c (not Escape, verified). - Docs: harness-adapters/SKILL.md cursor section, AGENTS.md §4, docs/configuration.md, docs/turnend-guard.md, verification evidence. - Tests: fm-cursor-harness.test.sh (new), extensions to fm-composer-lib, fm-composer-ghost, fm-tmux-agent-liveness, fm-busy-state, fm-busy-adapter-wiring. Out of scope: firstmate primary on cursor, herdr-backend cursor, cursor --worktree, phase 3 hooks, effort-suffix composition. Reference: kunchenguid#771
|
Rebased onto current main (0a2f59e): resolved the documentation-audiences.json overlap by keeping task-grill's entries alongside task-design, and merged the AGENTS.md planning-family exception to cover task-planning, task-grill, and task-design with design documents included. The redundant task-planning audience-classification commit collapsed during rebase since main already carries those entries. Lint and documentation-audience checks pass; content unchanged from the previously validated pipeline run. |
…3-windows merge: sync windows with upstream kunchenguid/firstmate main (1c5c9c1)
* fix(bin): forbid manual gh issue close/reopen and gh project in ship briefs Pipeline seats hand-closing issues (kunchenguid#1222, kunchenguid#1229) with three gh issue close calls each under a throttled account tripped the fleet-wide GraphQL abuse limiter. The PR body's closes #N already closes issues on merge, and project boards are ruled out entirely, so manual closes and any gh project command are pure churn. Adds one rule to the ship scaffold in bin/fm-brief.sh and a matching --intent carry-forward clause in the no-mistakes DOD (bin/fm-dod-lib.sh) so pipeline seats inherit it. Extends tests/fm-brief.test.sh to assert the rule text across all three ship delivery modes and confirm scout briefs do not carry it. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Uj566GMjYRLax7NMbU5gpw * no-mistakes(review): Propagate GitHub command ban through scout promotions --------- Co-authored-by: NewAiCoder <170579485+NewAiCoder@users.noreply.github.com> Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
* fix(bin): forbid manual gh issue close/reopen and gh project in ship briefs Pipeline seats hand-closing issues (kunchenguid#1222, kunchenguid#1229) with three gh issue close calls each under a throttled account tripped the fleet-wide GraphQL abuse limiter. The PR body's closes #N already closes issues on merge, and project boards are ruled out entirely, so manual closes and any gh project command are pure churn. Adds one rule to the ship scaffold in bin/fm-brief.sh and a matching --intent carry-forward clause in the no-mistakes DOD (bin/fm-dod-lib.sh) so pipeline seats inherit it. Extends tests/fm-brief.test.sh to assert the rule text across all three ship delivery modes and confirm scout briefs do not carry it. * no-mistakes(review): Propagate GitHub command ban through scout promotions ---------
chore: merge upstream firstmate main into the fork
Intent
The firstmate watcher (bin/fm-watch.sh) produced duplicate wakes: a crewmate's final status-file write and the same turn's turn-end hook land seconds apart (observed 15s), and since the watcher exits on the first changed signal file and is restarted between wakes, one logical event (crewmate finished its turn) cost two full firstmate turns. The user asked to patch this duplicate-signal behavior. Change: the signal scan is now a pure read collecting all changed files; on the first detection the watcher lingers FM_SIGNAL_GRACE (default 30s, deliberately chosen to cover the observed 15s status-to-turn-end gap), re-scans, dedupes by file, and reports everything in one 'signal: ...' wake line. Seen-signatures are deliberately written only at wake time now, so a watcher killed mid-grace cannot mark a signal seen without reporting it (the old code wrote seen before waking). AGENTS.md section 8 wording updated to match the multi-file wake format. Single-signal wake format is unchanged ('signal: '), keeping existing wake-handling instructions valid. Tested end-to-end in a sandbox: coalescing of two signals 3s apart into one wake, no duplicate wake after restart, and solo-signal wake all verified. The added wake latency (one grace period) is an accepted tradeoff; crewmate tasks run minutes to hours.
What Changed
bin/fm-watch.shto collect changed signal files, wait through a configurableFM_SIGNAL_GRACEwindow, and emit one dedupedsignal: <file>...wake for near-simultaneous status and turn-end signals.AGENTS.mdand noted the default signal grace window inREADME.md.Risk Assessment
✅ Low: The change is narrowly scoped to watcher signal coalescing and documentation, preserves the single-signal output shape, and I found no material correctness or operational risks in the changed code.
Testing
Exercised the actual
bin/fm-watch.shend-to-end with synthetic watcher state: coalesced status plus turn-end signals, verified no duplicate wake after restart, verified kill-during-grace does not swallow a signal, verified solo-signal output shape, captured a transcript artifact, and removed transient in-worktree state afterward.Evidence: fm-watch coalescing transcript
Shows one wake containing both coalesced signal files, no duplicate wake after restart, preserved pending signal after kill during grace, and unchanged solo-signal wake format.Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
✅ **Review** - passed
✅ No issues found.
✅ **Test** - passed
✅ No issues found.
FM_SIGNAL_GRACE=2 FM_POLL=1 FM_HEARTBEAT=9999 FM_HEARTBEAT_MAX=9999 FM_CHECK_INTERVAL=9999 bin/fm-watch.shwith a status write followed by a turn-end marker 1s later, isolated from live tmux windows by a faketmuxonPATHFM_SIGNAL_GRACE=1 FM_POLL=1 FM_HEARTBEAT=9999 FM_HEARTBEAT_MAX=9999 FM_CHECK_INTERVAL=9999 bin/fm-watch.shrestarted after the coalesced wake and observed for 3s to confirm no duplicate signal wakeFM_SIGNAL_GRACE=5 ... bin/fm-watch.shkilled during the grace period, followed by a restart withFM_SIGNAL_GRACE=1, to confirm pending signals are not marked seen before a wake is reportedFM_SIGNAL_GRACE=1 ... bin/fm-watch.shwith a single status-file signal to confirm the original single-filesignal: <file>wake shape remains intact✅ **Document** - passed
✅ No issues found.
✅ **Lint** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.