Skip to content

fix(watch): coalesce delayed wake prompts and suppress obsolete notifications - #1164

Closed
pokorny344 wants to merge 7 commits into
kunchenguid:mainfrom
pokorny344:fm/restore-firstmate-upstream-delivery
Closed

fix(watch): coalesce delayed wake prompts and suppress obsolete notifications#1164
pokorny344 wants to merge 7 commits into
kunchenguid:mainfrom
pokorny344:fm/restore-firstmate-upstream-delivery

Conversation

@pokorny344

Copy link
Copy Markdown

Intent

Deliver the already captain-approved delayed obsolete-notification correction upstream without losing its five exact local commit identities, integrate the latest upstream Firstmate changes through a merge commit, add the captain-requested .idea/ Git ignore, preserve every supervision and safety contract, and open a replacement upstream PR that supersedes closed PR 1141 and requests merge-commit landing so the captain's local main can return to normal updates.

What Changed

  • The Pi extension (.pi/extensions/fm-primary-pi-watch.ts) and OpenCode plugin (.opencode/plugins/fm-primary-watch-arm.js) now revalidate actionable wake prompts against the durable queue before delivery: prompts for reasons already consumed by a completed drain are suppressed, delivery is deferred (with bounded retries) while the wake-queue mutation lock is held, and later watcher closes coalesce behind a pending handling turn instead of scheduling extra host prompts. The latch is released at Pi agent_settled / OpenCode session.idle (or session.deleted/session-scoped session.error), only the latching send epoch can release it on failure, and a rejected send restores undelivered reasons by merging them without clobbering newer ones.
  • Adds ~1,000 lines of regression coverage in tests/fm-pi-watch-extension.test.sh (real durable queue and drain script, real Pi 0.82.1 TUI and OpenCode 1.16.2 TUI runs, queue-lock deferral, stale-send suppression, send-epoch latch guarding, Pi rejection handling), and updates docs/watcher-continuity.md plus supervision verification evidence to document the new ordering and release points.
  • Adds .idea/ to .gitignore so JetBrains project directories stay untracked.

Risk Assessment

✅ Low: All source-verifiable intent criteria are met (five exact captain-approved commit identities preserved and matching refs/pull/1141/head, true merge commit of origin/main with a clean conflict resolution, .idea/ gitignore added, no supervision contract weakened), the adapter state machines are correct against the verified wake-queue and lock semantics, no bin/ scripts changed, and the change carries exhaustive regression coverage including real-TUI evidence, leaving only informational observations.

Testing

Exercised the delivery shape with git-level proofs (five preserved commit SHAs matching closed PR 1141's head, true merge commit of current upstream main, lossless conflict resolution verified via merge-tree and stage diffs, live check-ignore demo of the .idea/ entry) and ran the repo's prescribed focus path on the merged tree: the 40-assertion fm-pi-watch-extension suite plus three static supervision-contract guard tests, all passing; no screenshot artifacts because this change has no rendered UI surface (git history, bash/TS watcher adapters, and docs), so CLI/git transcripts are the end-user-visible evidence. Opening the replacement PR itself belongs to the outer executor's PR phase.

Evidence: Delivery-shape evidence: commit identities vs PR 1141, merge parents vs upstream main, merge fidelity, .idea ignore demo
== Branch graph (base b29621b -> target 14d2eb0) ==
* 14d2eb0 chore: ignore JetBrains project directories
* dd4ce67 Merge origin/main into obsolete-notification fix
* 57e2c36 no-mistakes(document): Update watcher prompt docs
* f60057c no-mistakes(review): Defer wake prompts during queue locks
* 058c5ed no-mistakes(review): guard wake latch with send epochs and catch Pi rejection
* 14f5935 no-mistakes(review): fix OpenCode wake latch recovery and pending reason merging
* 817a9e6 fix: coalesce delayed watcher wake prompts

== Full SHAs and parents ==
14d2eb06962d4b561a07a536e57f512b5a964f6a dd4ce6706d3d7b75cf6730c778cf70f2a98b1682 | chore: ignore JetBrains project directories
dd4ce6706d3d7b75cf6730c778cf70f2a98b1682 57e2c36bb18ca4376c876920fe536b096451849e b29621ba19a4d15b688ae277ca06b67f80baa365 | Merge origin/main into obsolete-notification fix
57e2c36bb18ca4376c876920fe536b096451849e f60057c6c033eaec77656890160fff7adcd49ae5 | no-mistakes(document): Update watcher prompt docs
f60057c6c033eaec77656890160fff7adcd49ae5 058c5ed2da2b2598f4d04aaab3fcb22a7bdf54d9 | no-mistakes(review): Defer wake prompts during queue locks
058c5ed2da2b2598f4d04aaab3fcb22a7bdf54d9 14f593506b630b92f2569ff16586acc75e3caacc | no-mistakes(review): guard wake latch with send epochs and catch Pi rejection
14f593506b630b92f2569ff16586acc75e3caacc 817a9e6d9b23d576c1eb6ed3dac20db2f9fde590 | no-mistakes(review): fix OpenCode wake latch recovery and pending reason merging
817a9e6d9b23d576c1eb6ed3dac20db2f9fde590 a5fe1bcc8c2ac01951e6a68d1c8b1b1ecae21499 | fix: coalesce delayed watcher wake prompts

== Remote proof: PR 1141 head == tip of the five local commits; remote main == merge second parent ==
b29621ba19a4d15b688ae277ca06b67f80baa365	refs/heads/main
57e2c36bb18ca4376c876920fe536b096451849e	refs/pull/1141/head

== PR 1141 state (closed, unmerged) ==
gh-axi pr view 1141 -> state: closed, merged: no, title: "fix: suppress stale watcher wake prompts after drain"

== Merge fidelity: mechanical merge-tree vs actual merge commit tree (only the hand-resolved SKILL.md conflict differs; resolution verified as union of both sides) ==
 .agents/skills/harness-adapters/SKILL.md | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)

== .gitignore change (14d2eb0) and end-user effect ==
@@ -3,6 +3,7 @@ state/
 data/
 .no-mistakes/
 .lavish/
+.idea/
 .fm-secondmate-home
 .DS_Store
 __pycache__/
.gitignore:6:.idea/	.idea/workspace.xml
Evidence: fm-pi-watch-extension suite transcript (40 ok, exit=0) via bin/fm-test-run.sh
FM_TEST_BEGIN 2026-07-28T04:03:01Z tests/fm-pi-watch-extension.test.sh family=watcher-wake-lock expected_gate_skip=none
ok - Pi primary watcher extension is tracked, self-hashing, and self-locating
ok - Pi secondmate launch wiring includes both tracked primary extensions
ok - Pi extension reports external healthy watcher output
ok - Pi custom tool exposes repair-only metadata and returns automatic-continuation guidance
ok - Pi redundant tool call returns ownership guidance and spawns no second child
ok - Pi scheduled retry remains extension-owned after another tool call
ok - Pi actionable close survives an active queue lock and starts one successor before delivery
ok - Pi coalesces actionable closes until a real drain and preserves later events
ok - Pi failed send restores coalesced wake delivery without an unhandled rejection
ok - native Pi coalesces pre-drain closes, suppresses retired stale delivery, and preserves a new post-drain event
ok - Pi hung successor falls back to one typed actionable wake
ok - Pi unretired successor falls back without an overlapping retry
ok - Pi late unretired closes resume classified supervision
ok - Pi clean empty close triggers a bounded continuity retry
ok - Pi established clean closes stop at the configured retry limit
ok - Pi close handler verifies session-lock ownership before successor launch
ok - Pi watcher arm distinguishes all session lock ownership states
ok - Pi process-exit cleanup listener has a bounded lifecycle
ok - Pi process-exit cleanup stops the attached arm child
ok - OpenCode primary watcher plugin has the verified TUI wake wiring
ok - OpenCode plugins have an explicit ESM boundary even under a typeless parent package
ok - OpenCode watcher plugin uses the effective FM_HOME state
ok - OpenCode watcher plugin sources the effective config
ok - OpenCode watcher plugin requires session lock ownership
ok - OpenCode watcher coordinator respects primary scope
ok - OpenCode actionable close survives an active queue lock and starts one successor before delivery
ok - OpenCode coalesces actionable closes until a real drain and preserves later events
ok - OpenCode failed send preserves the newer coalesced actionable reason
ok - OpenCode failed send merges undelivered failure reasons instead of clobbering
ok - OpenCode releases the wake prompt latch when the outstanding session is deleted or errors
ok - OpenCode stale rejected send cannot release a newer wake prompt latch
ok - OpenCode pre-ready actionable close preserves its successor
ok - OpenCode hung successor falls back to one typed actionable wake
ok - OpenCode unretired successor falls back without an overlapping retry
ok - OpenCode late unretired closes resume classified supervision
ok - OpenCode clean empty close triggers a bounded continuity retry
ok - OpenCode established clean closes stop at the configured retry limit
ok - OpenCode close handler verifies session-lock ownership before successor launch
ok - OpenCode watcher plugin coordinates with the turn-end guard
ok - OpenCode healthy arm output does not suppress the turn-end guard
FM_TEST_END 2026-07-28T04:03:34Z tests/fm-pi-watch-extension.test.sh exit=0 duration_ms=32989 gate_skip=false
FM_TEST_SUMMARY total=1 failed=0 skipped_gate=0 duration_ms=33105
FM_TEST_SUMMARY_FAMILY family=watcher-wake-lock count=1 duration_ms=32989 failed=0
FM_TEST_SLOWEST rank=1 script=tests/fm-pi-watch-extension.test.sh duration_ms=32989
Evidence: Supervision-contract guard tests transcript (3 suites, 0 failed)
FM_TEST_BEGIN 2026-07-28T04:04:14Z tests/fm-captain-translation-contract.test.sh family=pure-contract-unit expected_gate_skip=none
ok - section 9 owns the positive captain-facing translation contract
ok - scout remains allowed in private captain chat
ok - compressed safety labels require concrete plain renderings
ok - section 9 maps high-risk internal vocabulary families
ok - captain chat rejects verbatim internal evidence while private reports stay precise
ok - routine no-action response is exact and scoped to its event
ok - outward-facing skill handoffs point to the section 9 owner
ok - skills cross-reference section 9 instead of duplicating the mapping list
ok - ahoy is internal, user-invocable, and absent from public skills
ok - README lists ahoy under the shared cross-harness invocation convention
ok - ahoy delegates first-message fallback and keeps later recaps visible-session-only
ok - ahoy adds visibly open decisions without changing the ordinary recap boundary
ok - ahoy: one canonical owner constructs typed operational input for every Firstmate-controlled user-role producer
FM_TEST_END 2026-07-28T04:04:15Z tests/fm-captain-translation-contract.test.sh exit=0 duration_ms=517 gate_skip=false
FM_TEST_BEGIN 2026-07-28T04:04:15Z tests/fm-documentation-audiences.test.sh family=pure-contract-unit expected_gate_skip=none
ok - documentation inventory classifies every maintained prose surface exactly once
ok - classification, setup routing, and maintained-prose scope fail safely
ok - required documentation owner pointers cannot silently disappear
ok - local links resolve while dates, versions, commands, and incident prose remain semantically reviewed
ok - no-mistakes uses the supported trusted document.instructions schema
FM_TEST_END 2026-07-28T04:04:17Z tests/fm-documentation-audiences.test.sh exit=0 duration_ms=1739 gate_skip=false
FM_TEST_BEGIN 2026-07-28T04:04:17Z tests/fm-instruction-owners.test.sh family=pure-contract-unit expected_gate_skip=none
ok - new internal skills have one precise AGENTS.md trigger each
ok - diagnostic-reasoning owns the approved evidence procedure
ok - project-management owns registry, delivery posture, consent, initialization, and removal safety
ok - generic effort fallback applies only below captain and standing configuration
ok - firstmate directly compares every quota candidate with authoritative model discovery
ok - firstmate-coding-guidelines owns compatibility review and deterministic enforcement
ok - secondmate registry guidance keeps concise routes and points to the charter
ok - state, startup, and ordinary recovery have focused owners and triggers
ok - compressed AGENTS.md records the approved one-owner map
ok - intake reuses evidence, reserves scouts for uncertainty, and parallelizes safe work
ok - compressed AGENTS.md retains authority, supervision, AFK, and X safety
FM_TEST_END 2026-07-28T04:04:17Z tests/fm-instruction-owners.test.sh exit=0 duration_ms=453 gate_skip=false
FM_TEST_SUMMARY total=3 failed=0 skipped_gate=0 duration_ms=2958
FM_TEST_SUMMARY_FAMILY family=pure-contract-unit count=3 duration_ms=2709 failed=0
FM_TEST_SLOWEST rank=1 script=tests/fm-documentation-audiences.test.sh duration_ms=1739
FM_TEST_SLOWEST rank=2 script=tests/fm-captain-translation-contract.test.sh duration_ms=517
FM_TEST_SLOWEST rank=3 script=tests/fm-instruction-owners.test.sh duration_ms=453

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

⚠️ **Rebase** - 1 warning
  • ⚠️ .agents/skills/afk/SKILL.md - branch carries 5 commit(s) that exist on your local main branch but were never pushed to origin/main; rebasing would bundle this unrelated work (38 file(s)) into the PR:
  • 57e2c36 no-mistakes(document): Update watcher prompt docs
  • f60057c no-mistakes(review): Defer wake prompts during queue locks
  • 058c5ed no-mistakes(review): guard wake latch with send epochs and catch Pi rejection
  • 14f5935 no-mistakes(review): fix OpenCode wake latch recovery and pending reason merging
  • 817a9e6 fix: coalesce delayed watcher wake prompts

Push main to origin, or rebase your branch onto origin/main, before gating.

⚠️ **Review** - 2 infos
  • ℹ️ .pi/extensions/fm-primary-pi-watch.ts:515 - The coalescing latch is released at the harness settle/idle event (Pi agent_settled, OpenCode session.idle) that can precede the injected handling turn when the latching send happened mid-turn. A watcher close arriving in that window schedules a second prompt whose later drain finds an empty queue (the first handling turn drains both records). This is benign - the second prompt is queue-revalidated at send time and the drain is idempotent - and it matches the documented release points in docs/watcher-continuity.md, but debuggers observing an occasional extra wake prompt with an empty drain should know it is a designed tradeoff, not a regression of the fix.
  • ℹ️ .opencode/plugins/fm-primary-watch-arm.js:239 - The ~100-line flush/coalescing state machine (queuedWakeSnapshot, scheduleQueueLockRetry, flushWakePrompt, requestActionableWake, surfaceFailure) is duplicated nearly verbatim between the OpenCode plugin (JS) and the Pi extension (TS). This follows the repo's established per-harness self-containment convention (fm-operational-input is likewise duplicated per adapter), so no shared abstraction is demanded - but any future fix to this logic must be mirrored in both files, as this branch itself had to do across its five commits. The docs and paired regression tests already bind the two implementations together.
✅ **Test** - passed

✅ No issues found.

  • git log --graph/--format='%H %P' over b29621b..14d2eb0 - confirmed the five exact local commits, the two-parent merge dd4ce67 (57e2c36 + b29621b), and the .idea ignore commit 14d2eb0
  • git ls-remote origin refs/pull/1141/head refs/heads/main - proved PR 1141 head == 57e2c36 (tip of the five preserved commits) and remote main == the merge's upstream parent
  • gh-axi pr view 1141 - confirmed PR 1141 is closed and unmerged (superseded by this branch)
  • git merge-tree --write-tree 57e2c36 b29621b compared against dd4ce67's tree - only the genuine SKILL.md conflict differs, and stage-by-stage diffs proved the hand resolution keeps upstream's pi-signed changes and the branch's watcher-continuity line with no lost content
  • bin/fm-test-run.sh tests/fm-pi-watch-extension.test.sh - 40/40 assertions passed covering Pi and OpenCode wake coalescing, queue-lock deferral, stale-send suppression, send-epoch latch guarding, Pi rejection handling, session-lock ownership, and turn-end guard coordination
  • bin/fm-test-run.sh tests/fm-captain-translation-contract.test.sh tests/fm-documentation-audiences.test.sh tests/fm-instruction-owners.test.sh - all passed, guarding the hand-merged SKILL.md and changed docs against supervision-contract regressions
  • manual git check-ignore -v .idea/workspace.xml - matched by .gitignore:6:.idea/, and git status stayed clean with an untracked .idea directory present
✅ **Document** - passed

✅ No issues found.

⚠️ **Lint** - 1 warning
  • ⚠️ linter found issues (exit code 127)
✅ **Push** - passed

✅ No issues found.

@pokorny344 pokorny344 closed this Jul 28, 2026
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