diff --git a/CHANGELOG.md b/CHANGELOG.md index 39277fc..8972601 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,88 +4,18 @@ All notable changes to this crate are documented here. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this crate adheres to [Semantic Versioning](https://semver.org/). -## [Unreleased] - -Post-landing audit of review 008's remediation (0.7.1 → 0.8.0) — two -hardening guards, two log/error-message corrections, one -rejection-posture fix, and the ADR-051 §6 residual update. No wire -changes; all behavior deltas are failure-path. - -### Fixed - -- **Adopted-entry teardown on every relay plan path** (ADR-051 §6) — - the relay's `RelayPlan` now owns the producer-leg - `ChannelManager` and reclaims the adopted spoke `channel_id` via a - `Drop` guard, instead of the pump handler's post-`pump_bidi` - explicit reclaim. This closes the leak windows the pump's normal - path cannot reach: the open wrapper's establishment bound expiring - after the adopt (the plan dropped before the pump ever spawns) and - the pump handler's early-return arms (plan absent, downcast - failure, `try_unwrap` failure, `accept_bi` failure). Dropping the - plan's send half still EOFs the spoke side via the mux pump's - implicit-EOF sentinel, so the spoke handler reclaims through the - same cascade as before. Regression test: - `relay_plan_drop_reclaims_the_adopted_producer_leg_entry`. -- **Empty/whitespace `channel_open_alpn` rejected at rebuild** — an - explicit string of `""` (or whitespace-only) previously overrode a - sane name-derived ALPN, poisoning the marker for every consumer - from one misconfigured producer; `rebuild_spec_for` now trims and - rejects empties, falling back to the derivation. -- **Empty-segment standard-shape names** — `channels//sub` was - serialized boolean-only (the standard-shape check saw flavor - `sub`) but reconstructed UNMARKED (the derivation's empty-segment - guard returned `None`) — a silent stub for a marked op. The - standard-shape check now applies the same empty-segment guard, so - the explicit `channel_open_alpn` string rides the wire and the - marker reconstructs. -- **Reserved-reply-key error text and log level** — the - `channel:open_failed` message interpolates `RESERVED_REPLY_KEY` - instead of hardcoding `"channel_id"` (the two could drift), and the - establisher-bug log fires at `warn!` (a programming error), not - `debug!`. - -### Testing - -Review 009's coverage debt (all seven findings, no wire or API -changes; three as-filed errata recorded in the review): - -- **C-1** — the template's plain-bundle install-failure arm is pinned - (un-compilable `input_schema` → the install task ends before the - dispatch loop, channel 0 never dispatches); ADR-051 §5 carries the - loud-install coverage note (relay-openable + plain-bundle arms - pinned; generic-ops + bootstrap-discovery arms documented as - best-effort-loud). -- **C-2** — the `HubLegImports` filter: the `filtered` closure path - and the `only` marked/plain partition unit-tested; the empty-stash - e2e gate (serves only the generic ops + discovery, dropped ops - resolve `NOT_FOUND`). -- **C-3** — the batch-form reserved-reply-key rejection - (`with_reply_fields` smuggling `channel_id`) pinned: reason - `handler_error`, channel torn down, ledger decremented, pump never - spawns. -- **C-4** — `open_channel_with_reply`'s failure path pinned e2e: the - typed error carries the `channel:open_failed` code and the full - `details` shape (`reason` + `message`) through the new API. -- **C-5** — both "byte-identical" claims are golden-pinned: the - no-fields reply against the exact `{"channel_id": 2}` literal, and - the standard-shape wire payload against the full 9-key literal. -- **C-6** — the derivation's edge shapes pinned: `channels//sub` / - `channels//direct` / `channels` → `None`; the 4-segment strict - superset (`channels/x/sub/extra` → `Some("x/sub")`) annotated as the - pre-amendment behavior change; the verbatim `channels/alk/tty/sub` - case. -- **C-7** — the builder's overwrite semantics pinned last-win (single - and batch forms) with the doc sentence on `with_reply_field`. - ## [0.8.0] - 2026-09-18 Review 008's remediation lands in full — the graduation upstream asks (U-1 flavor-form open-op ids in discovery; U-2 the establisher → reply projection) plus the in-tree channel relay they demanded (`ChannelRelay`, the hub-leg install template, and the review's gate-2 -e2e harness). The channels data plane is untouched (ADR-034/035's -one-way doors stay closed); the new surfaces are additive on the -call-plane JSON and the registry seams. +e2e harness). A post-landing audit of that diff hardened the relay's +adopted-entry teardown and the discovery ALPN guards, corrected two +log/error messages, and review 009's coverage debt is paid in full — +all failure-path; no wire or API changes. The channels data plane is +untouched (ADR-034/035's one-way doors stay closed); the new surfaces +are additive on the call-plane JSON and the registry seams. ### Added @@ -153,9 +83,78 @@ call-plane JSON and the registry seams. re-exported from `channels`**; the relay's teardown fix is part of this landing (no pre-relay behavior to preserve). +### Fixed + +Post-landing audit of the remediation diff — hardening guards, +message corrections, and one rejection-posture fix; all failure-path. + +- **Adopted-entry teardown on every relay plan path** (ADR-051 §6) — + the relay's `RelayPlan` now owns the producer-leg + `ChannelManager` and reclaims the adopted spoke `channel_id` via a + `Drop` guard, instead of the pump handler's post-`pump_bidi` + explicit reclaim. This closes the leak windows the pump's normal + path cannot reach: the open wrapper's establishment bound expiring + after the adopt (the plan dropped before the pump ever spawns) and + the pump handler's early-return arms (plan absent, downcast + failure, `try_unwrap` failure, `accept_bi` failure). Dropping the + plan's send half still EOFs the spoke side via the mux pump's + implicit-EOF sentinel, so the spoke handler reclaims through the + same cascade as before. Regression test: + `relay_plan_drop_reclaims_the_adopted_producer_leg_entry`. +- **Empty/whitespace `channel_open_alpn` rejected at rebuild** — an + explicit string of `""` (or whitespace-only) previously overrode a + sane name-derived ALPN, poisoning the marker for every consumer + from one misconfigured producer; `rebuild_spec_for` now trims and + rejects empties, falling back to the derivation. +- **Empty-segment standard-shape names** — `channels//sub` was + serialized boolean-only (the standard-shape check saw flavor + `sub`) but reconstructed UNMARKED (the derivation's empty-segment + guard returned `None`) — a silent stub for a marked op. The + standard-shape check now applies the same empty-segment guard, so + the explicit `channel_open_alpn` string rides the wire and the + marker reconstructs. +- **Reserved-reply-key error text and log level** — the + `channel:open_failed` message interpolates `RESERVED_REPLY_KEY` + instead of hardcoding `"channel_id"` (the two could drift), and the + establisher-bug log fires at `warn!` (a programming error), not + `debug!`. + +### Testing + +Review 009's coverage debt (all seven findings, no wire or API +changes; three as-filed errata recorded in the review): + +- **C-1** — the template's plain-bundle install-failure arm is pinned + (un-compilable `input_schema` → the install task ends before the + dispatch loop, channel 0 never dispatches); ADR-051 §5 carries the + loud-install coverage note (relay-openable + plain-bundle arms + pinned; generic-ops + bootstrap-discovery arms documented as + best-effort-loud). +- **C-2** — the `HubLegImports` filter: the `filtered` closure path + and the `only` marked/plain partition unit-tested; the empty-stash + e2e gate (serves only the generic ops + discovery, dropped ops + resolve `NOT_FOUND`). +- **C-3** — the batch-form reserved-reply-key rejection + (`with_reply_fields` smuggling `channel_id`) pinned: reason + `handler_error`, channel torn down, ledger decremented, pump never + spawns. +- **C-4** — `open_channel_with_reply`'s failure path pinned e2e: the + typed error carries the `channel:open_failed` code and the full + `details` shape (`reason` + `message`) through the new API. +- **C-5** — both "byte-identical" claims are golden-pinned: the + no-fields reply against the exact `{"channel_id": 2}` literal, and + the standard-shape wire payload against the full 9-key literal. +- **C-6** — the derivation's edge shapes pinned: `channels//sub` / + `channels//direct` / `channels` → `None`; the 4-segment strict + superset (`channels/x/sub/extra` → `Some("x/sub")`) annotated as the + pre-amendment behavior change; the verbatim `channels/alk/tty/sub` + case. +- **C-7** — the builder's overwrite semantics pinned last-win (single + and batch forms) with the doc sentence on `with_reply_field`. + ### Verified -- 669 tests pass (`cargo test`), `clippy --all-targets -- -D warnings` +- 682 tests pass (`cargo test`), `clippy --all-targets -- -D warnings` and `fmt --check` clean. The gate-2 e2e harness (`src/channels/gate2_tests.rs`) pins the review's gate 2: the full consumer → hub → producer relay through the template (hub-allocated @@ -679,6 +678,8 @@ Vendored core types (`Connection`, `ProtocolHandler`, `BiStream`, (ADR-046), the channels protocol with openable-ALPNs-as-operations (ADR-047), and the `ChannelClient` transport-agnostic client. +[0.8.0]: https://git.alk.dev/alkdev/alkcall/releases/tag/v0.8.0 +[0.7.1]: https://git.alk.dev/alkdev/alkcall/releases/tag/v0.7.1 [0.7.0]: https://git.alk.dev/alkdev/alkcall/releases/tag/v0.7.0 [0.6.0]: https://git.alk.dev/alkdev/alkcall/releases/tag/v0.6.0 [0.5.0]: https://git.alk.dev/alkdev/alkcall/releases/tag/v0.5.0