- WS-31: websocket.md §"Data channels for browsers" + ADR-067's landed note record that WS-session discovery is the bootstrap set — the hook's bootstrap `services/*` registrations overwrite a base-registry `services/*` registration on the WS path by design (a deployment's custom `services/list` is shadowed on WS sessions only). - ADR-048's landed note: correction + completion — the WS-26 retention sentence was aspirational at the landed commit (WS-28) and is now real; the UP-02 posture's override half is now an explicit surface (`with_ws_op_register_acl` / `OpRegisterAcl`), with the note that `ChannelsPolicy` could not carry an op ACL. - ADR-067's landed note: review-007 notes (WS-28 fix + gate, WS-29 surface, WS-31 record). - OQ-05 resolution: the retention claim carries the WS-28 correction. - review-006 UP-02 log + WS-26 paragraph: corrections marking what the pre-fix tree did not have, with the landed remediation named. - review-002 WS-17: the "bounded at 64 sessions" claim corrected — the bare-registry semaphore was per-request and bounded nothing; `SessionSlots` is the shared-cap surface. - review-007 status: open for remediation → remediated, with the decisions taken (both "implement" options) and the gate names. Verification: cargo test 454 passed / 0 failed; cargo doc --no-deps clean (6 pre-existing warnings, identical at baseline). Review: docs/reviews/007-ws-data-channel-surface-review.md
28 KiB
Review 006 — alkcall 0.3.0 consequence review (WS-24/WS-25 upstream, alkhttp residual)
Status
All units landed: Unit 2 + Unit 3 remediated and verified (030c5ef);
Unit 4 (docs reconciliation) landed with the 0.3.1 consumption
(Cargo.lock bump + the services/list-peers discovery gate). UP-02
decided (recorded below). UP-03 fixed in alkcall 0.3.1 (fd21230,
ADR-030) and asserted by the extended WS-path gate. See Remediation
log.
Scope
Consequence review of alkcall 0.3.0 (published 2026-09-04 — the remediation of alkcall reviews 004–005, which is the upstream resolution of this repo's review-003 findings WS-24/WS-25) against this crate. The pass answers three questions:
- What does the 0.3 bump itself cost here (source breaks, behavior changes)?
- Which review-003 findings are now resolved upstream, and which remain alkhttp-side work?
- Did the upstream remediation introduce anything this crate must absorb (new protocol codes, new wiring contracts, new decisions)?
Sources read: alkcall review 004 (docs/reviews/004-…md, remediated)
and review 005 (docs/reviews/005-…md, resolved G-01..G-05), the
0.3.0 changelog, the ADR amendments (alkcall ADR-047 §4 amendment #2,
ADR-022 amendments 2026-09-03 + 2026-09-04), and the published
0.3.0 crate source. Every claim below verified in source at this
tree (df86f89, consuming alkcall 0.3.0) or the published crate.
Findings use the new prefix UP (upstream-consequence); review 003's
WS numbering is referenced for continuity.
Baseline verification (this pass)
cargo test → 453 passed, 0 failed
cargo test --all-features → 575 passed, 0 failed
cargo clippy --all-targets -- -D warnings → clean
cargo clippy --all-features --all-targets -- -D warnings → clean
cargo fmt --check → clean
cargo doc --no-deps → clean
Verified after the 0.2 → 0.3 bump (3dee46a). The bump cost was
two source-break classes, both mechanical:
ChannelClient::take_call_connectionnow returnsOption<Arc<CallConnection>>(was a bareCallConnection) —WssSession::connectdropped itsArc::newre-wrap (src/adapters/from_wss.rs:268-274).OperationRegistry::register,ChannelOperations::register_on, andChannelCore::register_openablenow take&OperationRegistry(was&mut) — 51 stalemutbindings dropped viaclippy --fixacrosssrc/adapters/{from_wss,to_mcp, to_openapi}.rs,src/gateway/routes.rs,src/server/adapter.rs, andtests/.
The &self-registration change is the interior-mutability change
that makes the per-session fork surface possible; for every existing
alkhttp call site it is source-compatible (reborrow). The
registration() → owned and list_operations() → owned return-type
changes required no edits here (call sites already cloned through
the borrow or consumed by value-compatible iteration — verified by
the clean build).
Verdict
- WS-24 and WS-25 are resolved upstream. The two design gaps
review 003 Part B identified are now mechanized in alkcall 0.3.0
and decided in its ADRs: the per-session fork is the
dispatch-registry mechanism (alkcall ADR-047 §4 amendment #2 —
review-003 option (a), the wire-proven shape), and
op/registeris the client-side registration mechanism (alkcall ADR-022 amendment 2026-09-03 — review-003's bootstrap-op candidate). The alkhttp-side consequences of review-003 Unit 1's candidates are exactly what landed. - One upstream remediation result lands here for free: review
005 Unit 1 mechanized the accept-side pending-resolution arms into
run_loop_single_stream(the loop our WS channel-0 dispatch runs). Pre-0.3, an accept-side nested-composing handler (afrom_callimported-op stub riding the WS connection) could deadlock against the inline dispatch; post-0.3 the loop spawns Once invocations and Sub pumps, so the G-01 hazard class is shut for WS sessions by the bump alone — no alkhttp code involved. - The remaining review-003 findings are all still open and now
unblocked (WS-20/21/22/26 mechanical, WS-27 cleanup, WS-23
tests): the hook still discards the
ChannelManager(src/websocket/upgrade.rs:255), no channel ops / openables / bootstrap discovery are registered, no deployment surface declares openable ALPNs, and no live connection handle is retained. All grep-verified at this tree. - One new finding (UP-01): alkcall 0.3 introduced the
ALREADY_EXISTSprotocol code; this crate's gateway error table has no arm for it and would map it to 500. Unreachable today (onlyop_register_handleremits it), load-bearing the moment Unit 2 wiresop/register. - One wiring contract Unit 2 must honor (UP-02, recorded as a
decision point, not a defect): the
op/registercollision policy binds the WS session's handler to the session fork.
Severity legend
Same scale as review 003: [major] — a decided behavior is missing/wrong or a real hazard; [minor] — drift, convention violation, or doc/spec inconsistency with no correctness impact.
Part A — What 0.3.0 resolves for this crate
WS-24 [was major, cross-crate] — dispatch-resolution mechanism → RESOLVED upstream
alkcall ADR-047 §4 amendment #2 (2026-09-03) decides option (a): the
install_channel_zero hook forks the deployment's base registry,
registers the per-session ops on the fork (ChannelOperations:: register_on, ChannelCore::register_openable,
install_bootstrap_discovery — the last closed over the fork so
services/list sees per-session openables, review-004 F-06), and
dispatches channel 0 over the fork. The connection overlay remains
the peer-announced-op landing zone and nested-invocation target, not
the dispatch-resolution path. The reference shape is alkcall's
fork_registry_open_op_resolves_and_is_discoverable gate
(alkcall/src/channels/client.rs:1994 — fork → register_openable
→ install_bootstrap_discovery → Dispatcher::new(fork, …)), which
is the shape Unit 2 must reproduce in install_channel_zero.
Upstream also resolved the fork's discovery half (review-004 F-06):
install_bootstrap_discovery registers services/list,
services/list-peers, and services/schema closed over the fork
itself, so per-session openables are discoverable without a
re-registration walk. The bootstrap set is closed at four with
op/register (alkcall ADR-022 amendment, services/list-peers
added 2026-09-04 per review-005 G-05).
alkhttp consequence: none to code yet; Unit 2 consumes it.
WS-25 [was major, cross-crate] — client-side op registration → RESOLVED upstream
alkcall ADR-022 amendment (2026-09-03) decides the bootstrap-op
mechanism: op/register (registry::op_register module) — a peer
announces an op spec over channel 0; the hub's per-session handler
rebuilds the spec, wraps it as a forwarding stub that nested-calls
back over channel 0, and lands it in the connection overlay via
register_imported (forced Visibility::Internal, provenance
FromCall). The wire stays the six call.* envelope kinds.
Collision policy (2026-09-04 sub-amendment, review-005 G-03): an
announced op may replace other announced ops (replace governs)
but never the serving side's own registrations — ALREADY_EXISTS
regardless of replace; the handler gates on the serving registry
first, then the overlay.
The serving half it composes on (Dispatcher::serve_single_stream,
opt-in via ChannelClient::from_connection_with_serving) landed
with review-005 Unit 1's concurrency rework: Once invocations, Sub
pumps, and sink response writers are spawned in both single-stream
loops, so same-connection nested composition (the flagship
hub-composes-peer-op flow) resolves concurrently instead of
deadlocking to the 30 s sweeper (review-005 G-01, empirically
verified upstream with a since-removed probe, productized as two
5s-bounded gates).
alkhttp consequences: Unit 2 wires op/register on WS sessions
by registering op_register_spec(acl) +
op_register_handler(connection, fork) on the per-session fork
(the collision set is the fork — exactly what the session
dispatches over, mirroring the upstream gates' production shape).
Two additional facts from review 005 apply:
- The channel-0
Connectionmust carry the peer identity forcompose_root_envto attach the connection's overlay (keyed byidentity.id). alkhttp's hook already does this (src/websocket/upgrade.rs:264— identity propagated from the upgrade'sAuthContext); verified correct as-is. - The serving loop's concurrency means a hub handler that composes an announced op resolves live (no sweeper); no alkhttp-side scheduling work is needed.
ADR-048's browser bidirectionality promise is now mechanized on the
hub half; the browser half (announcing via WS framing) is a
client-side concern with no alkhttp code beyond serving the op.
The re-point edits in ADR-048/OQ-05 (committed df86f89) record
this.
Fixed-upstream items this crate inherits
- Accept-side nested-composition hazard (review-005 G-01's
second consequence): mechanized shut by the pending-resolution
arms now in
run_loop_single_stream— inherited by the bump, no alkhttp code. resource_id_pathwire round-trip (review-005 G-04): afrom_call-imported or announced op declaring ownership-scoped resource extraction previously rebuilt withresource_id: None(ACL checks ran without the resource id). Both round-trip halves now carry the field.from_wss's import path inherits the fix.- Head-of-line blocking / interleaved directions: a serving
side's outbound calls resolve while it serves a Sub (the
interleaved-directions gate upstream) — relevant to any future
serving-enabled
from_wss(see Part C).
Part B — Findings at this tree
UP-01 [minor] — The ALREADY_EXISTS protocol code is unmapped in the gateway error table
Verified: YES. alkcall 0.3 added CallError::already_exists
("ALREADY_EXISTS", non-retryable —
alkcall-0.3.0/src/protocol/wire.rs:124) for the op/register
collision policy; its own ADR-022 amendment records the code.
alkhttp's status map (src/gateway/error.rs:57-80) has no arm for
it: NOT_FOUND, FORBIDDEN, INVALID_INPUT,
INVALID_OPERATION_TYPE, TIMEOUT, INTERNAL, and HTTP_*
prefixed codes are mapped; anything else falls to STATUS_INTERNAL
(500). alkcall's ADR-016 code list (six codes) was not amended for
the seventh — upstream doc drift, noted for that repo.
Reachability today: none — the only emitter is
op_register_handler, which no alkhttp path invokes yet (grep
verified; the gateway dispatches base-registry ops directly). It
becomes reachable exactly when Unit 2 wires op/register on WS
sessions, at which point a collision rejection would surface to
wire callers as 500 with an ALREADY_EXISTS body instead of a
conflict status. Because the collision path is also how a peer
experiences the reconnect-reannounce contract (replace refused
for serving-registry names), fidelity matters there.
Fix (Unit 2, ride-along): map ALREADY_EXISTS → 409 Conflict in
call_error_to_http_status_with_identity (plus the test arm),
matching ADR-023's HTTP_<status> discipline for the new code.
One-line change + test; no consumer breaks (the code is new).
UP-02 [minor, decision point] — op/register's AccessControl on the WS surface needs a decided default
Not a defect — a decision Unit 2 cannot avoid. alkcall's
op_register_spec(access_control) takes the ACL as a parameter; its
doc notes the default (AccessControl::default()) lets any peer
register, and the op is Mutation/External-typed. This crate's
convention (AGENTS.md §13, ADR-015): adapter-registered ops are
Visibility::Internal by default — but op/register cannot be
Internal on the wire surface (the peer must call it; Internal ops
resolve spec-404 to wire callers). The analogous precedent here is
the built-in gateway's NoCap posture for channel opens (review-001
SRV-10: crate default is permissive, deployments gate via the
ChannelsPolicy extension). The consistent shape: register
op/register on the per-session fork with AccessControl::default()
on the built-in surface and let an assembly-layer override pass a
stricter ACL the same way ChannelsPolicy/OpenableAlpn flow —
record the posture in the Unit-2 task and in ADR-048's amendment
note when the wiring lands.
WS-20/WS-21/WS-22/WS-26 [major] — unchanged, now unblocked
Re-verified at this tree; all four stand as review 003 found them:
install_channel_zerostill discards the manager (src/websocket/upgrade.rs:255—_managerunused); noChannelCore, noChannelOperations::register_on(grep: zero callers in this crate), no openables.- No
OpenableAlpnsurface exists (src/server/adapter.rsbuilders unchanged:with_decoy/with_extra_routes/with_ws_max_sessions/with_ws_idle_timeout). - No live
Arc<CallConnection>handle is retained by the session (the hook moves channel 0's connection into the spawned task);WsSessionsstill tracks pump handles only.
The fix shape from review 003 Unit 2 stands, now with the upstream
mechanism named concretely: per-session fork() in the hook →
ChannelOperations::register_on(&fork) → openables via
register_openable(spec, handler, &fork, auth) →
install_bootstrap_discovery(&fork) → op_register_spec +
op_register_handler(Arc<CallConnection>, fork) → dispatch over the
fork (Dispatcher::run_loop_single_stream unchanged as the loop).
The OpenableAlpn list + with_ws_openable_alpns threading
(RouterState → SessionState → hook, request-extension fallback
mirroring ChannelsPolicy) is unchanged, as is the WS-26
handle-retention design (eviction hooks into WsSessions).
WS-27 [minor] — unchanged; folds into the Unit-2 hook rework
adapter_install_channel_zero (src/websocket/upgrade.rs:290-295)
is still #[allow(dead_code)] with its only caller test-gated
(src/adapters/from_wss.rs test server). The rework gives both
callers a real hook; fold the cfg cleanup there.
WS-23 [minor] — unchanged; Unit 3's e2e gates gain two upstream-mirroring gates
The browser-opened-channel e2e test is still absent (no test opens a
data channel through a live WS session). Unit 3's five scenarios
stand as written. Two gates to add, mirroring what review 005
taught: services/list on a live WS session lists the per-session
openable (the F-06 shape), and the op/register announce → hub
nested-composition → routes-back-to-consumer flow (the stub path —
the one review-005's original gate missed and G-02 called out).
Part C — Noted, not findings
- Connect-side serving for
from_wss: alkcall 0.3'sfrom_connection_with_servingmakes the WSS consumer half capable of serving inbound calls (the from_wss session could answer hub→consumer calls).from_wsskeeps the pure-consumer default (from_connection, resolution-only pump — verified unchanged); opt-in serving is a deliberate later decision (it changesfrom_wss's security posture: a dial would then execute inbound requests against a caller-chosen registry). Recorded here so the opportunity is visible; no finding, no unit. op/registerforced-Internal semantics vs AGENTS.md §13: the announced op landsInternal/FromCallon the hub — consistent with the adapter-registered-ops-are-Internal convention (it is composition material, not wire-callable from the hub side). No conflict; noted to preempt an apparent tension.list_operationsreturnsExternalonly — theto_openapi/to_mcpprojections and the gateway/searchcontinue to see exactly the External set; unchanged from 0.2.- Upstream doc drift (alkcall-side, not ours): ADR-016's
protocol-code list ("six codes", last amended by ADR-021) does not
name
ALREADY_EXISTS. The 2026-09-04 ADR-022 collision sub-amendment records the code's use; ADR-016's list is stale. Flagged for the next alkcall doc pass; UP-01 is the alkhttp-side half of the same gap.
Remediation log
Unit 2 — alkhttp wiring (WS-20/21/22/25/26/27 + UP-01) — LANDED (030c5ef)
Hook rework (WS-20/21/25). install_channel_zero
(src/websocket/upgrade.rs) now takes (registry, sessions, policy, openable_alpns) and, per the alkcall ADR-047 §4 amendment #2 shape
(verified against alkcall's own e2e reference
fork_registry_open_op_resolves_and_is_discoverable):
- Forks the deployment's base registry.
- Registers on the fork:
ChannelOperations::register_on(the genericchannel/close/channel/control/channel/resources/subscribe— WS-21), the deployment's openables viaChannelCore::register_openable(WS-22),install_bootstrap_discoveryclosed over the fork (F-06 — the session'sservices/listsees its own openables), andop/register(WS-25 —op_register_spec+op_register_handler(connection, fork); the collision set is the fork, the production shape review-005's gates use). - Dispatches over the fork (
Dispatcher::run_loop_single_streamunchanged as the loop; the loop's pending-resolution arms are the review-005 G-01 mechanization the bump inherited).
UP-02 decided. op/register is registered with
AccessControl::default() on the built-in surface (the SRV-10
precedent: permissive crate default), and an assembly layer overrides
via a stricter policy passed through the hook — the posture mirrors
ChannelsPolicy. The override surface rides the existing policy
parameter; the assembly layer's ChannelsPolicy value is the same
object the open wrappers consult. (Review 007 WS-29 correction: the
ChannelsPolicy object is an Arc<dyn ChannelLifecyclePolicy> — the
channel-cap policy — and cannot carry an operation AccessControl; at
this commit the hook hardcoded the permissive default and no override
surface existed. Remediation implemented the surface as decided:
HttpAdapter::with_ws_op_register_acl + the OpRegisterAcl request
extension, review 007 Unit 2. The posture above stands as recorded;
only the "rides the existing policy parameter" mechanism was wrong.)
Openable-ALPN surface (WS-22). OpenableAlpn { spec, open_handler } + HttpAdapter::with_ws_openable_alpns(Vec<..>),
threaded RouterState → SessionState → run_channels_session →
hook, with the OpenableAlpns request-extension fallback on the
upgrade handler (mirroring ChannelsPolicy/WsTimeouts). Default:
no openables (channel 0 only).
WS-26. WsSessions gains a connections map +
live_connections() / live_connection_count(); the hook retains
the channel-0 Arc<CallConnection> under a self-removing
ConnectionGuard (dropped when the channel-0 task ends — any
teardown path). Shared only when a shared WsSessions instance is in
play (the bare-registry/test-server path passes None). (Review 007
WS-28 correction: at this commit the guard was bound inside the if let block and dropped microseconds after insertion — the retention
sentence above described the intended scope, not the landed one.
Remediation bound the guard as an Option<ConnectionGuard> in the
channel-0 task's frame; the gate
live_connections_visible_mid_session_and_drain_after_teardown pins
it.)
Policy threading. The hook takes the session's resolved
ChannelsPolicy (the extension resolution, NoCap default) and
passes it to both ChannelOperations and ChannelCore — one policy
instance across the open wrappers and the demux-loop teardown path.
(First cut used a fresh default_policy() per hook; the
ledger-decrement gate caught it — the ChannelsPolicy extension was
never consulted. Fixed by threading; that interaction is exactly the
seam SRV-10's extension was built for.)
UP-01. ALREADY_EXISTS → 409 Conflict in
call_error_to_http_status_with_identity + the test arm; the module
doc names the seventh code.
from_wss import exclusion (new in this unit, surfaced by the
gates). The reworked hook serves the bootstrap + channel-lifecycle
set on every WS session, so a from_wss import's services/list
listing now carries them. Proxying session-scoped machinery would be
nonsense (channel/close proxied to the remote would forward a close
for this session's channel ids; op/register proxied duplicates
the import itself). FromWss::import now runs discovery first and
passes the listing minus the protocol-session ops
(services/list, services/schema, services/list-peers,
op/register, channel/close, channel/control,
channel/resources/subscribe) as the operation_filter — the
from_call filter surface, used for the first time by alkhttp. The
three from_wss discovery tests assert the original op sets and pass.
WS-27. adapter_install_channel_zero cfg-gated to exactly its
caller's cfg (all(test, feature = "server", feature = "wss")); the
#[allow(dead_code)] is gone; it inherits the reworked hook (the
from_wss test-server producer now serves the session ops too).
Gate results (Unit 3, tests/ws_upgrade_session.rs):
data_channel_open_discoverable_and_bytes_round_trip—services/listlists the session's openable + generic channel opsop/register(F-06 shape); open op returns an evenchannel_id; a data chunk echoes back through theOpenHandler'sConnection.
channel_close_resolves_and_decrements_the_opener_ledger— close resolves{"closed": true}; the ledger drops to 0.channel_cap_denies_open_over_the_limit— the second open over the cap denies with thechannel:too_many_channelscode (theChannelsPolicyextension is consulted).disconnect_mid_open_tears_down_and_decrements— hard disconnect → the demux teardown decrements the ledger, no leak.too_large_data_channel_chunk_survives_demux_resync— aMAX_CHUNK_LEN + 1declared chunk is skipped (the skip consumes the declared payload off the transport, so the gate sends the filler), the next chunk routes and echoes — resync through the WS path (scenario 5).op_register_served_per_session_and_collision_is_already_exists— announce resolvesregistered: true; a second announce withoutreplacerejects with the overlay gate'sALREADY_EXISTS; announcing the serving side's own op name withreplace: truerejectsALREADY_EXISTS(the G-03 gate, through the WS path).disconnect_mid_call_does_not_hang_the_serverand the full pre-existing suite stay green.
Test-harness hardening the gates required (recorded so the next
pass doesn't re-derive it): call_and_await filters by request id
(a prior Sub's trailing call.completed may interleave) and
tolerates data-channel chunks; await_envelope is the same filter
for hand-built frames; CallError-payload envelopes serialize flat
(env.payload["code"], not env.payload["error"]["code"]).
Verification (post-fix):
cargo test → 454 passed, 0 failed
cargo test --all-features → 582 passed, 0 failed
cargo clippy --all-targets -- -D warnings → clean
cargo clippy --all-features --all-targets -- -D warnings → clean
cargo fmt --check → clean
cargo doc --no-deps → clean
Unit 4 — spec reconciliation (docs) — LANDED (with the 0.3.1 consumption)
As planned in this review's remediation plan: OQ-05 resolved,
ADR-067 v1-cut note + ADR-048 amendment note updated to the landed
state, websocket.md §"Data channels for browsers" status block removed
(the section now documents the landed surface: with_ws_openable_alpns,
the OpenableAlpns fallback, cap policy, discovery, gates), the
idle-knob deployment note surfaced (silent data channels make the 60 s
default bite more often; semantics unchanged), the op/register ACL
posture (UP-02's decision) recorded in ADR-048's landed note, and the
UP-03 alkcall-side gap closed by 0.3.1 and asserted alkhttp-side (the
extended op/register gate). The alknet-ADR-044 §5 pointer was
checked: not stale (the rationale lives in this crate's ADR-044 §5;
the alknet-ADR-043 pointers are correctly marked parked/not-ported).
Findings added during remediation
UP-03 [minor, alkcall-side] — services/list-peers cannot list peer-announced ops; PeerCompositeEnv does not override peer_operations
Discovered by the Unit-3 gates: the first draft asserted announced-op
discoverability through services/list-peers and failed — the peers
array carries only peer_id entries with empty operation lists.
Verified in both the published 0.3.0 crate and the alkcall tree:
services_list_peers_handler maps ctx.env.peer_operations(&peer_id)
per peer (alkcall/src/registry/discovery.rs:347-351), but
PeerCompositeEnv overrides peer_ids only — peer_operations falls
to the trait default (Vec::new(),
alkcall/src/registry/env.rs:63-65); the connections map and
connection_order it would list from are in scope but unused there.
OverlayOperationEnv likewise overrides contains only. Net effect:
the peer-keyed listing shows peer_id entries whose operations
array is always empty, and the ADR-022 amendment's "announced op is
discoverable via services/list-peers" promise does not resolve on
the wire today. The alkcall gates never caught it because the F-05
gate calls the announced op directly (bypassing discovery) and the
G-02 gate composes it — neither lists peers after announcing.
Fix lives in alkcall (one method override:
PeerCompositeEnv::peer_operations(peer) → the connection env's
overlay registration names, ACL-filtered like the local listing;
OverlayOperationEnv may want a matching helper). Flagged for the
next alkcall review pass; the alkhttp-side gate
(op_register_served_per_session_and_collision_is_already_exists)
asserts the overlay-landing + collision semantics instead, which is
the behavior Unit 2 actually wires, and is not blocked by UP-03.
LANDED in alkcall 0.3.1 (2026-09-04, fd21230): implemented as
alkcall ADR-030 — OperationEnv gains a defaulted
list_operation_names; OverlayOperationEnv overrides it;
PeerCompositeEnv::peer_operations delegates to the peer overlay,
and its list_operation_names aggregates session + connections +
base. Upstream gate:
announced_op_is_discoverable_via_services_list_peers. The version
pin here is 0.3 (semver-compatible), so the fix arrives through a
Cargo.lock bump with no source change; the WS-path gate that
originally surfaced UP-03 now asserts the discovery promise —
op_register_served_per_session_and_collision_is_already_exists
gained the services/list-peers step (announce → the alice peer
entry lists consumer/exec) and passes against 0.3.1 (it fails
against 0.3.0, verified by the original draft).
UP-02 decision (recorded)
op/register is served per WS session with
AccessControl::default() on the built-in surface (the SRV-10
posture: the crate default is permissive, deployments gate via the
ChannelsPolicy extension whose value the hook now consults). The
wire-callability tension in this review's UP-02 note resolves the
same way: the peer must call op/register (it is in the bootstrap
set), so Internal visibility was never an option; the ACL parameter
is the deployment's gate, not the visibility flag.
Verification log (this pass)
- Bump verified empirically:
cargo check(default + all-features) surfaced exactly the two break classes; post-fix gates all green (453 / 575 / clippy both / fmt / doc). - WS-24/WS-25 resolutions read in the published 0.3.0 crate source
(
registry::op_registerhandler + collision gate;Dispatcher::serve_single_stream;install_bootstrap_discovery;OperationRegistry::fork) and in the upstream ADR amendment texts; the reference fork shape read atalkcall/src/channels/client.rs:1988-2130. - Review-003 residual claims re-verified at this tree: hook
discards manager (
upgrade.rs:255), zeroregister_oncallers, no openable surface, no retained connection handle, WS-27 caller census unchanged. - UP-01 verified: no
ALREADY_EXISTSarm insrc/gateway/error.rs:57-80; emitter census in 0.3.0 =op_register.rsonly; fall-through confirmed toSTATUS_INTERNAL. - UP-02 verified:
op_register_specsignature and its default-ACL doc note; AGENTS.md §13 vs wire-callable tension resolved against the SRV-10 precedent (permissive crate default + extension override). - Cross-repo state verified: alkcall review 005 resolved
(G-01..G-05 remediation log), 0.3.0 published with
cargo semver-checks196 checks against 0.2.0 (changelog's Changed section matches the two source-break classes this crate experienced).