fix(omp): make task-inbox doorbell handshake truthful - #141
Merged
Conversation
A partially failed doorbell activation left .omp-ready present with no .omp-doorbell-ready marker and no diagnostic, so every fm-send reported omp-native-refused session-pid=unreadable and owned redelivery could never succeed. - activate() now returns whether the doorbell is live; the generated worker extension touches .omp-ready only after it succeeds. - Activation and drain failures journal their reason durably to <id>.omp-doorbell-failed (staged write, mode 0600) and still retire cleanly; a successful activation clears a stale journal. - fm-send refusals name the concrete artifact via FM_TASK_INBOX_RING_OMP_DOORBELL: doorbell-failure=<journal>, doorbell-marker-missing=<marker>, doorbell-marker-unreadable=<marker>, or doorbell-binding-unproven=<marker>. - fm-spawn bounded-waits for .omp-doorbell-ready after launch and fails the spawn loudly, naming the marker and the journaled reason when present. - teardown and the spawn abort/relaunch artifact lists cover the new journal.
…he failing secondmate fixtures to publish `.omp-doorbell-ready` alongside their OMP acknowledgement markers, and relaxed outdated preservation-text assertions to match the new bounded-wait failure path. `tests/fm-omp-secondmate.test.sh` passes; shell syntax and diff checks pass
…gression coverage
dnth
force-pushed
the
fm/fm-omp-doorbell-handshake-unwritten
branch
from
September 13, 2026 14:34
68cfe86 to
acd5544
Compare
…ll-escaped OMP doorbell paths before extraction, handling wrapped remote launches. bash -n, shellcheck, diff checks, and a fixture integration reproduction pass. The full lifecycle test reaches its pre-existing unrelated markerless-reconciliation failure before the OMP scenario; fm-lint.sh was started but terminated after an extended run without diagnostics
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 OMP task-inbox doorbell handshake observable and truthful: today a partially failed activate() leaves state/.omp-ready present with no state/.omp-doorbell-ready marker and no diagnostic, so every fm-send reports 'omp-native-refused session-pid=unreadable' and owned redelivery can never succeed. Required change: (1) touch .omp-ready only after activate() succeeds; (2) on activate failure, journal the reason durably (state/.omp-doorbell-failed with the error text) and keep retiring cleanly; (3) make fm-send's refusal message name the concrete missing marker / failure journal so firstmate sees WHY instead of 'unreadable'; (4) spawn-time verification: fm-spawn.sh waits (bounded) for .omp-doorbell-ready after .omp-ready and reports a clear spawn failure if it never appears. Tests: extension unit tests for activate success/failure marker behaviour; bash tests for spawn-time wait and the improved refusal text. Accepted Firstmate requirements: deterministic default failure-journal derivation from the ready marker (caller-neutral .omp-doorbell-failed. fallback when no marker configured); the bounded doorbell wait applies to every launch emitting the generated extension including secondmate OMP launches, while raw OMP launches without the generated template remain exempt; drain-time send failures (synchronous and asynchronous sendMessage rejections) journal 'drain' and retire the marker, and initial async sends are included in activation success so a rejection cannot leave .omp-ready published; existing PR #136 doorbell/turn-proof tests are preserved including test_requester_window_tracks_turn_grace_and_acked_suppresses and uncorrelated turn_start expectations. Firstmate-Validation-Generation: e9d3c7781595d3a306320cb18dca0177
What Changed
.omp-readyis published only after successful activation; activation and drain failures are durably journaled, owned markers retire cleanly, and initial async sends participate in activation success..omp-doorbell-readywait and clear failure reporting to template-based OMP spawns, including secondmate launches, while leaving raw OMP launches exempt.Risk Assessment
Testing
The focused executable tests covering activation success/failure journaling, refusal diagnostics, bounded OMP spawn waits, and secondmate extension handling passed. The required live OMP worker check was attempted but blocked by the no-mistakes gate-worktree fleet guard, so live scenarios remain untested.
fm-send.shrefused fleet lifecycle from this no-mistakes gate worktree. Provide a normal checkout/worktree (or equivalent fleet-driving authority) a…Evidence: Doorbell targeted tests
Evidence: Spawn doorbell targeted tests
Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
🔧 **Review** - 1 issue found → auto-fixed (7) ✅
.omp/extensions/lib/fm-task-inbox-doorbell.ts:390- The activation wait snapshotsactivationSendsonce at line 389. A request arriving while that snapshot is pending can start a second asynchronoussendMessage; that promise is added after the snapshot and is not awaited. If the first send resolves,activate()returns true and the generated extension touches.omp-ready; when the late send rejects, its handler only journals and retires the doorbell marker, leaving.omp-readybehind. This recreates the false-ready invariant during concurrent initial drain activity. Keep activation from resolving until all sends initiated during activation have settled, or otherwise propagate any later rejection before readiness is published.🔧 Fix applied.
1 warning still open:
bin/fm-task-inbox-lib.sh:252-fm_task_inbox_omp_doorbell_state()reportsdoorbell-binding-unproven=<marker>whenever the marker is valid but the native request fails for another reason. For example, if<marker>.requestsis missing during teardown/race,fm_omp_task_doorbell_request()returns 1, the refusal path reaches this helper, and fm-send claims the session binding is unproven even though the binding is valid and the request directory is the concrete failure. Add a state check for the request directory (and any similarly distinguishable request-write failure) so the refusal names the actual missing artifact instead of a false cause.🔧 Fix applied.
1 error still open:
.omp/extensions/lib/fm-task-inbox-doorbell.ts:329- A late rejection from an older doorbell generation can poison a newer healthy launch. Generation A starts an asyncsendMessage, is retired, and generation B reuses the same task marker and activates successfully (clearing the old journal). If A's promise then rejects, the handler at line 329 writes the shared failure journal even though A is no longer active;fm_task_inbox_omp_doorbell_state()prioritizes that journal over the live marker, sofm-sendrefuses delivery to generation B. Only journal/retire an async rejection while that doorbell instance still owns an active marker, or otherwise guard the journal write by generation ownership.🔧 Fix applied.
1 error still open:
.omp/extensions/lib/fm-task-inbox-doorbell.ts:341- An asynchronous drain rejection can lose the durable request whenomp.onis unavailable. Indrain(), the request is renamed from.ambiguousto.deliveredimmediately aftersendMessage()returns a Promise (line 341), before that Promise settles. If it later rejects, the handler journals and retires the doorbell (lines 328-332) but never reverts the.deliveredrecord to a retryable state. The watcher therefore treats the failed delivery as complete and owned redelivery cannot occur. Keep the request pending/failed until the async send succeeds, or move it back to a retryable state in the rejection path.🔧 Fix applied.
1 error still open:
.omp/extensions/lib/fm-task-inbox-doorbell.ts:359- A synchronous drain-timesendMessage()throw strands the durable request as.pending.ambiguous:invokedis set true before the call, so the catch at line 359 skips the only rename, then journals and retires the doorbell. On the next activation, reconciliation handles.processing.*and.awaiting-turnbut not.ambiguous, so the request is never redelivered. Restore the request to.pending(or another retryable state) in this catch, matching the asynchronous rejection path.🔧 Fix applied.
1 error still open:
.omp/extensions/lib/fm-task-inbox-doorbell.ts:329- The async rejection handler exits immediately whenactiveis false (line 329), which can strand other requests from the same drain after one send failure retires the doorbell. For a runtime withoutomp.on, each send is renamed to.deliveredimmediately (lines 343-345); if the first Promise rejects, it retires the doorbell and setsactive=false, then any later in-flight send rejection skips the retry-state rename and journaling, leaving its.deliveredrecord unrecoverable on the next activation. Keep all sends initiated by the draining generation retryable (or otherwise reconcile them) even after the first failure retires the marker.🔧 Fix applied.
1 error still open:
bin/fm-spawn.sh:1045- The required criterion says activation failures must be journaled durably atstate/<id>.omp-doorbell-failed. The spawn failure cleanup path now deletes that journal (bin/fm-spawn.sh:1045) after the generated extension records the failure (:4741-4746), whenever the owned worktree is clean and returned. A failed launch therefore exits with no.omp-doorbell-failedartifact left for durable inspection; please clarify whether cleanup is intentionally allowed to remove this required diagnostic, or preserve it (or otherwise provide an explicitly durable equivalent).🔧 Fix applied.
✅ Re-checked - no issues remain.
fm-send.shrefused fleet lifecycle from this no-mistakes gate worktree. Provide a normal checkout/worktree (or equivalent fleet-driving authority) a…bash tests/fm-omp-task-inbox-doorbell.test.shTargetedfm-spawn-dispatch-profilechecks for generated-extension readiness gating, bounded handshake failure, and secondmate extension inspectionbash tests/fm-remote-secondmate-lifecycle-e2e.test.sh(remote lifecycle portion reached successful remote spawn checks)FM_SEND_INBOX_LIVE_E2E=1 FM_SEND_INBOX_LIVE_HARNESSES=omp FM_SEND_INBOX_LIVE_TIMEOUT=90 bash tests/fm-send-inbox-doorbell-live-e2e.test.sh(blocked by gate-worktree refusal)✅ **Document** - passed
✅ No issues found.
✅ **Lint** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.