docs(arch): call-completion — ADR-028 peer-scoped filtering + client-and-adapters spec + tasks
Resolves the four gap-analysis decisions (DC-1..4) blocking the alknet-call client/adapter surface specced in ADR-017: - ADR-028 (new): locks the one-way door for DC-1 — CallClient registry is default-deny (remote_safe: bool on HandlerRegistration, default false across all provenance); share-global is an explicit trusted-peer opt-in; filtering is a dispatch-time read over the single Layer-0 registry, not a copy. - client-and-adapters.md (new spec): operationally fills the gap ADR-017 left to implementation — CallClient, from_call, from_jsonschema, OperationAdapter trait, adapter location map, no-env-vars invariant, exchange-of-operations pattern. Keeps call-protocol.md and operation-registry.md under the 700-line split threshold. - ADR-017 amended: records DC-2/3/4 v1 defaults (auto-on-reconnect, error-on-collision, Result error type) and points DC-1 at ADR-028. - OQ-25..28 (new): two-way-door remainders (remote_safe shape, AdapterError variants, re-import trigger, namespace collision) with v1 defaults recorded. - Index/cross-ref updates across READMEs and the two existing call specs. Tasks: 6 task files under tasks/call/ decomposing the completion work along the gap-analysis priority order — remote-safe-marking (one-way door, first) → call-client (phase-risk) → from-call → operation-adapter-trait → from-jsonschema (parallel with call-client) → review-completion. Graph validated with taskgraph; parallelism designed in (from-jsonschema runs concurrent with call-client/from-call once the trait lands).
This commit is contained in:
124
tasks/call/client/from-jsonschema.md
Normal file
124
tasks/call/client/from-jsonschema.md
Normal file
@@ -0,0 +1,124 @@
|
||||
---
|
||||
id: call/client/from-jsonschema
|
||||
name: Implement from_jsonschema adapter (schema-only registration, FromJsonSchema provenance, no handler)
|
||||
status: pending
|
||||
depends_on: [call/client/operation-adapter-trait]
|
||||
scope: narrow
|
||||
risk: low
|
||||
impact: isolated
|
||||
level: 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
|
||||
|
||||
```rust
|
||||
/// 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: FromJsonSchema`
|
||||
- `composition_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 a `NOT_FOUND`-style or
|
||||
`INVALID_INPUT`-style error if ever invoked. Since `FromJsonSchema` ops are
|
||||
`Internal`/not-remote-safe by default and have no composition authority, they
|
||||
should never be dispatched; the placeholder makes the type-level constraint
|
||||
hold (the `Handler` type 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.
|
||||
|
||||
```rust
|
||||
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.rs` exists with `from_jsonschema` fn + `FromJsonSchema` struct
|
||||
- [ ] `from_jsonschema` produces a `HandlerRegistration` with `provenance: FromJsonSchema`
|
||||
- [ ] `composition_authority: None`, `scoped_env: None`, empty `capabilities`
|
||||
- [ ] `remote_safe: false` (provenance-aware default, ADR-028 §4)
|
||||
- [ ] Handler placeholder returns an error if invoked (no real handler)
|
||||
- [ ] `FromJsonSchema` implements `OperationAdapter` (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-call` succeeds
|
||||
- [ ] `cargo clippy -p alknet-call --all-targets` succeeds 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.
|
||||
Reference in New Issue
Block a user