Parent
Spec #209 (capability-derived tool catalogs; design ticket #191, wayfinder map #182). Immune-system lane; promises Immune system + Permissions; users Bell, Lyra.
What to build
A new kernel-owned per-action tool descriptor and a descriptors map on the ActionCatalog, mirroring the existing egress_declarations axis exactly (a curated kernel const in crates/openspine-kernel/src/action_catalog.rs + supporting types in crates/openspine-schemas/src/action.rs; never shell-spoofable, never carried on the TaskGrant).
Each ToolDescriptor carries:
- the LLM-facing tool name,
- a one-line description,
- a parameter JSON Schema for the action's invocation payload,
- presentation flags:
approval_required, selection_token_required.
Populate a descriptor for every currently-dispatchable action id (the ids the kernel actually mediates through POST /v1/actions). Intentionally-unwired PRD ids (route.activate, workflow.activate, capability_pack.change, policy.change_proposal, connector.enable) need no descriptor — they are not dispatchable and the completeness rule below is scoped to dispatchable actions.
Add a completeness test in the gate (runs under scripts/check.sh, mirroring the existing missing-egress-declaration completeness test in action_catalog_tests.rs) that fails if any granted/dispatchable action lacks a tool descriptor. This is a failing test, not a log line: a granted-but-undescribed action is a capability gap a human must consciously accept (spec owner note A).
No projection function and no wire/HTTP change in this ticket — those are IT2 and IT3.
Acceptance criteria
Blocked by
None (can start immediately).
Parent
Spec #209 (capability-derived tool catalogs; design ticket #191, wayfinder map #182). Immune-system lane; promises Immune system + Permissions; users Bell, Lyra.
What to build
A new kernel-owned per-action tool descriptor and a descriptors map on the
ActionCatalog, mirroring the existingegress_declarationsaxis exactly (a curated kernel const incrates/openspine-kernel/src/action_catalog.rs+ supporting types incrates/openspine-schemas/src/action.rs; never shell-spoofable, never carried on theTaskGrant).Each
ToolDescriptorcarries:approval_required,selection_token_required.Populate a descriptor for every currently-dispatchable action id (the ids the kernel actually mediates through
POST /v1/actions). Intentionally-unwired PRD ids (route.activate,workflow.activate,capability_pack.change,policy.change_proposal,connector.enable) need no descriptor — they are not dispatchable and the completeness rule below is scoped to dispatchable actions.Add a completeness test in the gate (runs under
scripts/check.sh, mirroring the existing missing-egress-declaration completeness test inaction_catalog_tests.rs) that fails if any granted/dispatchable action lacks a tool descriptor. This is a failing test, not a log line: a granted-but-undescribed action is a capability gap a human must consciously accept (spec owner note A).No projection function and no wire/HTTP change in this ticket — those are IT2 and IT3.
Acceptance criteria
ToolDescriptortype exists (name, description, parameter JSON Schema,approval_required,selection_token_required) and round-trips through serde.ActionCataloggains a descriptors map + a builder method mirroringwith_egress_declarations, and an accessor to read a descriptor byActionId.canonical_catalog().compose()orgate()decision logic; no change toTaskViewBodyor the shell.cargo test -p openspine-schemas -p openspine-kernelgreen;scripts/check.shgreen.Blocked by
None (can start immediately).