CB-307: lock push-loop injection to dedicated status-gated loop (mechanism b)
Chose a small dedicated scheduled loop over AgentControl.send guarded by an injectable status check, instead of reusing the worker Injector (which couples to WorkerPresence/StatusPoller). Isolated + unit-testable via injected clock. Records live ground truth: this primary resolves to term_656c8cc03e1f0b1 (w2:pY) — confirms the primary runs in a herdr pane so the push path is exercisable.
This commit is contained in:
@@ -78,9 +78,15 @@ A `ReplyPushLoop` component, notified at the single no-waiter call site
|
||||
- **Ack = drain.** The primary draining (`drainReplies` = peek + ack) is the acknowledgement.
|
||||
The loop's **stop condition is `inbox.peek(target).isEmpty()`** — the reply is gone from the
|
||||
inbox because it was acked. No new `bridge_ack` tool needed for v1 (see Increment 3).
|
||||
- **Status-gated injection.** Never inject mid-turn. Reuse the `Injector`/`StatusPoller`
|
||||
discipline: deliver only when the primary's terminal samples **injectable** (IDLE/BLOCKED),
|
||||
one message per turn. **Readiness caveat (below).**
|
||||
- **Status-gated injection (mechanism (b), chosen).** A dedicated lightweight scheduled loop,
|
||||
**not** the worker `Injector`. It injects via `AgentControl.send(primaryTerminal, nudge)`
|
||||
(the same herdr `agent.send` = `pane send-text` + submit that delivers to workers) only when
|
||||
`AgentControl.status(primaryTerminal).injectable()` (IDLE/BLOCKED) — never mid-turn. This keeps
|
||||
the primary path fully isolated from `WorkerPresence`/`StatusPoller` (which are worker-scoped),
|
||||
and makes it unit-testable with a fake `AgentControl` + an injected clock (per the CB-306
|
||||
`LongSupplier` clock + `Runnable` sleeper seam). Rejected (a) reuse-the-Injector: it would force
|
||||
the primary terminal into the worker poller set and couple to worker-presence semantics — more
|
||||
integration surface, harder to test, no real gain for a bounded reminder.
|
||||
- **Bounded reminder / backoff.** While `peek(target)` stays non-empty, re-inject on a
|
||||
backoff schedule up to a cap (N reminders or a max duration; config
|
||||
`primary.push_reminders` / `primary.push_backoff_ms`). After the cap, **stop reminding** —
|
||||
@@ -102,16 +108,15 @@ in v1; the stop-on-empty loop is sufficient.
|
||||
and needs no new env var or argument (identity stays connection-derived, per the existing
|
||||
`BridgeMcp` invariant).
|
||||
|
||||
2. **Readiness-gate mismatch.** The existing `Injector` gates delivery on
|
||||
2. **Readiness-gate mismatch → dedicated loop.** The existing `Injector` gates delivery on
|
||||
`ready.test(target)` = `WorkerPresence` (the *worker's* MCP connected). The primary is not
|
||||
in `WorkerPresence`. So the primary push path uses a **different liveness signal**: the
|
||||
primary is provably MCP-connected at the moment it calls us (that's how we learned its
|
||||
terminal), and we still status-gate on its terminal sampling **injectable** via the poller.
|
||||
Concretely, the primary push path either (a) uses a dedicated `Injector` instance whose
|
||||
`ready` predicate is "primary terminal is known" (always true once registered), or
|
||||
(b) a lighter direct `AgentControl.send` guarded by a `StatusPoller` injectable sample.
|
||||
Decision: **(a)** — reuse the `Injector` queue/one-per-turn/backoff machinery with a
|
||||
primary-appropriate `ready` predicate, rather than reimplement gating.
|
||||
in `WorkerPresence`, so reusing `Injector` would mean forcing the primary terminal into the
|
||||
worker `StatusPoller` set and swapping the `ready` predicate — extra integration surface with
|
||||
worker-scoped machinery. Decision: **mechanism (b)** — a small dedicated scheduled loop that
|
||||
calls `AgentControl.status(primaryTerminal).injectable()` then `AgentControl.send(...)`, with
|
||||
an injected clock. Isolated from worker presence, trivially unit-testable, sufficient for a
|
||||
bounded reminder. (Verified live: this primary resolves to `term_656c8cc03e1f0b1`, pane
|
||||
`w2:pY` — the primary genuinely runs in a herdr pane on this host, so the path is exercisable.)
|
||||
|
||||
## Boundary note
|
||||
|
||||
|
||||
Reference in New Issue
Block a user