The #2 gap in alknet-call: discovers the remote peer's External operations
via services/list + services/schema and registers them in the connection's
Layer 2 overlay as FromCall-provenance leaves with forwarding handlers. The
discovery mechanism was already implemented in registry/discovery.rs;
from_call is the client-side consumer of that API.
src/client/from_call.rs:
- from_call(connection, FromCallConfig) -> Result<Vec<HandlerRegistration>,
AdapterError>. Calls services/list then services/schema for each op,
rebuilds OperationSpec from the schema JSON (parsing op_type, visibility,
error_schemas, access_control), constructs a forwarding handler that calls
the remote op via CallConnection::call(), and returns FromCall-provenance
bundles (composition_authority: None, scoped_env: None, empty capabilities,
remote_safe: false per ADR-028 §4).
- FromCallConfig { namespace_prefix: Option<String>, operation_filter:
Option<HashSet<String>> } with builder methods.
- v1 defaults (two-way doors recorded in client-and-adapters.md):
- error-on-collision (DC-3/OQ-28): applying the (possibly empty) prefix
produces a name already seen -> AdapterError::Conflict, not silent
overwrite.
- auto-on-reconnect (DC-2/OQ-27): the overlay is per-connection (Layer 2,
ADR-024), so re-import on reconnect is naturally scoped; the assembly
layer calls from_call immediately after connect().
- Forwarding handler captures an Arc<CallConnection> and, on invocation,
calls the remote op and returns its ResponseEnvelope. The
parent_request_id participates in the cross-node abort cascade
(ADR-016 §6) — if the parent is aborted, the cascade reaches this handler
which sends call.aborted to the remote node; cross-node abort is
transparent.
- Trust is transitive (recorded in spec): a from_call-imported op executes
the remote node's code; scoped_env bounds which ops are reachable, not
what they do.
OperationContext.internal is now pub (was pub(crate)) so downstream
consumers (assembly layer, integration tests) can construct contexts for
overlay-env dispatch.
Tests (207 lib + 2 integration):
- Unit: rebuild_spec name/prefix/op_type/visibility/error_schemas/acl;
unknown op_type -> SchemaParse; missing op_type -> SchemaParse;
FromCallConfig builder; from_call against a mock connection returns
DiscoveryFailed (no transport); FromCall provenance + leaf fields + remote_safe false.
- Integration (tests/two_node_call.rs): from_call over a real QUIC loopback
— CallClient connects, from_call discovers server/echo, registers the
bundle in the overlay, and the forwarding handler round-trips an input
through the overlay env to the remote op and back.
clippy + fmt + test all green.
Refs: tasks/call/client/from-call.md
Refs: docs/architecture/decisions/017-call-protocol-client-and-adapter-contract.md §3, §6
Refs: docs/architecture/crates/call/client-and-adapters.md §from_call
Implements the operation context types in registry/context.rs (ADR-015,
ADR-022, ADR-024): OperationContext with all 10 fields (internal is
pub(crate) for writes, read via is_internal()), AbortPolicy enum with
AbortDependents default, CompositionAuthority with synthetic Identity
projection for ACL, ScopedOperationEnv reachability set, and
generate_request_id() (UUID v4). Adds a minimal OperationEnv trait
forward-declaration in registry/env.rs so the context env field compiles;
the operation-env task will expand it.
Create crates/alknet-call with Cargo.toml, lib.rs, and module skeletons
for the registry (spec, context, registration, env, discovery) and
protocol (wire, pending, connection, adapter, abort) subsystems. Add the
crate to the workspace members list. Depends on alknet-core (workspace
path), irpc (workspace dep), tokio, serde, serde_json, async-trait,
tracing, thiserror, uuid, and futures. Implements ProtocolHandler on
ALPN alknet/call per docs/architecture/crates/call.