fix: Unit 6 — convention + doc cleanup (C-09, C-20-rem, C-22-rem, C-24, P-10, P-11)

Conventions satisfied, `cargo doc` clean, no actively-wrong comments,
producer/consumer naming consistent in the call/registry docs.

P-10 — `pump_sink` matched the string literals "call.published",
"call.completed", "call.aborted" (dispatch.rs) instead of the
EVENT_PUBLISHED / EVENT_COMPLETED / EVENT_ABORTED constants the rest
of the file imports. Replaced with the constants — pure refactor
hazard, no behavior change.

C-20 remainder — the wrong "SAFETY:" comment at from_call.rs:271
marked no `unsafe` block and was factually wrong (described a `'static`
return that isn't what `derive_alpn_from_op_name` does — it returns
`Option<String>`; the leak happens in `leak_alpn`). Reworded to a
plain note about the `'static` lifetime requirement. Also reworded
the abort-cancels claims in the `pump_sink` and `pump_stream` doc
comments (dispatch.rs): both claimed `call.aborted` "cancels the task
and drops the handler future" — the handler is actually `join!`-ed to
completion and not yet cancelled (the abort-cancels-Pub mechanism is
review 001 Unit 9). Trimmed step-numbered narration comments in
adapter.rs / client.rs that restated what the code does, keeping the
ordering-constraint and REQ-CH comments. The big reassembly.rs
deliberation landed with Unit 4; this finishes the remainder.

C-09 — fixed the 2 remaining `cargo doc` warnings (was 4; the
register_openable links were fixed in Unit 3):
- `unresolved link to default_policy` (operations.rs:50) — the
  [`default_policy`] intra-doc link resolves to
  super::policy::default_policy; used the full path.
- `env is both a module and a macro` (channels/mod.rs:30) — the
  [`env`] link collided with the std `env!` macro; qualified as
  [`self::env`].
`cargo doc --no-deps` now emits 0 warnings.

C-22 remainder — removed the filler `PhantomData` test at client.rs
(`let _ = std::marker::PhantomData::<ChannelClient>;` — asserts
nothing). The env.rs tautology was already removed in Unit 3.

C-24 — replaced "client→server streaming" with "producer→consumer
streaming" in call-protocol.md, operation-registry.md, README.md, and
open-questions.md (4 occurrences). Per AGENTS.md §8 the convention is
producer/consumer, not server/client. The remaining "client→server"
references in channels ADRs 034/037 are in stream_type table contexts
that Unit 10 (C-26) will handle as part of the spec-doc renumbering.

P-11 — amended ADR-046 §3's SinkHandler type so the stream item type
matches §6. §3 declared `Pin<Box<dyn Stream<Item = Value> + Send>>`;
§6 declared `Pin<Box<dyn Stream<Item = Result<Value, CallError>> +
Send>>`. The code uses §6's shape uniformly (registration.rs:32-40,
aliased as PublishStream). §3's text and the Door-type section are
amended to match §6; the Door-type section already marked the concrete
stream item type a two-way-door detail, so this is a text correction,
not a design change. Added an amendment note dated 2026-08-13.

Verification:
- cargo test --lib         → 449 passed, 0 failed (was 450; -1 removed filler test)
- cargo clippy --all-targets -- -D warnings → clean
- cargo fmt --check        → clean
- cargo doc --no-deps      → 0 warnings (was 2)
This commit is contained in:
glm-5.2 committed 2026-08-13 08:47:20 +00:00
1 parent 48564a8f49
commit bfb265e31b
11 files changed
+65 -84

No files matched your search

+2 -2
View File
@@ -98,7 +98,7 @@ are wire-stable and unchanged — see ADR-004.
| [043](decisions/043-channelclient.md) | ChannelClient | Transport-agnostic from_connection |
| [044](decisions/044-channels-subcrate-decomposition.md) | Channels Sub-Crate Decomposition | channels-core / channels-call (modules in alkcall) |
| [045](decisions/045-alknetclient-native-dial-seam.md) | AlknetClient Dial Seam | spawn_dispatch / from_connection take-over; dial in consumer |
| [046](decisions/046-publish-operation-type-and-handler-kind-sink.md) | Publish Operation Type and HandlerKind::Sink | `OperationType::Pub` (client→server streaming); `SinkHandler` + `HandlerKind::Sink`; `call.published` wire event; `invoke_sink()` dispatch; `Subscription` renamed to `Sub` |
| [046](decisions/046-publish-operation-type-and-handler-kind-sink.md) | Publish Operation Type and HandlerKind::Sink | `OperationType::Pub` (producer→consumer streaming); `SinkHandler` + `HandlerKind::Sink`; `call.published` wire event; `invoke_sink()` dispatch; `Subscription` renamed to `Sub` |
| [047](decisions/047-openable-alpns-are-operations.md) | Openable ALPNs Are Operations | `channel/open` dissolves into per-ALPN ops `channels/<alpn>/sub`/`pub`; `channel_open` marker on `OperationSpec`; `ChannelCore` wrapper; extension-trait `ChannelOperationEnv`; connection-owner allocates `channel_id`; opener ledger (Gap 2 fix); ALPNs are call apps |
## Relevant Open Questions
@@ -107,7 +107,7 @@ See [open-questions.md](open-questions.md) for the full tracker. Key
questions affecting this crate:
- **OQ-01**: Call protocol pub/sub primitive (partially resolved) —
ADR-046 adds the `Pub` primitive (client→server streaming). The
ADR-046 adds the `Pub` primitive (producer→consumer streaming). The
fan-out/broker is deferred to channels (Gap B in ADR-047 is named
out-of-scope for alkcall; the hub composes the broker on top).
- **OQ-02**: Full channel-level flow-control windowing (deferred(scope))
+1 -1
View File
@@ -581,7 +581,7 @@ Handlers clean up resources when their call is cancelled (in Rust, the future is
| Forwarded-for identity | [ADR-026](decisions/032-forwarded-for-identity.md) | `forwarded_for` field on `call.requested` and `OperationContext`; metadata only — `AccessControl::check` never reads it; the `from_call` handler populates it |
| Operation error schemas | [ADR-016](decisions/023-operation-error-schemas.md) | Operations declare domain errors; `call.error` carries typed `details` |
| Streaming handler for subscriptions | [ADR-021](decisions/021-streaming-handler-for-subscriptions.md) | `StreamingHandler` type, `invoke_streaming()` dispatch path, `INVALID_OPERATION_TYPE` protocol code; the server-side streaming branch in `handle_stream` |
| Publish operation type and HandlerKind::Sink | [ADR-046](decisions/046-publish-operation-type-and-handler-kind-sink.md) | `OperationType::Pub` (client→server streaming); `SinkHandler` + `HandlerKind::Sink`; `call.published` wire event; `invoke_sink()` dispatch; `Subscription` renamed to `Sub` |
| Publish operation type and HandlerKind::Sink | [ADR-046](decisions/046-publish-operation-type-and-handler-kind-sink.md) | `OperationType::Pub` (producer→consumer streaming); `SinkHandler` + `HandlerKind::Sink`; `call.published` wire event; `invoke_sink()` dispatch; `Subscription` renamed to `Sub` |
## Open Questions
@@ -137,13 +137,14 @@ A new handler kind consumes the initiator's stream and returns a single
/// Sink handler — `Pub` operations. Receives the initiator's data
/// stream and returns a single `ResponseEnvelope` (the result of
/// consuming the stream). Each `Ok(value)` published by the initiator
/// arrives as an item in the `RecvStream`; the handler processes them
/// and returns one result (success or error).
/// arrives as an item in the stream; the handler processes them and
/// returns one result (success or error). An `Err` item (an
/// initiator-side `call.error`) terminates the stream early.
pub type SinkHandler = Arc<
dyn Fn(
Value,
OperationContext,
Pin<Box<dyn Stream<Item = Value> + Send>>,
Pin<Box<dyn Stream<Item = Result<Value, CallError>> + Send>>,
) -> Pin<Box<dyn Future<Output = ResponseEnvelope> + Send>>
+ Send
+ Sync,
@@ -156,13 +157,25 @@ pub enum HandlerKind {
}
```
The `Pin<Box<dyn Stream<Item = Value> + Send>>` is the initiator's
published stream — each `call.published` event's `payload.input` yields
one `Value` item. The handler consumes the stream to completion, then
returns a single `ResponseEnvelope`. An `Err` in the stream (a
`call.error` from the initiator) terminates the stream early; the
handler may produce its `ResponseEnvelope` from the partial input or
return the initiator's error.
The `Pin<Box<dyn Stream<Item = Result<Value, CallError>> + Send>>` is
the initiator's published stream — each `call.published` event's
`payload.input` yields one `Ok(Value)` item. The handler consumes the
stream to completion, then returns a single `ResponseEnvelope`. An
`Err(CallError)` item (a `call.error` from the initiator) terminates
the stream early; the handler may produce its `ResponseEnvelope` from
the partial input or propagate the error. The `Result`-carrying item
type matches §6's `publish_stream` shape so the handler sees initiator
errors; the concrete stream item type is a two-way-door detail (see
§"Door-type decisions" below).
> **Amendment (2026-08-13, review 001 P-11):** §3 originally declared
> the stream item type as `Value`, while §6 declared
> `Result<Value, CallError>`. The code resolves this in favor of §6
> uniformly (`SinkHandler` takes `Item = Result<Value, CallError>`,
> aliased as `PublishStream`). §3's text is amended here to match §6;
> the ADR's Door-type section already marked the concrete stream item
> type a two-way-door detail, so this is a text correction, not a
> design change.
Registration validates: `Pub` → `HandlerKind::Sink`. Mismatch is a
startup error (same pattern as ADR-021's `Subscription` → `Stream`
@@ -418,8 +431,8 @@ the canonical form; `"subscription"` is gone).
The `HandlerKind` enum shape (`Once(Handler) | Stream(StreamingHandler)
| Sink(SinkHandler)`) is the one-way commitment: three handler variants,
validated against `op_type`. The concrete `Pin<Box<dyn Stream<Item =
Value> + Send>>` choice for the sink's input stream is a two-way-door
validated against `op_type`. The concrete stream item type
(`Result<Value, CallError>`, per §3/§6) is a two-way-door
implementation detail within the one-way decision.
The `publish_schema: Option<Value>` field on `OperationSpec` is a
+2 -2
View File
@@ -49,10 +49,10 @@ The call protocol's `StreamingHandler` / `invoke_streaming()` path
(ADR-021) is point-to-point: a `call.requested` arrives, the handler
produces a stream of `call.responded` events back to that one caller.
There was no mechanism for a producer to stream data *to* a responder
(client→server streaming), and no fan-out (one producer, N consumers).
(producer→consumer streaming), and no fan-out (one producer, N consumers).
**ADR-046** resolves the directional gap: `OperationType::Pub` is the
client→server streaming complement to `Sub` (was `Subscription`,
producer→consumer streaming complement to `Sub` (was `Subscription`,
renamed for symmetry). `HandlerKind::Sink` is the consuming handler
type. `call.published` is the wire event carrying stream chunks.
`invoke_sink()` is the dispatch path. `CallConnection::publish()` is
+1 -1
View File
@@ -972,7 +972,7 @@ The `Capabilities` type holds non-serializable, zeroized secret material. It doe
| Forwarded-for identity | [ADR-026](decisions/032-forwarded-for-identity.md) | `forwarded_for` field on `OperationContext` and `call.requested`; metadata only — `AccessControl::check` never reads it; the `from_call` handler populates it |
| Streaming handler for subscriptions | [ADR-021](decisions/049-streaming-handler-for-subscriptions.md) | `StreamingHandler` type alongside `Handler`; `HandlerKind` enum on `HandlerRegistration` validated against `op_type`; `invoke_streaming()` on `OperationRegistry`; `invoke()` and `OperationEnv::invoke()` error with `INVALID_OPERATION_TYPE` on `Subscription` ops; composition stays request/response-only, stream composition is handler-level |
| Dynamic resource ownership for runtime-spawned resources | [ADR-011](decisions/050-dynamic-resource-ownership-for-runtime-spawned-resources.md) | `AccessControl::check` consults an `OwnershipProvider` (sync read trait, ADR-033 repo/adapter pattern); `OperationSpec` gains `resource_id_path` (JSON pointer into the input); proxy-only access pattern (spawner owns, proxy to share, teardown revokes); `list` = scope-gate + result-filter; teardown = automatic, handler-driven; composition = two orthogonal checks, ADR-017/022 unchanged |
| Publish operation type and HandlerKind::Sink | [ADR-046](decisions/046-publish-operation-type-and-handler-kind-sink.md) | `OperationType::Pub` (client→server streaming); `SinkHandler` + `HandlerKind::Sink` + `PublishStream`; `invoke_sink()` dispatch path; `call.published` wire event; `OperationSpec.publish_schema`; `Subscription` renamed to `Sub`; fan-out/broker deferred to channels |
| Publish operation type and HandlerKind::Sink | [ADR-046](decisions/046-publish-operation-type-and-handler-kind-sink.md) | `OperationType::Pub` (producer→consumer streaming); `SinkHandler` + `HandlerKind::Sink` + `PublishStream`; `invoke_sink()` dispatch path; `call.published` wire event; `OperationSpec.publish_schema`; `Subscription` renamed to `Sub`; fan-out/broker deferred to channels |
## Open Questions