- Rewrote the remaining em dashes outside docs/reviews/ (preserved as historical record, matching the alktty precedent): tests/ (313), fuzz/ (34 plus one shell comment), tasks/ (671 plus 12 kept in fences), docs/research + sdd_process + plans (525 plus 25 in fences), docs/architecture/overview.md fence labels (13), README, CHANGELOG, AGENTS.md, fuzz/README.md (176), and the .opencode/agents prompts (110) - Rewrote fenced content too: ASCII tree and module-map label separators, Rust snippet comments, and template placeholders; wire data, JSON/BAST examples, and transcripts survive verbatim - Rewrote 6 assert/expect message literals in tests/ and 2 format strings in fuzz/shared/src; no test data changed - Added Project Convention 18: no em dashes in prose (doc comments, markdown including docs/ and tasks/, agent prompts, commit messages); fence content rule, en-dash clause rule, and the no-spaced-hyphen prohibition follow alktty's convention 15 Follows6b20924(src doc comments) andcd0afe0(docs/architecture). Verification: cargo test (141 pass); cargo clippy --all-targets -D warnings; cargo fmt --check; cargo doc --no-deps; wasm check and clippy for wasm32-unknown-unknown with -D warnings; em dash grep over the repo returns matches only in docs/reviews/
9.3 KiB
id, name, status, depends_on, scope, risk, impact, level, tags
| id | name | status | depends_on | scope | risk | impact | level | tags | ||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| tunnels/corrmiss-forwarded-gate | API: the forwarded-op establisher variant (ForwardedGate + register_tunnel_forwarded_openable_with_establisher) | completed |
|
moderate | medium | component | implementation |
|
Description
Implement the additive establisher variant for the forwarded op's
serving half (plan 003 W1's API item): an assembly-injected
correlation gate that the crate wraps into the alkcall
OpenEstablisher dance, registered via
register_openable_with_establisher: a stale listen token now
returns a typed channel:open_failed before the channel's data plane
is served, instead of succeeding at the call plane and declining
post-open (the phantom-channel shape the review records as
fully silent on the fabric side).
Candidate shape (the plan's W1 design decision to finalize, which this task finalizes it):
/// The injected correlation gate: `Err` at the gate → typed
/// `channel:open_failed` before the channel is born.
pub type ForwardedGate = Arc<
dyn Fn(&TunnelForwardedParams) -> Result<(), TunnelEstablishError> + Send + Sync,
>;
pub fn register_tunnel_forwarded_openable_with_establisher(
core: &ChannelCore,
on_registry: &Arc<OperationRegistry>,
auth: AuthContext,
gate: ForwardedGate,
handler: ForwardedHandler,
) -> Result<(), String>
The design decisions to finalize (and pin in doc comments)
- Gate input vs raw input. The gate receives the PARSED
TunnelForwardedParams; the establisher parses (the params' serde pins,deny_unknown_fields, positivelisten, are the loud gates;parsefailure at the establisher maps throughEstablishmentError::HandlerError→ a typedchannel:open_failed, which is an improvement over the base shape's silent birth-teardown for a malformed ingress; pin this deliberately, do not drift it). The handler still receives the raw input and re-parses as it does today, so keepmake_forwarded_op_handlerunchanged (the double parse is cheap and preserves deny-unknown at both layers; the handler must stay usable by the base shape). - The establishment answer. A gate pass produces no resource
handle (the correlation map is assembly state; the handler needs
nothing it does not re-derive). It returns
Establishment::new(plan)with an empty/marker plan payload (same typed-opaqueChannelPlanposture as ADR-049 amendment 2;()or an empty marker struct is the payload choice to pin). No reply-field contribution (the forwarded op's reply stays the wrapper'schannel_id). - Error mapping. A gate
Err(TunnelEstablishError)maps through the EXISTINGFrom<TunnelEstablishError> for EstablishmentErrortable (src/producer.rs:142-154); a stale token isUnknownResource→details.reason == "unknown_resource"on the wire. Do not mint new variants. The gate SHOULD document that a miss isTunnelEstablishError::UnknownResource(that is the canonical reason the ADR pins), but the crate passes whatever the gate returns through. - No timeout change:
timeout: None(the alkcall 10s default) as the other registrations do; the gate is an in-memory map lookup. - No scope-table change: the same
tunnel_forwarded_spec()/tunnel:forwardedgate runs (the registry runs the ACL before the establishment phase, alkcall's open-op wrapper order; pin the order in a doc comment + a test, see the tests task).
Also in this task (the doc half of W1's API item)
- Update the in-code docs that describe the miss as
assembly-side-only:
make_forwarded_op_handler's correlation-miss-semantics block (src/producer.rs:1014-1020) andregister_tunnel_forwarded_openable's doc (1047-1056); both gain the two-shape posture: the establisher variant is the recommended shape (the miss is a typed failure at the open); the base shape stays for compatibility and keeps the post-open teardown-at-birth semantics (still the residual-race fallback under the establisher variant). - Re-export the new surface from
lib.rs(convention 16). - The base
register_tunnel_forwarded_openablestays byte-for-byte compatible (additive-only, per plan decision 4).
NOT in this task: the sibling-repo adoptions (alkssh W2 / alksocks W3, pointer tasks exist) and the gate-miss tests (the dedicated tests task; but do not land the API without landing its tests task after).
Acceptance Criteria
ForwardedGatetype +register_tunnel_forwarded_openable_with_establisherlanded insrc/producer.rs, re-exported fromlib.rs- The establisher parse-failure → typed
channel:open_failedmapping is deliberate + documented (design decision 1) - Gate errors pass through the existing
From<TunnelEstablishError>table; no new error variants make_forwarded_op_handler/ the base registration are unchanged in behavior (byte-for-byte compat)- The in-code docs updated: the two-shape posture (establisher = recommended; base = compatibility + residual-race fallback) replaces the assembly-side-only framing
cargo testgreen (existing suite untouched); clippy--all-targets -- -D warningsclean (native + wasm32);cargo fmt --checkclean; default crate stays wasm-clean--all-featurestest run green (thelocalfeature surface is untouched)
References
- docs/plans/003-correlation-miss-resolve.md §W1 (the API bullet; the decisions this finalizes)
- docs/architecture/decisions/008-listen-metadata-forwarded-op.md §3
- the ADR this task's predecessor lands (Amendment 2 or successor; cite whichever the ADR task landed)
- src/producer.rs:982-1082 (the base serving shape:
ForwardedHandler,make_forwarded_op_handler,register_tunnel_forwarded_openable); 142-154 (theFrom<TunnelEstablishError>table); 641-673 (thebindfirst_establisher, the wrap-into-OpenEstablisherclosure pattern to mirror, including theBox::pinshape) - alkcall 0.10.0
ChannelCore::register_openable_with_establisher(src/channels/operations.rs:761; the establisher runs AFTER allocation, BEFORE the reply; failure → typedchannel:open_failed, no phantom channel;timeout: None= the 10s default) - AGENTS.md conventions 11 (the ADR-049 establishment surface is load-bearing; typed-opaque plans), 8 (teardown-at-birth posture), 16 (re-export posture)
Notes
ForwardedGate(sync closure over the parsedTunnelForwardedParams) wraps into a privatemake_forwarded_gate_establisher→register_openable_with_establisher(spec, Some(...), make_forwarded_op_handler(handler), registry, auth, None), so the base handler is reused byte-for-byte and the base registration stays untouched (additive-only).- The design decisions were finalized exactly as the task sketched:
(1) gate receives the PARSED params; establisher parse failure maps
TunnelEstablishError::HandlerError→ typedchannel:open_failed(pinned in the establisher doc, an improvement over the base shape's silent birth-teardown for malformed ingress); the handler re-parses the raw input (deny-unknown at both layers, handler unchanged); (2) gate pass →Establishment::new(plan)with an EMPTY MARKER plan payload, pinned as a new pubForwardedPlanunit struct (chosen over(): self-documenting, downcast-able by assemblies that want to assert the gate ran; the same typed-opaque posture as ADR-049 amendment 2); no reply-field contribution; (3) gate errors pass through the existingFrom<TunnelEstablishError>table, no new variants; the gate doc pinsUnknownResourceas the canonical stale-token reason; (4)timeout: None(10s default); (5) no scope-table change; the wrapper order (schema → ACL → allocate → establisher → handler) is pinned in the establisher doc; the order-pinning test lands with the tests task. - Doc updates:
make_forwarded_op_handler's correlation-miss block +register_tunnel_forwarded_openable's doc now carry the two-shape posture. Two adjacent doc drifts were synced in the same wave per ADR-008 Amendment 2 ("syncs with the API task's in-code doc updates"):wire.md§The Forwarded Op's "no establisher" prose line, andtunnel_forwarded_spec()'s "No establisher" doc block insrc/params.rs.
Summary
Landed the forwarded-op establisher variant per ADR-008 Amendment 2:
ForwardedGate, ForwardedPlan, make_forwarded_gate_establisher
(private), and register_tunnel_forwarded_openable_with_establisher
in src/producer.rs, re-exported from lib.rs. A gate Err (and an
establisher parse failure) now yields a typed channel:open_failed
before the channel is born; the base shape stays byte-for-byte
compatible as the compatibility + residual-race-fallback posture.
In-code docs updated (two-shape posture) plus the wire.md/params.rs
doc sync the amendment rides on this task. Verification: cargo test
green (existing suite untouched), clippy --all-targets -D warnings
clean native + wasm32, cargo fmt --check clean, cargo doc --no-deps clean (0 warnings), --all-features test run green.
Follow-ups: the dedicated gate-miss tests task
(tunnels/corrmiss-gate-tests), then the sibling-repo adoptions
(corrmiss-alkssh-adopt, corrmiss-alksocks-adopt).