--- id: ops-call-harness name: the in-process alkcall harness — the serving assembly and the client side over one duplex pair status: pending depends_on: [ops-foundation] scope: narrow risk: low impact: component level: implementation tags: [wave-7, ops, testing, harness, alkcall] --- ## Description The test harness every ops handler task exercises its handlers through: an in-process alkcall **serving side + client side over one `tokio::io::duplex` pair** — the embedder's "dials and hands an established `Connection` in" shape (ops-surface.md §Why alkcall; alkcall is a pure protocol crate), realized in-process so the ops surface is testable server-less. - **The serving assembly** (the embedder-assembly pattern realized in test): `OperationRegistryBuilder` receiving the ops family's spec/handler pairs → the dispatch serving stack (`CallAdapter` / `GatewayDispatch`, plus `ChannelsAdapter` for the binary ops' `channel_open` path) → the dispatch run loop over a `Connection::from_bidi` half (alkcall ADR-009's public stream constructor). The assembly's shape is the module's documented embedder entry — the "module exports spec/handler pairs; the embedder assembles" placement (ops-surface.md's placement note, the alkgit `git/repo/*` pattern). - **The client side**: `CallClient` + `spawn_dispatch` over the duplex pair's other half; `ChannelClient` (`from_connection`, `open_channel`/`call_open_op`) for the channel ops. - **The identity/ACL wiring**: a test `IdentityProvider` + `OwnershipProvider` — the embedder-registry stand-in (namespace → identities → granted actions) — so ACL behavior is exercisable from the first handler task onward, including the closed-by-default denial path (OQ-09). - **Proven with a stub op**: a trivial stub op registered through the same path round-trips (call → response envelope), and the channel path opens with bytes flowing — the pipe proven before any real op rides it. The stub lives in the test tree; it registers nothing into the shipped module surface. ## Acceptance Criteria - [ ] The harness lives in the ops module's test tree (`#[cfg(test)]` under feature `ops`), parameterized over the assembled registry (ops register; the harness serves) - [ ] Server + client halves over one duplex pair; the dispatch run loop serves; the client calls and receives envelopes - [ ] The identity/ownership wiring exercisable: a stub op carrying an `AccessControl` denies an ungranted identity and admits a granted one (the ACL path proven end-to-end pre-handler) - [ ] The channel path proven: a stub `channel_open` op establishes (the `Establishment` phase) and bytes flow both directions - [ ] `cargo test --features ops` green; the default build unaffected (the harness is feature-gated test code); `cargo clippy --all-targets -- -D warnings` and `cargo fmt --check` green on both shapes ## References - docs/architecture/ops-surface.md (§Why alkcall — the Connection hand-in; the placement note) - docs/architecture/decisions/001-substrate-posture-and-ops-placement.md §2 (the machinery inventory this harness assembles) - alkcall (upstream checkout `/workspace/@alkdev/alkcall`): `src/registry/registration.rs` (`OperationRegistryBuilder`, the handler kinds), `src/protocol/adapter.rs` (`CallAdapter`), `src/protocol/connection.rs` (`Connection::from_bidi`), `src/channels/adapter.rs` (`ChannelsAdapter`), `src/channels/client.rs` (`ChannelClient`), ADR-047/049 (the channel_open marker + establishment) - tasks/ops-foundation.md (the context the harness's assembled handlers capture) ## Notes > To be filled by implementation agent ## Summary > To be filled on completion