docs(adr): ADR-008 Amendment 2 — the forwarded op's serving-side establishment phase + the correlation-miss vocabulary

- The serving side of the forwarded op gains an establisher variant:
  an assembly-provided gate closure (over TunnelForwardedParams) the
  crate wraps into the alkcall OpenEstablisher dance via
  register_openable_with_establisher; a stale/missing listen token
  fails the open as a typed channel:open_failed with
  details.reason = unknown_resource — never a phantom channel
  (ADR-049's posture extended to the forwarded op's serving side)
- unknown_resource reused as-is; the wire vocabulary is unextended
  and wire.md is untouched (the empty wire delta stated explicitly)
- Gate order pinned: the tunnel:forwarded ACL precedes the
  establisher (alkcall's open-op wrapper order)
- The residual post-open race keeps teardown-at-birth; canonical
  event name correlation-miss at debug (assembly-side convention);
  the producer-side generic arm stays warn with the typed reason
- Additive-only surface: the base register_tunnel_forwarded_openable
  stays; the establisher variant is the recommended default posture
- Tri-vocabulary history recorded (alkssh correlation-miss/debug,
  alksocks prose/warn, alktunnels handler_error-class generic
  arm/warn + the fabric-side silence) with sources
- The amended §3 sentence is marked inline, not silently replaced

Resolves review 003 via docs/plans/003-correlation-miss-resolve.md
§Decisions 1-4 (W1's ADR bullet). Doc-only; review 003's status block
and wire.md belong to other tasks and are untouched.

Verification: md renders coherently (read back); alkcall 0.10.1
EstablishmentError/reason() mapping and wrapper order verified in the
registry source before pinning
This commit is contained in:
glm-5.3-flash committed 2026-11-03 08:13:50 +00:00
1 parent aa3db39e3e
commit 7d2b6d11f0
2 files changed
+168 -13

No files matched your search

@@ -3,7 +3,9 @@
## Status
Accepted (2026-09-16); amended 2026-09-18 (the correlation token —
`listen` reply field + forwarded-params key, see Amendment 1)
`listen` reply field + forwarded-params key, see Amendment 1); amended
2026-11-03 (the forwarded op's serving-side establishment phase + the
correlation-miss vocabulary, see Amendment 2)
## Context
@@ -185,9 +187,12 @@ the listen-opener** — accept-as-open, the `forwarded-tcpip` shape:
exposing a listener and dialing egress are different capabilities.
- The opener-side serving shape: `register_openable_with_establisher`
as-is (no establisher — the forwarded channel needs no
establishment; its data plane starts immediately), ACL-gated by the
scope, handler = correlate the open (via `listen`, the correlation
token) to the waiting
establishment; its data plane starts immediately — **amended by
Amendment 2**: the served shape gains an establisher variant, the
injected correlation gate; this sentence records the pre-amendment
posture, which stays a valid shape — see Amendment 2), ACL-gated by
the scope, handler = correlate the open (via `listen`, the
correlation token) to the waiting
session, accept the `BiStream`, pump against the local BIND client's
halves. The correlation registry is assembly-side bookkeeping —
the protocol provides the op and the params, not the
@@ -338,4 +343,114 @@ establisher's reply surface (§2 gains `listen`).
delta vs the pre-amendment ADR is exactly: the reply gains the
optional `listen` field, the forwarded params rename `channel` →
`listen` with token semantics. Nothing else shipped against either
(no consumer exists), so the change is pre-consumer and legal.
(no consumer exists), so the change is pre-consumer and legal.
## Amendment 2 (the serving-side establishment phase + the correlation-miss vocabulary, 2026-11-03)
Resolves review 003 (`docs/reviews/003-correlation-miss-arrival-class-alkssh-input.md`)
via `docs/plans/003-correlation-miss-resolve.md` §Decisions 1–4. The
decision stands — bind-first establisher, accept-as-open, the
`bound`/`listen` reply fields, the forwarded op, `tunnel:forwarded`.
What changes: **the serving side of the forwarded op (§3) gains an
establishment phase** — §3's sentence "no establisher — the forwarded
channel needs no establishment; its data plane starts immediately"
(marked inline above) is the text this amendment amends.
### The miss class this closes (the tri-vocabulary history)
A forwarded open whose `listen` token no longer routes — the
stale/missing-token arrival class — is currently served
establisher-less everywhere (alkssh's door, alksocks' composed BIND,
and this crate's built-in shape). That is the one posture exception
to ADR-049's "a refused open is a typed call error, never a phantom
channel": the open succeeds at the call plane, declines post-open in
the assembly, and the producer-side pump EOFs with no signal. The
family has named the class three ways:
- **alkssh** — `correlation-miss` at `debug`:
`observe::decline_arrival(row, "correlation-miss", listen)` +
teardown-at-birth (`src/tunnel_map.rs`; tokens removed at session
teardown by the shared per-fabric-leg `ListenRouter`).
- **alksocks** — prose at `warn`: "forwarded open with no waiting
session" (`src/composed/mod.rs:497`).
- **alktunnels** — the `handler_error`-class generic arm at `warn`
(the bind-first accept loop's failed-open branch,
`src/producer.rs:838-854`), which fires only for the
connection-gone/`NOT_FOUND`/ACL-refused subclass — an in-session
miss (route removed while the fabric connection lives) succeeds at
the call plane and logs nothing at all.
Amendment 2 retires the prose variant and the fabric-side silence.
### The decisions
1. **The serving side gains an establisher variant — the gate is
injected.** The protocol crate cannot hold the assembly's
correlation map, so the establisher's check is an
assembly-provided gate closure over the forwarded params
(`TunnelForwardedParams`); the crate wraps that closure into the
alkcall `OpenEstablisher` dance and registers via
`register_openable_with_establisher` (alkcall 0.10.x, verified
present). Assemblies must NOT hand-roll the alkcall establisher
plumbing — the crate-owned registration surface is the only
supported path. The gate is a lookup, not a dial: establishment
stays fast, bounded by the usual establisher rules only.
2. **A stale/missing token fails the open as a typed
`channel:open_failed`.** The crate maps the gate's failure to
alkcall's `EstablishmentError::UnknownResource`, so the reply is
`channel:open_failed` with `details.reason = "unknown_resource"` —
no phantom channel is ever born (ADR-049's posture, extended here
to the forwarded op's serving side). The waiting session IS a
resource that is gone; **`unknown_resource` is reused as-is and
the wire vocabulary is NOT extended**: `wire.md`'s open-op sections
(the pinned `details.reason ∈ dial_failed / unknown_resource /
resource_shortage / handler_error / timeout` list and its
§The Forwarded Op) are untouched by this amendment — there is no
reason-class ADR, no new wire value. (The one serving-shape prose
line in `wire.md` — "no establisher" under §The Forwarded Op —
syncs with the API task's in-code doc updates, plan 003 W1; this
ADR does not touch `wire.md`.)
3. **Gate order is pinned: the scope gate precedes the establisher.**
The registry runs `tunnel:forwarded` ACL before the open op's
establishment phase (alkcall's open-op wrapper order: registry
gates — schema, then ACL — then the wrapper's allocate →
establisher → handler). A scope-less producer is `FORBIDDEN`
before any gate lookup; the gate never sees an unauthorized open.
4. **The residual post-open race keeps teardown-at-birth, named
`correlation-miss` at `debug`.** A token that vanishes between
establisher pass and handler run — the narrow race the gate cannot
close — keeps §3's opener-half posture, unchanged: the assembly
handler declines, its early return reaps the channel
teardown-at-birth (R-02). Its event name is **`correlation-miss`**
(alkssh's shipped vocabulary wins — it is published; alksocks'
prose variant retires), at `debug` **assembly-side** — the
protocol crate's built-in handler holds no map, so this is the
assemblies' convention this ADR records, not code this crate runs.
The producer-side generic arm (the accept loop's failed-open
branch) stays at `warn`, now carrying the typed reason from the
open failure instead of the prose one.
### Additive-only surface
The base `register_tunnel_forwarded_openable` (establisher-less, the
post-open shape) stays for compatibility; the establisher variant
(`register_tunnel_forwarded_openable_with_establisher`) is the
recommended posture, and new assemblies take it by default. Both ride
the same op id, params, scope, and reply surface — the wire delta of
this amendment is empty.
### References
- `docs/reviews/003-correlation-miss-arrival-class-alkssh-input.md`
(the ask; situation + framing this amendment resolves)
- `docs/plans/003-correlation-miss-resolve.md` §Decisions, §Verified
findings (the tri-vocabulary survey; W1 is this amendment + the API
task), §Ordering (W2 alkssh / W3 alksocks adoption waves)
- alkcall ADR-049 (the establishment phase and typed-error posture
extended here); alkcall 0.10.x
`register_openable_with_establisher` and
`EstablishmentError::UnknownResource → unknown_resource` (verified)
- alkssh `src/tunnel_map.rs` (`ListenRouter`, `decline_arrival`);
alksocks `src/composed/mod.rs:497`; alktunnels
`src/producer.rs:838-854` — the three miss sites this amendment
converges (source for §The miss class above)
+48 -8
View File
@@ -1,7 +1,7 @@
---
id: tunnels/corrmiss-adr
name: "ADR: the forwarded op's establishment phase + the correlation-miss vocabulary (resolves review 003 — decisions)"
status: pending
status: completed
depends_on: []
scope: narrow
risk: medium
@@ -77,18 +77,18 @@ format precedent).
## Acceptance Criteria
- [ ] Amendment 2 to ADR-008 (or the successor ADR) lands under
- [x] Amendment 2 to ADR-008 (or the successor ADR) lands under
`docs/architecture/decisions/`, covering all four §Decisions
and the tri-vocabulary history
- [ ] The amended/superseded §3 sentence is marked, not silently
- [x] The amended/superseded §3 sentence is marked, not silently
replaced
- [ ] Wire-vocabulary non-extension is explicit (`unknown_resource`
- [x] Wire-vocabulary non-extension is explicit (`unknown_resource`
reused; `wire.md` §The Open Ops untouched — state that)
- [ ] Status block of
- [x] Status block of
`docs/reviews/003-correlation-miss-arrival-class-alkssh-input.md`
is NOT updated here (the doc-sync task owns it — this task is
the ADR itself)
- [ ] No code changes; doc build clean (`cargo doc --no-deps` clean is
- [x] No code changes; doc build clean (`cargo doc --no-deps` clean is
NOT required for pure docs/ edits — verify md renders coherently
instead)
@@ -108,8 +108,48 @@ format precedent).
## Notes
> To be filled by implementation agent
- Shape chosen: **Amendment 2 to ADR-008** (the recommendation held —
it did not balloon; §4/§5 needed no restating).
- Before writing, verified the ground against the shipped sources:
alkcall 0.10.1 `EstablishmentError` (variants
`DialFailed`/`UnknownResource`/`ResourceShortage`/`HandlerError`;
`.reason()` maps `UnknownResource → "unknown_resource"`,
operations.rs:592-596) and `register_openable_with_establisher`
(operations.rs:761); the open-op wrapper order
(`policy.check_open` — the ACL — then allocate → establisher →
handler, operations.rs:1085-1103) pins the "scope gate precedes the
establisher" invariant. Cargo.toml pins `alkcall = "0.10.1"`, so
the ADR says "0.10.x, verified present".
- The amended §3 sentence (lines 187-196, the serving-shape bullet)
is marked inline with "**amended by Amendment 2**" — the
pre-amendment posture is recorded, not replaced (the Amendment 1
style).
- Deliberately NOT touched: `wire.md` (the amendment states its
`details.reason` vocabulary is unextended; the one serving-shape
prose line — "no establisher" under §The Forwarded Op — syncs with
the API task per the amendment's note) and review 003's status
block (doc-sync task owns it).
## Summary
> To be filled on completion
Amendment 2 landed at
`docs/architecture/decisions/008-listen-metadata-forwarded-op.md`
(2026-11-03): the forwarded op's serving side gains an establishment
phase — an assembly-provided gate closure (over
`TunnelForwardedParams`) the crate wraps into the alkcall
`OpenEstablisher` dance via `register_openable_with_establisher`,
with failures mapped to `EstablishmentError::UnknownResource` so a
stale/missing `listen` token is a typed
`channel:open_failed`/`unknown_resource` before any channel is born.
`unknown_resource` is reused as-is; the wire vocabulary is unextended
and `wire.md` is untouched. Gate order pinned (`tunnel:forwarded` ACL
precedes the establisher); the residual post-open race keeps
teardown-at-birth with the canonical assembly-side name
`correlation-miss` at `debug` (producer-side generic arm stays `warn`
with the typed reason); the base
`register_tunnel_forwarded_openable` stays, the establisher variant
is the recommended default posture; the wire delta of the amendment
is empty. The tri-vocabulary history (alkssh `correlation-miss`
debug, alksocks prose warn, alktunnels `handler_error`-class generic
warn + the fabric-side silence) is recorded with sources. Doc-only;
no code changes.