Conversation
…, and state artifacts
…unknown branch; dedupe identity
…efuse path; matrix rerun still in flight, doc transcript not yet replaced
…provenance and pointers
…OMP bun, gate tmux OMP probe
Preserve launch-bound OMP identity inside the generalized session-lock matcher, combine test-lane and supervision ownership, and carry current upstream main without rewriting history.
Enable OMP as the native Firstmate harness
Author
|
Closing because this fork-specific watcher correction was opened against upstream by the local delivery configuration. The validated branch belongs in dnth/firstmate. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Intent
Make the already-landed OMP daily-driver branch safe across primary watcher-core factory re-bindings. Preserve one shared active watcher generation and one process-exit listener ownership path across factory re-bindings. A factory re-binding must retire or supersede the old core so it cannot remain live, start a duplicate arm child, or accumulate exit listeners. Preserve current OMP and Pi behavior outside this defect. Extend the existing behavioral test surface in tests/fm-pi-watch-extension.test.sh or the authoritative watcher-core owner; the regression must fail on current main and pass with the correction, covering factory re-binding, duplicate arm prevention, and exit-listener ownership. Run the focused watcher-core, OMP, and Pi tests plus the repository lint required for changed tracked files. Keep the final diff limited to the correction and its behavioral coverage, with no .omc runtime artifacts or unrelated changes. Deliver through the configured no-mistakes PR path against dnth/firstmate:main and do not merge the PR. Use the prior unpublished validation commit only as evidence; do not copy its .omc artifacts, unrelated ignore changes, PR-polish changes, or old task chronology.
What Changed
bin/fm-primary-watch-core.tsand binds both.pi/extensions/fm-primary-pi-watch.tsand the new.omp/extensions/fm-primary-omp.tsto it. The core keeps exactly one active generation and one process-exit listener across factory re-bindings, and a superseded binding can no longer reclaim ownership, kill the live arm child, or accumulate exit listeners.bin/fm-omp-capabilities.sh,bin/fm-omp-process-lib.sh), harness/backend/spawn/send/session-start wiring for primary, secondmate, and worker roles, and tmux plus Herdr backend support including OMP busy-footer detection.tests/fm-omp-*.test.sh), a factory re-bind regression intests/fm-pi-watch-extension.test.sh, and documents the protocol indocs/supervision-protocols/omp.mdplus the backend, verification, and AFK/harness-adapter skill docs.Risk Assessment
✅ Low: The new work over the already-landed base is a 5-line binding-token guard plus its direct regression coverage, it closes the round-1 ownership gap at the shared boundary without altering any live-binding path, and no residual reachable route for a superseded core to reclaim the active generation or the exit listener remains.
Testing
Ran the focused watcher-core, OMP, and Pi suites plus a targeted red/green proof of the new regression. The new re-bind test fails with the pre-fix watcher core (duplicate process-exit listener, superseded arm child still alive) and passes with the correction; a manual side-by-side demo shows the operator-visible difference — before the fix a factory re-bind leaves two live arm children, two exit listeners, and a superseded binding that still claims ownership, after the fix one arm child, one exit listener, and the superseded binding refuses to arm. OMP primary/secondmate/harness, Pi calm, Pi-compatible family, and watcher-lock suites are green. Two failures in the focused set (the OpenCode session-lock case and the Pi type-check suite) reproduce identically on a clean checkout of upstream main and touch no file in this diff, so they are pre-existing rather than regressions. No visual/UI surface is involved — the change is a CLI/daemon watcher lifecycle fix, so evidence is CLI transcripts of arm-child and exit-listener state.
Evidence: Watcher re-bind before/after transcript (arm children + exit listeners)
=== BEFORE FIX (watcher core at b6fe376 = pre-fix main) === after bind #1: live arm children=[547546] exit listeners=1 --- runtime re-binds the watcher factory (no session_shutdown) --- after bind #2: live arm children=[547546] exit listeners=2 superseded bind arm -> ok=true msg="watcher: unchanged - Pi extension already owns an arm child..." after bind #2 arms: live arm children=[547546,547615] exit listeners=2 === AFTER FIX (watcher core at 9b4e407) === after bind #1: live arm children=[541556] exit listeners=1 --- runtime re-binds the watcher factory (no session_shutdown) --- after bind #2: live arm children=[] exit listeners=1 superseded bind arm -> ok=false msg="watcher: not armed - Pi session is shutting down" after bind #2 arms: live arm children=[541634] exit listeners=1Evidence: Regression fails on pre-fix watcher core
not ok - A fresh factory bind must supersede the prior binding and keep one exit fallback: expected exit 0, got 1 FM_TEST_SUMMARY total=1 failed=1Evidence: Pre-fix scenario detail (listener count + un-retired child)
PREFIX-NOTE: exit listeners after 2nd bind = 2 (expected 1) Error: timeout waiting for superseded bind child exitEvidence: Focused watcher-core / OMP / Pi suite run at 9b4e407
ok - Pi factory re-bind without shutdown retires the prior generation and exit fallback stays singular ok - Pi process-exit cleanup listener remains singular across session replacementEvidence: Upstream main (33a4287) baseline showing the two failures pre-date this branch
not ok - OpenCode watch plugin must arm only when this session owns the fleet lock: expected exit 0, got 1 fm-calm.ts(248,7): error TS2322 ... fm-calm.ts(290,57): error TS2345 FM_TEST_SUMMARY total=2 failed=2Evidence: Manual re-bind demo script (reusable)
Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
.agents/skills/afk/SKILL.md- branch carries 65 commit(s) that exist on your local main branch but were never pushed to origin/main; rebasing would bundle this unrelated work (113 file(s)) into the PR:Push main to origin, or rebase your branch onto origin/main, before gating.
🔧 Fix applied.
1 warning still open:
.agents/skills/afk/SKILL.md- branch carries 65 commit(s) that exist on your local main branch but were never pushed to origin/main; rebasing would bundle this unrelated work (113 file(s)) into the PR:Push main to origin, or rebase your branch onto origin/main, before gating.
🔧 **Review** - 2 issues found → auto-fixed ✅
bin/fm-primary-watch-core.ts:564-sessionStart()is the only retained core callback with no supersession guard. After a factory re-bind, the old core'sgeneration.stoppingis true, sosessionStart()mints a fresh generation andactivateGeneration(line 143) SIGTERMs the currently live binding's arm child and installs the dead binding asactiveGeneration. Concrete path: OMP binds core C1; host re-binds -> C2 active, C1 retired; the OMP adapter's retainedsession_switchhandler on C1 (.omp/extensions/fm-primary-omp.ts:210-216) fires ->C1.sessionShutdown(); C1.sessionStart(); C1.arm()-> C2's arm child is killed and C2'sfm_watch_arm_omptool now answerswatcher: not armed - OMP session is shutting downwhile the retired binding owns the watcher. This contradicts the invariant this commit added at lines 10-12 ("Stale callbacks from an earlier generation, including callbacks retained by a superseded core instance, are no-ops against the active replacement") and the intent's "a factory re-binding must retire or supersede the old core so it cannot remain live". Reachability requires the host to keep delivering session events to the superseded binding; the new test gives each bind a fresh API object (makePi()at tests/fm-pi-watch-extension.test.sh:1140/1153/1183), so this path is never exercised. Earliest shared boundary that closes it: record a module-level active-binding token increatePrimaryWatchCorealongsideactiveGeneration, and makesessionStart(andsessionShutdown) no-ops when the caller's binding token is not the active one - then extend the new test to call the superseded binding'ssession_starthandler and assert the live child and live-binding tool are untouched.bin/fm-primary-watch-core.ts:126- The single-active-generation and single-exit-listener guarantees are anchored in module scope, so they hold only while the module cache is retained across re-binds. A re-bind that re-imports the extension through a cache-busting specifier would give each module instance its ownactiveGeneration/exitFallbackInstalledand reintroduce both the duplicate arm child and the accumulating exit listener. docs/verification/supervision.md:281 states the modeled re-bind is "module cache retained", so this is a documented boundary rather than a defect - noting it because the header comment claims the ownership is "process-wide", which is only true under that assumption. AglobalThissymbol-keyed registry would make the claim literal if the assumption ever changes.🔧 Fix: guard superseded watcher bindings from reclaiming ownership
✅ Re-checked - no issues remain.
tests/fm-pi-watch-extension.test.sh:1501- tests/fm-pi-watch-extension.test.sh: "OpenCode watch plugin must arm only when this session owns the fleet lock" fails locally (arm never runs after the lock is re-acquired). Not caused by this change: .opencode/plugins/fm-primary-watch-arm.js and this test body are byte-identical to upstream main, and the same failure reproduces from a cleangit archive 33a4287checkout. Pre-existing/environmental; flagged because it will also fail the branch's CI lane.tests/fm-pi-primary-types.test.sh- tests/fm-pi-primary-types.test.sh fails with two TS errors in fm-calm.ts (TS2322 at 248:7, TS2345 at 290:57) against the locally installed Pi type definitions. Reproduces identically on upstream main (33a4287); unrelated to the watcher-core rebind fix.bin/fm-test-run.sh tests/fm-pi-watch-extension.test.sh tests/fm-omp-primary.test.sh tests/fm-omp-harness.test.sh tests/fm-calm-pi-extension.test.sh tests/fm-pi-primary-types.test.sh tests/fm-pi-compatible-family.test.shbin/fm-test-run.sh tests/fm-omp-secondmate.test.sh tests/fm-watcher-lock.test.sh(all pass)Red-state proof: extracted the target tree to /tmp, replaced onlybin/fm-primary-watch-core.tswith the pre-fixb6fe376version (sha256 verified), then ranbin/fm-test-run.sh tests/fm-pi-watch-extension.test.sh— new testPi factory re-bind without shutdown retires the prior generation and exit fallback stays singularfails there and passes at 9b4e407Pre-fix scenario probe with the exit-listener assertions relaxed: showedexit listeners after 2nd bind = 2 (expected 1)andtimeout waiting for superseded bind child exitManual end-user demo/tmp/no-mistakes-evidence/01KZ3J4X5PMS0WXFS815SEDQRE/demo.sh <tree> <label>run against both the pre-fix and fixed trees, printing live arm-child PIDs, process-exit listener count, and the arm-tool response of the superseded bindingBaseline check:git archive 33a4287 | tar -xthenbin/fm-test-run.sh tests/fm-pi-watch-extension.test.sh tests/fm-pi-primary-types.test.shto confirm the two failures pre-date this branchStandalone repro of the OpenCode session-lock scenario against the unchanged.opencode/plugins/fm-primary-watch-arm.jsgit status --short --branch— worktree clean, no stray test artifacts✅ **Document** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.