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 pure, deterministic, I/O-free projection function in openspine-authority (beside compose), e.g. project_catalog(grant: &TaskGrant, catalog: &ActionCatalog) -> CatalogView, where CatalogView is an ordered list of the tool descriptors (from IT1) the grant permits.
Implement the three-list projection rule exactly:
grant.allowed_actions -> projected as callable tools.
grant.approval_required_actions -> projected, annotated "requires owner approval" (the assistant may propose them; the existing approval/gate flow handles the pause).
grant.denied_actions and any action id not in the grant at all -> structurally absent: no name, no description, no schema is emitted.
The function is policy-free: it projects exactly what compose granted and makes no second authority decision (spec owner note B / D5). It has no I/O and no state, exactly like compose. An allowed/approval-required id whose catalog descriptor is missing is omitted (IT1's completeness test is the backstop that this never silently happens for a dispatchable action).
No wire/HTTP change in this ticket — that is IT3.
Acceptance criteria
Blocked by
IT1 (the ToolDescriptor type and the catalog descriptors map).
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 pure, deterministic, I/O-free projection function in
openspine-authority(besidecompose), e.g.project_catalog(grant: &TaskGrant, catalog: &ActionCatalog) -> CatalogView, whereCatalogViewis an ordered list of the tool descriptors (from IT1) the grant permits.Implement the three-list projection rule exactly:
grant.allowed_actions-> projected as callable tools.grant.approval_required_actions-> projected, annotated "requires owner approval" (the assistant may propose them; the existing approval/gate flow handles the pause).grant.denied_actionsand any action id not in the grant at all -> structurally absent: no name, no description, no schema is emitted.The function is policy-free: it projects exactly what
composegranted and makes no second authority decision (spec owner note B / D5). It has no I/O and no state, exactly likecompose. An allowed/approval-required id whose catalog descriptor is missing is omitted (IT1's completeness test is the backstop that this never silently happens for a dispatchable action).No wire/HTTP change in this ticket — that is IT3.
Acceptance criteria
project_catalogexists as a pure function besidecompose; no I/O, no shared state.worker.commission,email.send) is asserted NOT present anywhere in the projectedCatalogView(no name/description/schema).project_catalog(grant, catalog)is a pure function of its inputs — identical inputs yield identical catalogs regardless of surrounding state; projection introduces no allow/deny the grant did not already carry.compose()decision logic; no change toTaskViewBodyor the shell.cargo test -p openspine-authoritygreen;scripts/check.shgreen.Blocked by
IT1 (the
ToolDescriptortype and the catalog descriptors map).