feat: centralize process-event arm and delivery wiring - #3045
Conversation
…er check The adjacent-follow-up E2E case captured the tmux pane for its duplicate-captain-answer assertion immediately after the session file confirmed processing, with no settle wait, unlike every other readiness check in this test. Sending two followUp deliveries queues more Calm presentation work (an extra operational-user row plus its hiding invalidation) than a single one, so the already-settled captain answer's redraw could still be in flight at that instant, making the check flaky. Poll the pane the same way the session-file wait already does, and track the peak count seen along the way so a captain answer that is genuinely rendered twice for even one frame still fails even if a later redraw were to self-correct.
…ss gates Adds the two triggers from data/fm-auto-stow/report.md so the captain no longer has to type /stow to keep memory current, without a new daemon, watcher, or cascade: - bin/fm-session-start.sh prepends a STOW DUE line to a compact/clear session-start re-emit when state/.last-stow is missing or older than FM_AUTO_STOW_INTERVAL_SECS (default ~24h), silent when current. - AGENTS.md section 8 rule 4 now also checks that same marker on a heartbeat wake, using the same larger-than-heartbeat interval, so a pass runs at most once per interval rather than on every wake. - The stow skill touches state/.last-stow only at the end of a pass it can call reset-safe, mirroring state/.last-heartbeat's bare-mtime marker. Away-mode heartbeats stay bash-only and unaffected: they never reach an LLM turn to run /stow in, per the existing away-daemon design.
Vendor the lalo-admin assessor already proven on mrbeanz-brains, with the same drift pin and live SoT comparison, so firstmate PRs cannot skip the required overview, decision, module-boundary, and validation sections.
Promote the last-stow marker contract out of the cascade heading so a secondmate home still throttles automatic /stow. Pin the stale re-emit age assertion to a 9xxxx band instead of the prefix 900. Document that default Pi branch supervision does not run heartbeat /stow, and leave that wiring as follow-up (kunchenguid#2944) rather than editing fm-branch-prompt.sh.
Adds a stdlib-only Python server that shells quota-axi --json per request and serves one self-contained page (fleet summary, one card per provider, live/signed-out/error states in quota-axi's own words, 30s client refresh), matching the design in data/fm-quota-dashboard/report.md. Binds only to this host's own Tailscale IPv4 address, confirmed via `tailscale ip -4`, and refuses to start otherwise - never 0.0.0.0, never a public interface.
Raise the shell-out to 45s so one slow provider degrades instead of blanking the page. Prove the listener is address-scoped via 127.0.0.2, and keep the scripts index voice cluster intact.
Checking out the base branch cannot execute the new checker until it lands, so restore pull_request and assess the proposed head instead.
fix(test): settle Pi follow-up pane before the duplicate-captain-answer check
feat(bin): serve quota dashboard on the tailnet
Vendor the lalo-admin assessor already proven on mrbeanz-brains, with the same drift pin and live SoT comparison, so firstmate PRs cannot skip the required overview, decision, module-boundary, and validation sections.
Adds .agents/skills/gnhf-companion/SKILL.md, the single owner of Hands-Off and Companion mode for the installed GNHF CLI: GNHF only ever runs inside a crewmate's own already-isolated task worktree, on --current-branch, under explicit iteration/token/rate-limit-wait caps and an observable --stop-when condition, and its completion rides the existing crewmate status/worker supervision path rather than a new process-event source, watcher, or control plane. GNHF grants no merge, approval, or no-mistakes-bypass authority. One trigger line lands in AGENTS.md section 13. tests/fm-gnhf-companion-skill.test.sh pins the skill's frontmatter and the AGENTS.md trigger, and (self-skipping when gnhf isn't installed) guards the installed CLI's --help flags against drift. docs/verification/gnhf-companion.md records dated bounded-smoke evidence from a disposable scratch repo, proving completion, the failure/abort path, and preserved history, run with the installed gnhf 0.1.45.
Keep the automatic stow updates alongside the fail-closed CEO-overview validation changes. Co-authored-by: Cursor <cursoragent@cursor.com>
…enguid#2966) (#1) * feat(bin): add a Telegram process-event adapter Registers the captain's Telegram channel with the generic process-to-event runner so a captain message wakes firstmate within seconds instead of waiting up to five minutes for a check sweep. The adapter is deliberately thin: it owns Telegram's getUpdates long poll, the write-before-offset invariant that keeps a captain message from being lost, and token handling; ownership, durable capture, publication, and restart recovery stay with bin/fm-procevent.sh. The channel is never terminal on its own - only an explicit retire stops it. * no-mistakes(review): Authenticate Telegram captain message ingestion * no-mistakes(review): Enforce private Telegram credential permissions * no-mistakes(review): Make Telegram inbox writes crash durable * no-mistakes(document): Clarify Telegram adapter documentation ownership * no-mistakes(review): Prevent duplicate Telegram delivery after handoff * no-mistakes(review): Require legacy Telegram check retirement before arm * no-mistakes(review): Recover Telegram wakes after offset failures * no-mistakes(review): Document Telegram pre-capture crash limitations * no-mistakes(review): Recover pending Telegram wakes without credentials * no-mistakes(test): Fix Telegram handoff overlap contract * no-mistakes(document): Polish Telegram channel documentation * fix(bin): make Telegram inbox delivery atomic against the legacy check The prior handoff-safety fixes checked whether an update id's inbox file already existed and then wrote it, which cannot be safe against state/telegram-watch.check.sh: that home-local script writes in place with no temp file and no rename, so its output can be observed mid-write, and a check-then-write gap can still race it. Replace that with an atomic claim: this adapter always writes its own complete, fsynced payload to a private temp file first, then hardlinks that finished file onto the shared <update_id>.json name. A successful hardlink is an exclusive, race-free claim. A failed one (name already taken) is resolved by parsing whatever is already there - a complete, well-formed payload for that update means another claimant (the legacy script or an earlier invocation of this adapter) already delivered it, so this poll no-ops without a second captain-visible wake; anything else means a claimant, most likely the legacy script, is still mid-write, and that update blocks the whole batch's offset advance exactly like a failed write, so an unadvanced retry gives the write time to finish. handled/ is checked first so an archived update is never recreated in the live inbox. This does not make true simultaneous overlap (both producers inside getUpdates for the same not-yet-advanced offset) free - the legacy script has no knowledge of this adapter and can still fire its own independent wake through the check sweep, which nothing here can suppress. Only ensuring no legacy invocation is genuinely in flight before arming (not merely deregistering it) closes that window; the header documents this plainly rather than claiming a guarantee the design cannot make. Also keeps the two accumulated fixes this branch already carries: a durable pending-delivery record so an offset-write failure never strands an already- written message, checked and reported before any credential validation. Adds a regression test that reproduces the legacy script's exact non-atomic write shape mid-write, overlapping a batch that also contains a genuinely new update, and proves the batch blocks without corruption or duplication and resolves correctly once the legacy write finishes. * fix(bin): restore accepted Telegram safety handling Carry the accepted Telegram adapter and regression coverage onto a fresh validation branch, including permanent API failure signaling and receipt recovery protections. Co-authored-by: Cursor <cursoragent@cursor.com> * feat(bin): add a Telegram process-event adapter Registers the captain's Telegram channel with the generic process-to-event runner so a captain message wakes firstmate within seconds instead of waiting up to five minutes for a check sweep. The adapter is deliberately thin: it owns Telegram's getUpdates long poll, the write-before-offset invariant that keeps a captain message from being lost, and token handling; ownership, durable capture, publication, and restart recovery stay with bin/fm-procevent.sh. The channel is never terminal on its own - only an explicit retire stops it. * no-mistakes(review): Authenticate Telegram captain message ingestion * no-mistakes(review): Enforce private Telegram credential permissions * no-mistakes(review): Make Telegram inbox writes crash durable * no-mistakes(document): Clarify Telegram adapter documentation ownership * fix(bin): make Telegram inbox delivery atomic against the legacy check The prior handoff-safety fixes checked whether an update id's inbox file already existed and then wrote it, which cannot be safe against state/telegram-watch.check.sh: that home-local script writes in place with no temp file and no rename, so its output can be observed mid-write, and a check-then-write gap can still race it. Replace that with an atomic claim: this adapter always writes its own complete, fsynced payload to a private temp file first, then hardlinks that finished file onto the shared <update_id>.json name. A successful hardlink is an exclusive, race-free claim. A failed one (name already taken) is resolved by parsing whatever is already there - a complete, well-formed payload for that update means another claimant (the legacy script or an earlier invocation of this adapter) already delivered it, so this poll no-ops without a second captain-visible wake; anything else means a claimant, most likely the legacy script, is still mid-write, and that update blocks the whole batch's offset advance exactly like a failed write, so an unadvanced retry gives the write time to finish. handled/ is checked first so an archived update is never recreated in the live inbox. This does not make true simultaneous overlap (both producers inside getUpdates for the same not-yet-advanced offset) free - the legacy script has no knowledge of this adapter and can still fire its own independent wake through the check sweep, which nothing here can suppress. Only ensuring no legacy invocation is genuinely in flight before arming (not merely deregistering it) closes that window; the header documents this plainly rather than claiming a guarantee the design cannot make. Also keeps the two accumulated fixes this branch already carries: a durable pending-delivery record so an offset-write failure never strands an already- written message, checked and reported before any credential validation. Adds a regression test that reproduces the legacy script's exact non-atomic write shape mid-write, overlapping a batch that also contains a genuinely new update, and proves the batch blocks without corruption or duplication and resolves correctly once the legacy write finishes. * fix(bin): restore accepted Telegram safety handling Carry the accepted Telegram adapter and regression coverage onto a fresh validation branch, including permanent API failure signaling and receipt recovery protections. Co-authored-by: Cursor <cursoragent@cursor.com> * no-mistakes(review): Fix Telegram blocked lifecycle and credential-gated recovery * no-mistakes(review): Validate Telegram success before clearing blocked state * no-mistakes(document): Document Telegram process-event verification * no-mistakes: apply CI fixes * no-mistakes(review): Reject invalid Telegram update identifiers * fix(telegram): prevent duplicate wakes after cleanup failure Co-authored-by: Cursor <cursoragent@cursor.com> * no-mistakes(review): Move Telegram claim temp out of inbox, unify credential read * no-mistakes(document): document Telegram blocked, identifier, and cleanup-order contracts * no-mistakes(test): parse ci.yml timeouts with python3 yaml, ruby fallback --------- Co-authored-by: Cursor <cursoragent@cursor.com>
* feat(bin): add a Telegram process-event adapter Registers the captain's Telegram channel with the generic process-to-event runner so a captain message wakes firstmate within seconds instead of waiting up to five minutes for a check sweep. The adapter is deliberately thin: it owns Telegram's getUpdates long poll, the write-before-offset invariant that keeps a captain message from being lost, and token handling; ownership, durable capture, publication, and restart recovery stay with bin/fm-procevent.sh. The channel is never terminal on its own - only an explicit retire stops it. * no-mistakes(review): Authenticate Telegram captain message ingestion * no-mistakes(review): Enforce private Telegram credential permissions * no-mistakes(review): Make Telegram inbox writes crash durable * no-mistakes(document): Clarify Telegram adapter documentation ownership * fix(bin): make Telegram inbox delivery atomic against the legacy check The prior handoff-safety fixes checked whether an update id's inbox file already existed and then wrote it, which cannot be safe against state/telegram-watch.check.sh: that home-local script writes in place with no temp file and no rename, so its output can be observed mid-write, and a check-then-write gap can still race it. Replace that with an atomic claim: this adapter always writes its own complete, fsynced payload to a private temp file first, then hardlinks that finished file onto the shared <update_id>.json name. A successful hardlink is an exclusive, race-free claim. A failed one (name already taken) is resolved by parsing whatever is already there - a complete, well-formed payload for that update means another claimant (the legacy script or an earlier invocation of this adapter) already delivered it, so this poll no-ops without a second captain-visible wake; anything else means a claimant, most likely the legacy script, is still mid-write, and that update blocks the whole batch's offset advance exactly like a failed write, so an unadvanced retry gives the write time to finish. handled/ is checked first so an archived update is never recreated in the live inbox. This does not make true simultaneous overlap (both producers inside getUpdates for the same not-yet-advanced offset) free - the legacy script has no knowledge of this adapter and can still fire its own independent wake through the check sweep, which nothing here can suppress. Only ensuring no legacy invocation is genuinely in flight before arming (not merely deregistering it) closes that window; the header documents this plainly rather than claiming a guarantee the design cannot make. Also keeps the two accumulated fixes this branch already carries: a durable pending-delivery record so an offset-write failure never strands an already- written message, checked and reported before any credential validation. Adds a regression test that reproduces the legacy script's exact non-atomic write shape mid-write, overlapping a batch that also contains a genuinely new update, and proves the batch blocks without corruption or duplication and resolves correctly once the legacy write finishes. * fix(bin): restore accepted Telegram safety handling Carry the accepted Telegram adapter and regression coverage onto a fresh validation branch, including permanent API failure signaling and receipt recovery protections. Co-authored-by: Cursor <cursoragent@cursor.com> * feat(bin): add a Telegram process-event adapter Registers the captain's Telegram channel with the generic process-to-event runner so a captain message wakes firstmate within seconds instead of waiting up to five minutes for a check sweep. The adapter is deliberately thin: it owns Telegram's getUpdates long poll, the write-before-offset invariant that keeps a captain message from being lost, and token handling; ownership, durable capture, publication, and restart recovery stay with bin/fm-procevent.sh. The channel is never terminal on its own - only an explicit retire stops it. * no-mistakes(review): Authenticate Telegram captain message ingestion * no-mistakes(review): Enforce private Telegram credential permissions * no-mistakes(review): Make Telegram inbox writes crash durable * no-mistakes(document): Clarify Telegram adapter documentation ownership * fix(bin): make Telegram inbox delivery atomic against the legacy check The prior handoff-safety fixes checked whether an update id's inbox file already existed and then wrote it, which cannot be safe against state/telegram-watch.check.sh: that home-local script writes in place with no temp file and no rename, so its output can be observed mid-write, and a check-then-write gap can still race it. Replace that with an atomic claim: this adapter always writes its own complete, fsynced payload to a private temp file first, then hardlinks that finished file onto the shared <update_id>.json name. A successful hardlink is an exclusive, race-free claim. A failed one (name already taken) is resolved by parsing whatever is already there - a complete, well-formed payload for that update means another claimant (the legacy script or an earlier invocation of this adapter) already delivered it, so this poll no-ops without a second captain-visible wake; anything else means a claimant, most likely the legacy script, is still mid-write, and that update blocks the whole batch's offset advance exactly like a failed write, so an unadvanced retry gives the write time to finish. handled/ is checked first so an archived update is never recreated in the live inbox. This does not make true simultaneous overlap (both producers inside getUpdates for the same not-yet-advanced offset) free - the legacy script has no knowledge of this adapter and can still fire its own independent wake through the check sweep, which nothing here can suppress. Only ensuring no legacy invocation is genuinely in flight before arming (not merely deregistering it) closes that window; the header documents this plainly rather than claiming a guarantee the design cannot make. Also keeps the two accumulated fixes this branch already carries: a durable pending-delivery record so an offset-write failure never strands an already- written message, checked and reported before any credential validation. Adds a regression test that reproduces the legacy script's exact non-atomic write shape mid-write, overlapping a batch that also contains a genuinely new update, and proves the batch blocks without corruption or duplication and resolves correctly once the legacy write finishes. * fix(bin): restore accepted Telegram safety handling Carry the accepted Telegram adapter and regression coverage onto a fresh validation branch, including permanent API failure signaling and receipt recovery protections. Co-authored-by: Cursor <cursoragent@cursor.com> * no-mistakes(review): Fix Telegram blocked lifecycle and credential-gated recovery * no-mistakes(review): Validate Telegram success before clearing blocked state * no-mistakes(document): Document Telegram process-event verification * no-mistakes: apply CI fixes * no-mistakes(review): Reject invalid Telegram update identifiers * fix(telegram): prevent duplicate wakes after cleanup failure Co-authored-by: Cursor <cursoragent@cursor.com> * no-mistakes(review): Move Telegram claim temp out of inbox, unify credential read * no-mistakes(document): document Telegram blocked, identifier, and cleanup-order contracts * feat(telegram): make channel state transactional Commit accepted messages, stable notices, and irreversible offsets atomically so malformed input and local corruption cannot silently lose the captain channel. Co-authored-by: Cursor <cursoragent@cursor.com> * no-mistakes(review): Fix Telegram migration crash, blocked containment, and stale docs * no-mistakes(review): Separate recoverable Telegram migration refusals from blocked cutover * no-mistakes(review): Make Telegram migration archive transactional and causes actionable * no-mistakes(review): Close Telegram migration publication and verification crash windows * no-mistakes(review): Make Telegram database publication a monotonic recovery boundary * no-mistakes(review): Bound Telegram cutover transaction, journal, and poll-body lifecycles * no-mistakes(review): Import handled Telegram legacy rows as dedup tombstones * no-mistakes(review): Frame Telegram poll responses without staging them on disk * no-mistakes(document): Document Telegram poll listener, engine, and new proofs * no-mistakes: apply CI fixes * no-mistakes(review): Fix Telegram blocked-recovery, absent-sender, and state-path defects * no-mistakes(review): Encode full SQLite URI path and guard migrate engine check * no-mistakes: apply CI fixes * no-mistakes: apply CI fixes * no-mistakes: apply CI fixes * no-mistakes: apply CI fixes --------- Co-authored-by: Cursor <cursoragent@cursor.com>
Confidence Score: 5/5The PR appears safe to merge because no blocking failure remains from the prior review thread. The previously reported duplicated ownership wording and false exclusive-writer statement have been corrected at current HEAD, and no incomplete or unsafe fix was established. Reviews (8): Last reviewed commit: "no-mistakes: apply CI fixes" | Re-trigger Greptile |
* fix: safely split supervision wake handling by actor (kunchenguid#2953) * feat(bin,pi): per-actor wake consume, silent success gating, merge-poll dedup Three related fixes to the shared wake-drain and Pi supervision-branch dispatch machinery so a routine success is never main-blocking and a mixed queue can safely split between actors. 1. Successful routine results no longer create main-blocking wake rows. fm-startup-network.sh only enqueues a check: startup-network wake when the deferred result is actionable (state is not "done", or the report carries a bootstrap-diagnostics actionable prefix); a clean success stays durable in the report file without ever waking the agent. 2. Per-actor wake-drain consume contract. bin/fm-wake-drain.sh now scopes presentation and --ack-through to the current actor (bin/fm-lease-lib.sh's fm_lease_actor): main keeps the original whole-queue cutoff behavior, unaffected. A branch actor (FM_SUPERVISION_ACTOR=branch, set only inside the Pi supervision branch's own bash tool calls) is scoped to an explicit eligible-row snapshot instead of a cutoff comparison, so it can never remove a row it was not granted - the fix for the swallow risk that used to force an all-or-nothing whole-queue fallback to main. .pi/extensions/lib/fm-branch-dispatch.ts's scopeForUnreadWake is the single owner of eligibility: a check-kind row (merge-confirmation polls, Relay mentions, credential/auth failures) is now excluded rather than vetoing the whole scan for a non-heartbeat wake, while a heartbeat review keeps its original all-or-nothing rule unchanged. writeEligibleRowsSnapshot publishes the exact eligible sequence numbers before every branch prompt; fm-primary-pi-watch.ts's offer still refuses a check-kind trigger outright so a main-only close is never itself routed to the branch. 3. A repeat identical merged-PR-poll result for an already-notified task is absorbed instead of enqueued again. A poll's own retirement state is scoped to one registration and cannot see a prior registration's outcome, so a task re-registered after its merge was already surfaced would otherwise wake main a second time for the same event. bin/fm-pr-lib.sh's new per-task pr-poll-merge-notified marker survives across re-registrations to catch that case; the first notification for a task still reaches main unchanged. Regression tests colocated in tests/fm-startup-network.test.sh, tests/fm-wake-queue.test.sh (including the mixed-queue no-swallow property), tests/fm-pi-branch-extension.test.sh, and tests/fm-pr-check-security.test.sh. docs/watcher-continuity.md and docs/pi-supervision-branch.md updated for the new contracts. * no-mistakes(review): Bind merge deduplication to canonical PR identity * no-mistakes(review): Serialize wake row ownership across main and branch * no-mistakes(review): Bind branch grants and deduplicate within actor claims * no-mistakes(review): Fallback main-owned wake claims to main delivery * no-mistakes(review): Clarify silent startup success guidance * no-mistakes(review): Release residual branch grants after settled prompts * no-mistakes(review): Reject truncated wake rows as corrupted * no-mistakes(document): Document per-actor routing and silent startup success * no-mistakes(lint): Fix ShellCheck findings in wake grant and startup test * no-mistakes: apply CI fixes * fix(pi): hide branch outcomes tool rows in Calm (kunchenguid#3024) * Hide branch outcome tool in Pi Calm * no-mistakes(review): Preserve stock outcomes rendering and document tool audit * no-mistakes(review): Document branch read tool audit disposition * no-mistakes(review): Match stock outcomes output sanitization * no-mistakes(document): Document Calm custom-tool visibility * Add fleet-wide PR merge conflict watcher with routed wakes. Detect newly conflicted open PRs from registry-derived repos, dedupe by head SHA, poll lazy GitHub mergeability safely, and arm through the standard registered check path. * no-mistakes(review): fix PR conflict watch cap suppression, budget overrun, record pruning * no-mistakes(document): document PR conflict watch routing, silence, and record limits * no-mistakes: apply CI fixes * no-mistakes: apply CI fixes * no-mistakes: apply CI fixes * no-mistakes: apply CI fixes * no-mistakes(review): fix PR conflict watch gh-axi reads and disclose unread repos * no-mistakes(review): read PRs via one GraphQL call and disclose unswept repos * Rebuild PR conflict coverage as a ledger of unobserved targets. Conflict detection already worked; four review rounds patched the same coverage-disclosure hole. A gap is now keyed by a stable target with a frozen opened_at, cause is metadata that never resets age, and wake lines ack by identity rather than by scanning the rendered text. * no-mistakes(review): Fix conflict delivery, reread metadata, and repository parsing * no-mistakes(review): Distinguish origin failures and correct coverage documentation * no-mistakes(review): Preserve typed project origin failure causes * no-mistakes(review): Accept port-qualified GitHub SSH origins * no-mistakes(review): Track missing runtime dependencies as coverage gaps * no-mistakes(review): Persist independent coverage recovery lifecycle * no-mistakes(review): Build structural GitHub origin parser * no-mistakes(document): Correct stale conflict-watch documentation * no-mistakes(lint): Fix ShellCheck declarations and intentional lint annotations * no-mistakes(review): Handle truncated GraphQL PR sets as coverage gaps * no-mistakes(document): Document bounded PR conflict coverage * no-mistakes: apply CI fixes * no-mistakes: apply CI fixes * no-mistakes: apply CI fixes * no-mistakes: apply CI fixes * no-mistakes: apply CI fixes --------- Co-authored-by: Kun Chen <3233006+kunchenguid@users.noreply.github.com>
dade480 to
8de39b2
Compare
|
Speaking as Kun's firstmate: Reviewed HEAD Class: default-behavior. The body describes a process-event arm/delivery extraction; the branch also lands automatic /stow, a Tailscale quota dashboard, a GNHF companion skill, a Telegram process-event adapter, a PR-conflict watcher, new PR-communication CI workflows, and related wiring (68 files, +12189). Mixed intent is default-behavior. VISION (per rule):
Conflicts with main (DIRTY). Body attestation Security: a new This is waiting on the author, not the captain, for a split to the stated extraction and a rebase onto current main. The workflow security review is separately with the captain; do not wait on that for the split. Merge-eligible: NO. |
Intent
Extract the duplicated process-event arm and delivery-shim wiring into one owner after reading bin/fm-procevent-lavish.sh, bin/fm-procevent-when.sh, bin/fm-procevent-remote-reply.sh, bin/fm-watch.sh, and bin/fm-procevent.sh; do not invent a second control plane. Keep one owner for the arm-and-shim contract: register the source, bind identity, start or let reconcile start the runner, and own generated check-shim bytes. Keep adapters thin with only adapter-specific identity, argv, classify/terminal/autohandle behavior, and policy. Keep capture, publication, ownership, and handled acknowledgement in the generic runner. Do not restate the runner contract in adapters, AGENTS.md, or a second document. Keep flag and refusal mechanics in the owning script header and point other mentions at that owner. Add portable executable behavior tests under tests/ named .test.sh; never assert implementation-source bytes. Run bin/fm-lint.sh on script changes. Preserve the target captain fork bingb0t5/firstmate, do not merge, and stop if the pipeline opens a PR against kunchenguid/firstmate, retaining pipeline-fix commits and reporting the blocked outcome.
What Changed
fm-procevent.sh armcommand while retainingregisteras a compatibility spelling, and migrated adapters to the shared arm seam.Risk Assessment
✅ Low: The extraction is well-bounded, preserves observable arm and delivery behavior, centralizes the intended ownership, and the prior contract-documentation contradiction is resolved.
Testing
The targeted portable process-event suite passed, and an end-to-end public CLI check demonstrated shared arming, durable payload capture, normalized wake delivery, and explicit acknowledgement; the worktree remained clean and the transcript was saved as reviewer-visible evidence.
Evidence: Public arm-to-delivery CLI transcript
Source: Public arm-to-delivery CLI transcript
Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
🔧 **Review** - 1 issue found → auto-fixed ✅
bin/fm-procevent-lavish.sh:114- Intent requires “Do not restate the runner contract in adapters, AGENTS.md, or a second document,” but this new adapter comment restates that the generic arm seam owns registration and reconcile-driven startup. Replace it with a pointer to the owningfm-procevent.shheader. Also remove/update AGENTS.md’s now-false claim thatstate/procevent/is “written only by bin/fm-procevent.sh,” since adapters now call the shared library directly.🔧 Fix: Centralize process-event contract ownership references
✅ Re-checked - no issues remain.
✅ **Test** - passed
✅ No issues found.
Inspectedgit diff 6a2cd6cd2c642c9422bb43dccedbd91103285fb0..72cbeda7fb827370064ca835f8b65d3bcf6d2ce8to map the intent to changed executable surfaces.Rantests/fm-procevent.test.sh.Manually exercisedbin/fm-procevent.sh arm,list,start,bin/fm-wake-drain.sh, andhandledwith an isolated home, recording the captured payload, normalized wake, and pending count returning to zero.✅ **Document** - passed
✅ No issues found.
✅ **Lint** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.