- client module: defines the async OperationAdapter trait (import() -> Result<Vec<HandlerRegistration>, AdapterError>) and the #[non_exhaustive] AdapterError enum (string-message payloads: DiscoveryFailed, SchemaParse, Transport, Unauthorized, Conflict). The trait lives in alknet-call where the types live; implementations live with their transport deps. - from_jsonschema: schema-only registration producing a FromJsonSchema-provenance HandlerRegistration with no real handler (placeholder errors if invoked), None authority/scoped_env, empty capabilities, remote_safe false (ADR-028 §4). Implements OperationAdapter; malformed (non-object) schema returns AdapterError::SchemaParse. No network I/O. - Re-exported from lib.rs. - Tests: trait compiles for Ok and Err adapters; from_jsonschema bundle shape; placeholder handler errors; OperationAdapter import Ok + SchemaParse paths. All 178+N tests pass, clippy + fmt clean. Unblocks alknet-http Phase 1 (from_openapi/from_mcp adapter implementations). Refs: tasks/call/client/operation-adapter-trait.md, tasks/call/client/from-jsonschema.md Refs: docs/architecture/decisions/017-call-protocol-client-and-adapter-contract.md §5 Refs: docs/architecture/crates/call/client-and-adapters.md
5.8 KiB
id, name, status, depends_on, scope, risk, impact, level
| id | name | status | depends_on | scope | risk | impact | level | |
|---|---|---|---|---|---|---|---|---|
| call/client/from-jsonschema | Implement from_jsonschema adapter (schema-only registration, FromJsonSchema provenance, no handler) | completed |
|
narrow | low | isolated | implementation |
Description
Implement from_jsonschema in src/client/from_jsonschema.rs. This is the #4
gap — schema-only registration: produces HandlerRegistration bundles with
no handler (FromJsonSchema provenance). Used for validation, discovery, and
composition-graph construction without a runtime — type-checking a
composition plan without executing it, building a UI of available operations
without standing up the transports, etc.
Distinct from from_call (gap analysis DC-5 — confirmed, not a decision)
from_jsonschema |
from_call |
|
|---|---|---|
| Schema source | Provided directly (caller fetches, passes in) | Discovered over wire (services/list + services/schema) |
| Handler at call time | None (schema-only, FromJsonSchema provenance) |
Forwards over QUIC (FromCall provenance, leaf) |
| Use case | Type validation, discovery, composition graph construction | Actually invoking remote operations |
Keeping them separate preserves the "schema-only, no execution" use case (type checking, safe composition planning without runtime).
API
/// Schema-only registration: produce a HandlerRegistration bundle with
/// FromJsonSchema provenance and no handler. The caller fetches the JSON
/// Schema doc and passes it in; this adapter does no network I/O.
pub fn from_jsonschema(
spec: OperationSpec,
schema: serde_json::Value,
) -> HandlerRegistration;
The bundle:
provenance: FromJsonSchemacomposition_authority: None(no composition — it's schema-only)scoped_env: None(leaf-equivalent — no reachability)capabilities: Capabilities::new()(empty — no outbound credentials, no handler to use them)remote_safe: false(default — ADR-028 §4; provenance-aware default)handler: a placeholder that returns aNOT_FOUND-style orINVALID_INPUT-style error if ever invoked. SinceFromJsonSchemaops areInternal/not-remote-safe by default and have no composition authority, they should never be dispatched; the placeholder makes the type-level constraint hold (theHandlertype requires a closure) and fails loudly if a bug routes a call to it.
OperationAdapter impl
from_jsonschema implements the OperationAdapter trait (from
operation-adapter-trait). Because it does no I/O, the import() body
contains no .await points — it trivially satisfies the async trait.
pub struct FromJsonSchema {
spec: OperationSpec,
schema: serde_json::Value,
}
#[async_trait]
impl OperationAdapter for FromJsonSchema {
async fn import(&self) -> Result<Vec<HandlerRegistration>, AdapterError> {
// No .await — pure parse. Validates schema shape if useful, returns bundle.
Ok(vec![from_jsonschema(self.spec.clone(), self.schema.clone())])
}
}
If the schema is malformed, return AdapterError::SchemaParse.
Why this is standalone (medium priority)
from_jsonschema doesn't depend on CallClient or from_call — it's pure
parse with no transport. It's sequenced after operation-adapter-trait only
because it implements the trait; if the trait lands first, this can proceed
in parallel with call-client/from-call. It's medium priority because the
primary consumers (runner, container service, agent) need from_call, not
from_jsonschema; the schema-only use case is validation/discovery tooling.
Acceptance Criteria
src/client/from_jsonschema.rsexists withfrom_jsonschemafn +FromJsonSchemastructfrom_jsonschemaproduces aHandlerRegistrationwithprovenance: FromJsonSchemacomposition_authority: None,scoped_env: None, emptycapabilitiesremote_safe: false(provenance-aware default, ADR-028 §4)- Handler placeholder returns an error if invoked (no real handler)
FromJsonSchemaimplementsOperationAdapter(async, no .await in import)- Malformed schema returns
AdapterError::SchemaParse - No network I/O (pure parse — caller fetches the doc)
- Unit test: from_jsonschema produces a bundle with correct provenance + None fields
- Unit test: placeholder handler returns error when invoked
- Unit test: OperationAdapter impl returns Ok with one bundle
- Unit test: malformed schema returns SchemaParse error
cargo test -p alknet-callsucceedscargo clippy -p alknet-call --all-targetssucceeds with no warnings
References
- docs/architecture/crates/call/client-and-adapters.md — from_jsonschema §, from_jsonschema vs from_call table
- docs/architecture/decisions/017-call-protocol-client-and-adapter-contract.md — ADR-017 §5 (FromJsonSchema impl)
- docs/architecture/decisions/022-handler-registration-provenance-and-composition-authority.md — ADR-022 (FromJsonSchema provenance, leaf-equivalent None fields)
- docs/architecture/decisions/028-callclient-peer-scoped-registry-filtering.md — ADR-028 §4 (remote_safe default false)
- tasks/call/client/operation-adapter-trait.md — prerequisite (the trait + AdapterError)
- docs/research/alknet-call-completion/gap-analysis.md — DC-5, implementation priority #4
Notes
from_jsonschema is distinct from from_call (DC-5): schema source is provided directly (caller fetches), there's no handler at call time, and the use case is validation/discovery/composition-graph construction without a runtime. It's pure parse with no transport, so it can proceed in parallel with call-client/from-call once the trait lands. The placeholder handler fails loudly if a bug ever routes a call to a schema-only op — they're Internal + not-remote-safe + no composition authority, so dispatch should never reach them.