diff --git a/docs/plans/003-correlation-miss-resolve.md b/docs/plans/003-correlation-miss-resolve.md new file mode 100644 index 0000000..cbeca7c --- /dev/null +++ b/docs/plans/003-correlation-miss-resolve.md @@ -0,0 +1,138 @@ +# Plan — resolve review 003: the correlation-miss arrival class + +Status: planned (2026-11-03). Resolution of +`docs/reviews/003-correlation-miss-arrival-class-alkssh-input.md`, +including the verification caveats found in this session (see §Verified +findings — they extend the review, do not contradict it). + +## Verified findings (the ground the plan stands on) + +The review's claims held under code verification. Two additions sharpen +the resolution scope: + +- **A third vocabulary variant.** alksocks (`src/composed/mod.rs:497`) + logs the same miss class as prose at `warn` ("forwarded open with no + waiting session"). So the family names the stale-token miss three + ways: alkssh `correlation-miss` (debug), alksocks prose (warn), + alktunnels `handler_error`-class generic arm (warn, call-plane + failures only). +- **The in-session miss is fully silent on the fabric side.** The + forwarded op is served establisher-less everywhere (alkssh, + alksocks, and this crate's own built-in shape), so an in-session + correlation miss (route removed while the fabric connection lives) + succeeds at the call plane, declines post-open in the assembly, and + the producer-side pump EOFs with no log at all. The generic arm + (`src/producer.rs:838-854`) only fires for the + connection-gone/NOT_FOUND/ACL-refused subclass. +- All three are the phantom-channel shape — the ADR-049 posture + exception ("a refused open is a typed call error, never a phantom + channel") that the forwarded op's serving side currently rides. + +## Decisions (settled this session, pending ADR ink) + +1. **Route the miss through an establisher** — the serving side gains + an establisher variant so a no-route token returns a typed + `channel:open_failed` before the channel is born. The residual + post-open decline (a token missing between establisher pass and + handler run — a narrow race) keeps teardown-at-birth as the + fallback shape. +2. **Reuse `unknown_resource`** as the typed reason for a stale/missing + token. The waiting session *is* a resource that is gone; the wire + vocabulary (`wire.md` §The Open Ops, `details.reason ∈`) + already pins it. No wire-vocabulary extension, no reason-class ADR + (framing option 2's "new class" question is moot). +3. **Adopt `correlation-miss` as the canonical event name** for the + residual post-open decline class (alkssh's shipped vocabulary wins — + it is published); alksocks' prose variant retires. Level posture: + assembly-side declines at `debug`; the producer-side generic arm + stays `warn` (it now carries a typed reason instead of a prose one). +4. **Scope of the change is additive-only** in this crate: a new + registration surface for the forwarded op. The base + `register_tunnel_forwarded_openable` shape stays (compatibility + + assemblies that prefer the post-open shape); the establisher + variant is the recommended shape and new assemblies take it by + default posture. + +## Work items + +### W1 — alktunnels: ADR + additive API (this repo; ~1 session) + +- ADR-008 amendment (or a small successor ADR): the forwarded op's + serving side gains an establishment phase; misses are typed failures + (decision 1-4 above), the residual post-open decline keeps the + `correlation-miss` name. Also record the tri-vocabulary history the + decision retires (review 003 + this plan). +- API: an establisher variant for the forwarded-op serving half. The + protocol cannot hold the assembly's correlation map, so the + establisher's check is injected. Candidate shape (a W1 design + decision to finalize): a small assembly gate closure + (`(TunnelForwardedParams) -> Result<(), TunnelEstablishError>`) that + the crate wraps into the alkcall `OpenEstablisher` dance, plus + `register_tunnel_forwarded_openable_with_establisher(core, + on_registry, auth, gate, handler)`. Assemblies must not hand-roll + the alkcall establisher plumbing. +- Tests: miss at the gate → `channel:open_failed` with reason + `unknown_resource`, no channel allocated; gate-pass → existing + serving flow unchanged; the residual race shape (gate passes, route + gone at handler time) still reaps cleanly; scope gate unchanged + (`tunnel:forwarded`, runs before the establisher — pin the order). +- Update the in-code docs that describe the miss as assembly-side-only + (`make_forwarded_op_handler` doc, `Register…forwarded` doc). + +### W2 — alkssh: adopt the establisher (dedicated session; alkssh repo) + +- Move `pump_forwarded_arrival`'s route lookup into the new gate + (the `ListenRouter` lookup becomes the establisher's check); the + residual handler keeps `decline_arrival("correlation-miss", …)` for + the narrow post-open race only. +- Verify the fabric-side silence fix: a stale-token arrival now fails + the open (alkssh-side typed error; producer-side generic arm fires + with a parseable reason) — the race window no longer produces a + zero-signal pump. +- Fold alkssh's em-dash doc cleanup into its release for this change + ("cleanup rides the code-update release" — the user-owned task, out + of this plan). + +### W3 — alksocks: adopt the establisher (dedicated session; alksocks repo) + +- Move `CorrelationRegistry` lookup into the new gate; retire the + prose warn (the miss becomes a typed failure at the open; residual + logging aligns to `correlation-miss` at `debug`). +- Fold alksocks' em-dash doc cleanup into its release for this change + (user-owned, out of this plan). + +## Ordering + +W1 → {W2, W3} (the dependent waves can run in parallel; each is its +own session). Em-dash cleanup rides each repo's release for its wave +and is otherwise out of scope here. + +## Acceptance criteria (plan level) + +- [ ] W1: ADR landed; `…_with_establisher` surface landed; tests green + (`cargo test`, clippy `--all-targets -D warnings`, fmt, wasm + check; `--all-features` as far as features exist today); review + 003 status updated to resolved (W1 absorbed; W2/W3 pointed). +- [ ] W2: alkssh serves the gate shape; stale-token arrival produces a + typed call-plane failure; residual class named + `correlation-miss` at `debug`; alkssh release includes its doc + cleanup. +- [ ] W3: alksocks serves the gate shape; prose variant retired; + residual logging aligned; release includes its doc cleanup. +- [ ] The family names the class one way in its event surface; + composed `-R` traces read coherently end to end. + +## References + +- `docs/reviews/003-correlation-miss-arrival-class-alkssh-input.md` + (the ask; this plan resolves it) +- `docs/architecture/decisions/008-listen-metadata-forwarded-op.md` §3 + (the serving shape this amends); alkcall ADR-049 (the establisher + posture being extended); `docs/architecture/wire.md` §The Open Ops + (the pinned `details.reason` vocabulary — unextended by this plan) +- `src/producer.rs` (the generic arm; the forwarded-op serving half); + alkssh `src/tunnel_map.rs` (`pump_forwarded_arrival`, + `ListenRouter`); alksocks `src/composed/mod.rs` + (`make_forwarded_handler`, `CorrelationRegistry`) +- alkssh `docs/plans/implementation.md` ("Still booked with their + owners" — the booking this filing unblocks) \ No newline at end of file