Implements the 4-task DAG for runtime-spawned resource ownership so
AccessControl::check can answer "does this identity own this specific
container/TTY/process" instead of relying only on static
Identity.resources grants.
1. core/ownership-store-trait: OwnershipProvider (sync read) +
OwnershipStore (async write) traits + InMemoryOwnershipStore +
OwnershipError in alknet-core. Fourth instance of the repo/adapter
pattern (ADR-033), mirroring CredentialStore/store.rs.
2. call/registry/operation-spec-resource-id-path: resource_id_path
field on OperationSpec — JSON pointer into input for resource ID
extraction. Single field addition, all ~40 construction sites across
alknet-call + alknet-http updated to pass None (no semantic change).
3. call/registry/access-control-ownership-check: check() signature
gains resource_id + Option<&dyn OwnershipProvider>. 3-case decision
tree: ownership Some + resource_id Some -> owns(); ownership Some +
resource_id None -> owns_any() (list scope-gate); ownership None ->
static Identity.resources fallback (backward compat). 7 call sites
updated to (None, None) — including a 7th in alknet-http/gateway_routes
not listed in the original spec.
4. call/registry/dispatch-resource-id-extraction: wire the dispatch
path. OperationContext gains ownership field; Dispatcher/CallAdapter
gain with_ownership_provider builders; invoke/invoke_streaming/
invoke_with_policy extract resource_id via spec.resource_id_path
and thread context.ownership to check(). extract_json_pointer helper
handles $.field syntax (graceful None on missing/non-string). 20
OperationContext literals updated across both crates.
Backward compatibility is load-bearing throughout: ownership=None
falls back to the existing static resource-check path. Deployments
without runtime-spawned resources wire nothing and behave identically.
821 tests pass workspace-wide (was ~770); clippy clean; fmt clean.
Task specs marked done.
Four tasks forming a DAG for the dynamic resource ownership model (ADR-050):
1. core/ownership-store-trait (no deps) — OwnershipProvider (sync read) +
OwnershipStore (async write) traits + InMemoryOwnershipStore + OwnershipError
in alknet-core; fourth instance of the repo/adapter pattern (ADR-033)
2. call/registry/operation-spec-resource-id-path (no deps) — add
resource_id_path: Option<String> to OperationSpec (JSON pointer into
input for resource ID extraction)
3. call/registry/access-control-ownership-check (depends on 1) — update
AccessControl::check signature to accept resource_id + OwnershipProvider;
backward compatible (ownership=None falls back to static Identity.resources)
4. call/registry/dispatch-resource-id-extraction (depends on 2, 3) — wire
dispatch path to extract resource_id from input via spec.resource_id_path
and thread OwnershipProvider to check(); OperationContext gains ownership
field
Tasks 1 and 2 can run in parallel (different crates, no deps). Task 3
depends on 1. Task 4 depends on 2 and 3. Validated: no cycles, 90 tasks total.