feat(review 008 Unit 2): flavor-form open-op ids in discovery (U-1, ADR-047 amendment 3)

Flavor-form open op ids (channels/tunnel/direct, channels/tunnel/
forwarded — alktunnels ADR-007/008) now survive discovery + the
op/register announce path: the marker reconstructs, so a hub consuming
through discovery wraps the op with relay machinery instead of the
silent plain-forwarding-stub failure.

Per the review-008 plan's combined (b)+(c) shape:

- derive_alpn_from_op_name generalizes from strip-/sub|/pub to
  strip-last-segment (rsplit_once) — a strict superset: standard
  two-suffix shapes derive identically, multi-segment ALPNs survive
  the same way, and the flavor form derives. The boolean marker
  remains the gate (consulted only for marked ops in
  rebuild_spec_for) — a plain op named channels/tty/query is
  unaffected.
- spec_to_json_pub emits channel_open_alpn (the explicit string)
  beside the boolean when the op name is not the standard
  channels/<seg>/(sub|pub) shape; standard shapes stay byte-identical
  to the pre-amendment payload. The advertised operation_spec_schema
  documents the optional ["string","null"] property.
- rebuild_spec_for prefers the explicit string, else boolean →
  generalized derivation. Both wire consumers (from_call import,
  op/register announce) parse through the same parser, so one change
  covers both. A channel_open_alpn without the boolean never marks an
  op (the boolean is the dispatch hint).

Residual ambiguity pinned in the ADR: an ALPN segment colliding with
a flavor name (channels/x/direct → alk/x vs alk/x/direct) is
undecidable from the name alone; the explicit string is the
disambiguator.

Tests (review gates 1 + 3; gate 2 lands with the Unit 3 relay):
- channels/tunnel/direct reconstructs WITH channel_open = alk/tunnel,
  both via the explicit string and via the boolean alone (the
  deployment-skew case: old producer, new consumer)
- standard shapes (channels/tty/sub, multi-segment
  channels/custom/proto/sub) round-trip byte-stable — no new key
- explicit string overrides a colliding derivation; string without
  boolean is ignored
- op/register announced flavor-form spec round-trips with the marker
- derivation unit tests: flavor form, non-op-type suffix not
  special-cased, prior shapes unchanged

Docs: ADR-047 amendment 3 — the convention sentence (mirroring
alktunnels ADR-002 Amendment 1: flavors are new op ids, additive, the
.../sub op is never reused), the wire shape, the preference order,
the residual-ambiguity note, one-way-door timing.

Verification: cargo test 649 passed (12 new); clippy -D warnings
clean; cargo fmt --check clean; cargo doc --no-deps clean.
This commit is contained in:
glm-5.3-flash committed 2026-09-16 10:34:45 +00:00
1 parent 82ddddf986
commit 620180d615
4 files changed
+442 -25

No files matched your search

@@ -11,7 +11,94 @@ base registry installed as the session's dispatch registry**, not the
connection overlay — see "Amendment (§4 mechanism, 2026-09-03)" below;
§6 amended 2026-09-06 — the static half of discovery gains an additive
per-op `description` on the listing, the dynamic half stays deferred
— see "Amendment (§6 listing enrichment, 2026-09-06)" below)
— see "Amendment (§6 listing enrichment, 2026-09-06)" below;
amendment 3 (2026-09-16, review 008 U-1) — the op-name convention
extends to the flavor form and discovery carries an explicit
`channel_open_alpn` for non-derivable names — see "Amendment 3" below)
## Amendment 3 (flavor-form open-op ids + explicit `channel_open_alpn`, 2026-09-16 — review 008 U-1)
alktunnels' graduation (ADR-002 Amendment 1 at
`/workspace/@alkdev/alktunnels/docs/architecture/decisions/002-alpn-strategy.md`)
pins two NEW `Sub`-typed open ops on the existing `alk/tunnel` ALPN
with flavor-form op ids (`channels/tunnel/direct`,
`channels/tunnel/forwarded`) — the ssh
`direct-tcpip`/`forwarded-tcpip` shape: one ALPN, several channel
flavors, one open op per flavor. §1 pinned the open-op naming to
exactly two shapes per ALPN (`channels/<alpn>/sub`,
`channels/<alpn>/pub`), and Gap F's marker reconstruction derived the
ALPN by stripping exactly those two suffixes — so a flavor-form op id
failed the derivation and a hub consuming through discovery rebuilt
the spec WITHOUT the marker (the silent plain-forwarding-stub failure,
the worst mode for a relay). Runtime was never blocked (registration
sets the ALPN explicitly; `open_channel` takes it as an argument);
discovery + hub relay are the re-produce path that must survive.
**The convention sentence (mirroring alktunnels ADR-002 Amendment 1):**
`channels/<alpn>/<flavor>` is a valid open-op name where `<flavor>`
is a bare path segment (no `/`), `Sub`-typed, served through the same
establishment wrapper and relay path as `…/sub`. New flavors are new
op ids — additive; the `…/sub` op is never reused for a different
meaning. `OperationType` (`Sub`/`Pub`) continues to carry the
direction; the flavor is the channel-type discriminator the producing
crate owns (per-ALPN params semantics, per alktunnels ADR-001
Amendment 1).
**The wire shape (Gap F refinement):** the boolean marker's
round-trip is derivable only for the standard shapes. The rule:
- `spec_to_json_pub` keeps emitting `"channel_open": true` for every
marked op (unchanged). When the op name is NOT the standard
`channels/<segment>/(sub|pub)` shape, it ALSO emits
`"channel_open_alpn": "<alpn>"` — the explicit string rides beside
the boolean. Standard shapes stay byte-identical to the
pre-amendment payload (no new key).
- `rebuild_spec_for` prefers the explicit string when present; else
(the boolean alone — the deployment-skew case of an old producer)
the derivation generalizes from "strip `/sub`|`/pub`" to "strip the
LAST path segment." The boolean marker remains the gate: the
derivation is consulted only for marked ops, so a plain op named
`channels/tty/query` is unaffected, and a `channel_open_alpn`
string without the boolean never marks an op.
- `services/schema`'s advertised `operation_spec_schema` documents
`channel_open_alpn` as an optional `["string", "null"]` property
(schema-type widening, additive — old consumers ignore unknown
properties).
- Both wire consumers of the shape — the `from_call` import and the
`op/register` announced-spec path — parse through the same
`rebuild_spec_for`, so one parser change covers both.
**Residual ambiguity, pinned:** an op name whose ALPN segment itself
ends in a flavor-like name (`channels/x/direct` where the intended
ALPN is `alk/x/direct`-shaped) is undecidable from the name alone —
strip-last reads `alk/x`, and the true ALPN is unknowable from the
name. The explicit `channel_open_alpn` string is the disambiguator:
producing crates with non-`alk/*`-derivable names set it; the
derivation is the fallback for derivable shapes. Old producers
(alkcall ≤ 0.7.1) cannot serve flavor-form marked ops discoverably
until upgraded — the review's one-way-door timing note.
Door type: the flavor-form names and the additive
`channel_open_alpn` key are wire-stable from the first consumer
(alksocks composes against them); the boolean's meaning for
standard-shape ops is unchanged (the pre-amendment byte-stability is
pinned by test). The `op_name_is_standard_channel_open_shape` helper
and the strip-last derivation are two-way-door implementation details
within the one-way wire shape.
Implemented surface (alkcall 0.7.2): the generalized
`derive_alpn_from_op_name` (strip-last), the explicit-field emission
(`spec_to_json_pub` + the advertised schema property), and the
preference order in `rebuild_spec_for`. Verification gates landed as
tests: `channels/tunnel/direct` reconstructs WITH
`channel_open = alk/tunnel` (gate 1, both the explicit-string and the
skew-case paths); standard boolean ops (`channels/tty/sub`,
multi-segment `channels/custom/proto/sub`) round-trip byte-stable
(gate 3); the explicit string overrides a colliding derivation; a
string without the boolean never marks; `op/register` announced
flavor-form specs round-trip with the marker. Gate 2 (the hub-relay
round trip) lands with the in-tree relay component (review 008
remediation plan Unit 3, amending ADR-042).
## Amendment (§6 listing enrichment, 2026-09-06)