Skip to content

feat(bin): deliver local steers through durable task inboxes - #2856

Merged
kunchenguid merged 23 commits into
mainfrom
fm/fm-send-inbox-doorbell-r1
Aug 23, 2026
Merged

feat(bin): deliver local steers through durable task inboxes#2856
kunchenguid merged 23 commits into
mainfrom
fm/fm-send-inbox-doorbell-r1

Conversation

@kunchenguid

@kunchenguid kunchenguid commented Aug 23, 2026

Copy link
Copy Markdown
Owner

Intent

STAGE 1 (LOCAL steers only) of the captain-adopted inbox+doorbell steering design from data/fm-send-reliability-reframe-s1/report.md, replacing fm-send's typed-payload local data plane; the prior payload-delta/composer-verification approach was DROPPED and must not be resurrected. All requirements captain-approved, including two explicit gate rulings refined during validation: (A) SCOPE EXCLUSION - the pre-existing interaction where a MARKED secondmate parser-native invocation types marker-prefixed bytes (parser sees chat) is APPROVED as out-of-scope pre-existing behavior; follow-up fm-send-secondmate-harness-invocation-r1 owns it; do NOT change the marking contract (no appended-marker variant, no unmarked secondmate invocations) and do not flag it again - the stage-1 boundary comment in fm-send.sh documents this deliberately. (B) EXIT-STATUS RULING (final, refining the earlier both-failed call): on the inbox plane the durable record IS the delivery, so the record's fate alone governs the exit status - exit 0 whenever the record is durably written, including when pending-reply bookkeeping later fails; with the recovery marker stored the watcher reconciles silently, and with the commit and marker BOTH lost fm-send prints a distinct 'reply-tracking-degraded (steer delivered, do not resend)' warning and still exits 0, because any resend-inviting status would make automated callers duplicate a delivered instruction; nonzero is reserved for cases where nothing was delivered (unresolvable target, retired/changed endpoint under the metadata lock, unwritable record) or a decision-close append failed with its manual command attached; this exact contract is deliberate - do not re-flag either direction of it. Core design: (1) bin/fm-task-inbox-lib.sh is the ONE owner of the steering-inbox contract: sequenced durable records at state/.inbox/NNN.msg (temp-write + atomic rename, lock-serialized sequence allocation with bounded wait), the handled/ move as the acknowledgement, never-reissued sequences, BYTE-EXACT bodies (no added newline; raw read-back after the -- line), the constant self-describing doorbell line, and the delivery-attempt ladder (grace-paced; attempts include composer-protected skips and failed rings so nothing retries silently forever; escalate once per message with wake-before-marker ordering; reset on ack; bookkeeping tolerates a concurrently torn-down inbox; FM_TASK_INBOX_GRACE_SECS=90, FM_TASK_INBOX_RING_MAX=3). (2) fm-send local plane: CLI shape, resolution, fail-closed FM_HOME, gate-agent refusal kept; endpoint revalidated under the task metadata lock; durable record appended; ONE constant doorbell rung best-effort (advisory composer check skips only on exact proven pending; ambiguous verdicts still ring - skip-starvation observed live on grok 1.0.5); exit contract per ruling B; the exit-3 ladder and post-send composer proof REMOVED from the local plane. (3) --resolve-key closes the ledger and feeds the captain-hold intake at ENQUEUE time; pending-reply expectations marked delivered at enqueue. (4) Every brief scaffold carries the receive-and-ack paragraph; the relaunch note gains check-your-inbox; teardown removes the inbox. (5) Watcher: one cheap per-task check per poll - re-ring aged unhandled messages on a not-busy pane, wait on busy, escalate exactly once as an ordinary stale: wake with pre-wake revalidation; healthy/empty inboxes stay silent (silent file-move ack). (6) Typed-plane carve-outs UNCHANGED: --key lifecycle keys; harness-native slash and codex $-skill invocations classified on the pre-marker text; explicit backend targets; and the REMOTE secondmate leg, explicitly OUT OF SCOPE (cmd_send and the exit-3 contract crossing ssh stay byte-compatible; remote inbox leg ships separately). (7) KEEP per report section 5: every fail-closed refusal; fm-pending-reply-lib's REPLY direction untouched; fm-control lifecycle keys; spawn-time readiness/trust handling; composer classifier and capture adapters for non-send consumers, demoted to advisory. (8) Tests: portable regressions with real processes (byte-exact round-trips incl. trailing newlines, sequence dedup + idempotent ack, concurrent writers, the attempt ladder, real-watcher re-ring/busy-wait/silence/single-escalation, plane routing, enqueue-time close, endpoint revalidation, torn-down-inbox tolerance, and the ruling-B regression: record durable + both bookkeeping writes lost -> exit 0, distinct degraded warning, no duplicate record); existing suites migrated to the record plane without weakening; plus the env-gated live doorbell guard RUN 2026-08-23: claude, codex, opencode, pi, grok, muse all honored the doorbell end to end (docs/verification/runtime-backends.md, with the dated grok 1.0.5 composer-matrix staleness note; matrix refresh owed separately). Drive-by macOS BSD-wc fix in tests/fm-tool-update-check.test.sh rides along. KNOWN DEFERRED (do not fix in this run): the opt-in cmux live composer guard still expects the payload typed; the captain will route it as follow-up work. (9) Docs: AGENTS.md state entry + section 7 steering rewrite; configuration.md state clause + env knobs; scripts/tmux-backend/architecture/harness-adapters/stuck-crewmate-recovery scoped to the typed plane. PR note: homes pick this up after merge + self-update; landing timing coordinated with the main firstmate; STAGE 1 (local) with the remote leg to follow. One PR; yolo OFF; the captain owns the merge.

What Changed

  • Route ordinary local fm-send messages through byte-exact, sequenced inbox records with a constant best-effort doorbell, while preserving typed delivery for native commands, explicit backend targets, and remote secondmates.
  • Add worker acknowledgement instructions and watcher-managed re-ring, busy-wait, and single-escalation handling for unacknowledged records; remove inbox state during teardown.
  • Close resolved decisions and mark pending replies delivered at enqueue time, with expanded portable and live backend regression coverage and updated operational documentation.

Risk Assessment

⚠️ Medium: No material source defect was substantiated, but this is a broad, concurrency-sensitive replacement of the local steering data plane.

Testing

Reviewed the base-to-target change, ran the focused inbox/watcher and fm-send routing regressions, and manually exercised the delivered record → doorbell → handled acknowledgement flow; all checks passed and the evidence transcript shows the end-user-visible behavior and persisted state.

Evidence: Inbox and doorbell end-to-end transcript

Source: Inbox and doorbell end-to-end transcript

FM inbox+doorbell end-to-end evidence
fm-send exit: 0

Persisted unhandled record:
001.msg

Persisted body (od hex; ends 0a 0a, proving both trailing newlines):
 64 65 70 6c 6f 79 20 72 65 6c 65 61 73 65 20 63
 61 6e 64 69 64 61 74 65 0a 74 68 65 6e 20 72 65
 70 6f 72 74 20 73 74 61 74 75 73 0a 0a 


Terminal channel (constant doorbell only; payload absent):
Firstmate instruction waiting: list <fixture>/home/state/demo.inbox/*.msg and, in numeric order, read and act on each, then mv each handled file to <fixture>/home/state/demo.inbox/handled/.

Acknowledgement action: mv 001.msg handled/
unhandled records after ack: 0
handled records after ack: 1

fm-send stderr:
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
●  WATCHER DOWN - SUPERVISION IS OFF
●  1 task(s) in flight, but no watcher has a fresh beacon (last beat: never, grace 300s).
●  Trust the emitted supervision protocol for this harness; do not use shell & for watcher repair.
●  This is a supervision warning only; the requested message WILL still be sent.
●  repair a missing or failed watcher cycle with the Pi tool fm_watch_arm_pi, or restart Pi with -e <fixture>/home/.pi/extensions/fm-primary-turnend-guard.ts -e <fixture>/home/.pi/extensions/fm-primary-pi-watch.ts if the extensions are not loaded.
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

🔧 **Review** - 1 issue found → auto-fixed (3) ✅
  • 🚨 bin/fm-task-inbox-lib.sh:178 - The doorbell targets one specific record rather than enforcing the inbox's numeric-order contract. Concrete path: 001's initial ring is swallowed, then 002's ring lands; the worker is explicitly told to read and act on 002, while 001 is only re-rung later by the watcher, reversing the steer order without error. Make the shared doorbell instruct the worker to list and process all unhandled records in numeric order (or otherwise prevent a newer record from bypassing the oldest).

🔧 Fix: Preserve inbox ordering with drain-all doorbells
1 error still open:

  • 🚨 bin/fm-watch.sh:303 - The required ladder invariant says failed attempts must ensure “nothing retries silently forever,” but the watcher ignores fm_task_inbox_record_ring failure. If .ring-state cannot be written while the inbox still exists (for example, permissions or filesystem failure), each poll rings again without advancing toward escalation; only the private triage log records it. Handle non-vanished-inbox bookkeeping failure loudly rather than continuing an unbounded retry loop.

🔧 Fix: Surface unwritable inbox ladder bookkeeping
1 error still open:

  • 🚨 bin/fm-watch.sh:326 - The bookkeeping-failure wake checks only that the inbox directory exists, not that the record remains unhandled. Concrete race: the ring prompts the worker to move the record into handled/, .ring-state writing then fails, and this branch emits a stale wake claiming the already-acknowledged record remains unhandled. Revalidate [ -f &#34;$rec&#34; ] immediately before appending the wake and quietly reset/return when it was acknowledged.

🔧 Fix: Silence ladder failures after inbox acknowledgement
✅ Re-checked - no issues remain.

✅ **Test** - passed

✅ No issues found.

  • bash tests/fm-task-inbox.test.sh
  • bash tests/fm-send-inbox.test.sh
  • Manual sandbox execution of bin/fm-send.sh through a stubbed tmux backend, verifying exit 0, byte-exact persisted payload, constant doorbell-only terminal traffic, and acknowledgement via move to handled/
  • git status --short confirmed testing left the worktree clean
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

@greptile-apps

greptile-apps Bot commented Aug 23, 2026

Copy link
Copy Markdown

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains; the prior post-enqueue nonzero statuses now become successful delivered outcomes with explicit non-resend diagnostics, and the acknowledged parser-prefix behavior is unchanged.

Reviews (8): Last reviewed commit: "no-mistakes: apply CI fixes" | Re-trigger Greptile

Comment thread bin/fm-send.sh Outdated
Comment thread bin/fm-send.sh Outdated
@kunchenguid
kunchenguid force-pushed the fm/fm-send-inbox-doorbell-r1 branch from 0b16d56 to 571ed81 Compare August 23, 2026 11:17
@kunchenguid kunchenguid changed the title feat: add durable inbox steering for local tasks feat: steer local tasks through durable inboxes Aug 23, 2026
Comment thread bin/fm-send.sh Outdated
@kunchenguid kunchenguid changed the title feat: steer local tasks through durable inboxes feat(bin): deliver local steers through durable task inboxes Aug 23, 2026
…orbell

Stage 1 (local steers) of the captain-adopted reframe in
data/fm-send-reliability-reframe-s1/report.md: an ordinary fm-send text
steer to a task recorded in this home is appended as a sequenced durable
record under state/<id>.inbox/ and the terminal receives only one constant
self-describing doorbell line, best-effort. The worker acknowledges by
moving the record into handled/; the watcher re-rings an unacknowledged
message on an idle pane and escalates once as an ordinary stale wake.
--resolve-key closes decisions at enqueue time, because the durable
enqueue IS delivery to the task's record. bin/fm-task-inbox-lib.sh owns
the record format, doorbell line, and re-ring ladder.

The typed plane remains for what must reach the terminal itself:
lifecycle keys, harness-native slash and codex $-skill invocations,
explicit backend targets, and the remote secondmate leg (unchanged until
the remote inbox leg ships separately). The composer classifier is
demoted from delivery proof to an advisory ring guard that skips only on
a proven pending verdict.

Verified live against claude, codex, opencode, pi, grok, and muse: each
real worker read its record, acted, and acked with the mv
(docs/verification/runtime-backends.md "Steering-inbox doorbell").
…failed exit out of stage 1

The CI monitor's fix changed the secondmate marking contract for
parser-native invocations (appending the marker after the text) and
softened the both-commit-and-marker-failed branch to exit 0. The merge
authority ruled the marking question out of scope for this stage-1
transport PR (follow-up: fm-send-secondmate-harness-invocation-r1) and
ruled the both-failed case a loud nonzero local failure. Restore both,
keeping the monitor's legitimate migrations and hardening.
Captain-refined ruling on the F2/Greptile finding: the durable inbox
record is what delivers the steer, so pending-reply bookkeeping trouble
after a successful enqueue never exits nonzero - a resend-inviting status
would make automated callers enqueue the delivered instruction again
under a new sequence. With the recovery marker stored the watcher
reconciles silently; with the commit and marker both lost the send
surfaces a distinct reply-tracking-degraded do-not-resend warning and
still exits 0. Nonzero remains only where nothing was delivered (or a
decision close needs its manual command). Regression: record durable +
both bookkeeping writes lost -> exit 0, one record, no duplicate.
@kunchenguid
kunchenguid force-pushed the fm/fm-send-inbox-doorbell-r1 branch from e95ad30 to aeb0d05 Compare August 23, 2026 19:00
@kunchenguid
kunchenguid merged commit e46df1a into main Aug 23, 2026
14 checks passed
@kunchenguid
kunchenguid deleted the fm/fm-send-inbox-doorbell-r1 branch August 23, 2026 19:40
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