fix(review 005 Unit 2): op/register serving-registry collision gate (G-03)

- op_register_handler takes the serving registry alongside the
  connection and rejects announced names that collide with the serving
  side's own registrations (ALREADY_EXISTS regardless of replace).
  Peer-announced ops may collide with peer-announced ops (replace
  governs, the reconnect path) but never shadow the deployment's own
  ops: the connection overlay resolves before base in PeerCompositeEnv,
  so an unscreened same-name announce would silently rewrite what a
  wire-dispatched handler's ctx.env.invoke resolves. Composition
  authority (ADR-018) stays with the deployer.
- ADR-022 amendment (2026-09-04): collision policy recorded in the
  2026-09-03 amendment's op/register section (rationale + visibility
  irrelevance); status line notes the sub-amendment.
- Gates: base-External collision rejected even with replace (overlay
  stays clean, serving registration untouched); Internal base op
  equally protected; overlay/overlay collisions still follow replace;
  nested composition of a base op resolves the serving side's own op
  after an unrelated announce (real compose_root_env env shape).
- PeerCompositeEnv resolution order deliberately unchanged.

Verification: cargo test 587 / --all-features 604, clippy
(all-targets, all-features, wasm32) clean, fmt clean, doc clean.

Refs docs/reviews/005-...md (G-03; Unit 3 open).
This commit is contained in:
glm-5.3-flash committed 2026-09-04 09:40:11 +00:00
1 parent 1cbb7c6536
commit 23c9b28c6b
4 files changed
+330 -11

No files matched your search

@@ -2,8 +2,8 @@
## Status
Unit 1 (G-01, G-02) remediated and verified; Units 2–3 open for
remediation. See Remediation log.
Units 1 (G-01, G-02) and 2 (G-03) remediated and verified; Unit 3
(G-04, G-05) open for remediation. See Remediation log.
## Scope
@@ -223,6 +223,9 @@ with an interleaved-directions phase.
## G-03 [major] — Announced ops can shadow the serving side's own ops in nested composition; the collision gate checks the overlay only
**Status: REMEDIATED (Unit 2)** — see Remediation log; the ADR-022
amendment records the collision policy.
**ADR drift:** ADR-018 (composition authority) and ADR-019 (the
overlay is the *landing zone* for imported ops — a layer beneath the
deployment's own registry, not a rival to it); ADR-022 amendment
@@ -375,8 +378,8 @@ either way.
Sequenced by dependency. All units are alkcall work; Unit 4 (alkhttp
wiring) stays downstream and should **not** start before Unit 1 —
alkhttp's serving consumers would compose over the same connection
and hit G-01 immediately. (Unit 1 landed — see Remediation log;
Units 2–3 remain.)
and hit G-01 immediately. (Units 1–2 landed — see Remediation log;
Unit 3 remains.)
## Unit 1 — Concurrent serving loop + a stub-exercising gate (G-01, G-02)
@@ -417,6 +420,73 @@ Units 2–3 remain.)
# Remediation log
## Unit 2 — `op/register` collision policy (G-03) — LANDED
**Fix shape.** `op_register_handler` now takes the serving registry
alongside the connection (`op_register_handler(connection,
serving_registry)` — the review's "name-set closure" recommendation,
materialized as the `Arc<OperationRegistry>` itself, mirroring how
`install_bootstrap_discovery` closes over its registry). The handler
checks `serving_registry.registration(name)` **before** the overlay
gate and rejects base collisions with `ALREADY_EXISTS` regardless of
`replace`; overlay collisions keep the existing `replace` semantics.
Announced ops may collide with announced ops, never with the serving
side's own registrations.
**ADR-022 amendment:** the 2026-09-03 amendment's `op/register`
section gained a "Collision policy (amended 2026-09-04, review 005
G-03)" paragraph recording the decided policy and the rationale (the
`PeerCompositeEnv` connections-before-base resolution would let an
unscreened announce rewrite composition resolution; visibility is
irrelevant to the gate). The ADR's status line notes the sub-amendment.
**Call sites updated:** both e2e gates pass the fork the session
dispatches over (`Arc::clone(&accept_registry)` after `fork()`), which
is the production shape — the collision set is exactly the registry
the serving loop dispatches against.
**Gates:**
- `handler_rejects_base_registry_collision_even_with_replace` —
base-registered `fs/readFile` (External) + `replace: true` →
`ALREADY_EXISTS`; the overlay stays clean and the serving
registration is untouched.
- `handler_rejects_collision_with_internal_serving_op` — an
`Internal` base op is equally protected (the review's point that
forced `Visibility::Internal` on the *announced* spec never helped:
`OverlayOperationEnv` gates on `AccessControl`, and the composed
child is `internal: true` by design).
- `handler_overlay_collision_still_governed_by_replace` — announce/
announce collisions still follow `replace` (the base gate is
scoped to the serving registry, not widened into the overlay).
- `nested_composition_of_base_op_unaffected_by_unrelated_announce` —
after a successful distinct-name announce, composing the base op
through the real `compose_root_env` shape (`PeerCompositeEnv` +
`attach_peer(conn.overlay_env())`) resolves the serving side's own
op, not a peer stub. (Unit-level, using the actual env types rather
than a new full-wire fixture — the wire shape is already covered by
the Unit-1 gates.)
**Deliberate non-change:** `PeerCompositeEnv` resolution order is
untouched (connections before base) — the review's recommendation;
reordering would change long-decided layering semantics
(ADR-019/ADR-024) for no gain the registration-side gate doesn't
deliver.
**Verification (post-fix):**
```
cargo test → 587 passed, 0 failed
cargo test --all-features → 604 passed, 0 failed
cargo clippy --all-targets -- -D warnings → clean
cargo clippy --all-features --all-targets -- -D warnings → clean
cargo fmt --check → clean
cargo clippy --target wasm32-unknown-unknown -- -D warnings → clean
cargo doc --no-deps → clean
```
---
## Unit 1 — Concurrent serving loop + stub-exercising gates (G-01, G-02) — LANDED
**Fix shape.** `dispatch()` was split into a synchronous start half and