Skip to content
GitHub
Decisions

ADR-092: read:actions scope — action-registry read access; module approval stays operator-only

ADR-092: read:actions scope — action-registry read access; module approval stays operator-only

Context

ADR-086 D4 carried read:agents forward unchanged from the prior tenancy model as the scope granting action discoverability — the capability a programmatic caller uses to learn which actions a domain exposes. Two precision problems surfaced in the Codex review:

  1. The name names the wrong resource. “Agents” is not what the scope reads. The resource is the domain’s action registry — the registered actions and their input contracts (per the discoverability endpoint, ADR-089). The verb:resource scope shape (ADR-006 §4) calls for the resource the scope actually grants.
  2. ADR-086 D5 deferred a permission-review question — whether admin:domain should automatically grant approve:modules (ADR-080 D3). D5 explicitly left it as “a follow-up Phase 4 implementation concern.”

Both are scope-taxonomy precision issues on the same ADR-086 surface. This ADR resolves them together.

Decision

D1 — read:actions replaces read:agents

The scope is renamed read:actions. It grants read-only access to the bound domain’s action registry: the set of registered actions and their input contracts, sufficient for action discovery — including MCP tool-listing (per ADR-088) and the discoverability endpoint (per ADR-089). The name follows the action:resource shape with actions as the resource actually read. read:agents retires.

D2 — read:actions is a minimal scope, distinct from read:domain

read:actions grants the action registry only — the registered actions and their input contracts — and nothing more: no decision history, no audit records, no other domain-scoped data. It is deliberately narrower than read:domain, which covers reading domain state beyond the registry. A caller that needs only to discover and invoke actions carries read:actions + decide:domain; read:domain is broader than discovery requires and is not granted for it.

D3 — API keys carry read:actions + decide:domain

The complete programmatic-caller scope set is discovery (read:actions) + invocation (decide:domain). API keys carry both, bound to the key’s (org_id, domain_id) pair per ADR-084 D1. Action discovery is a genuine programmatic-caller need (including MCP tool-listing per ADR-088), so keys carry the discovery scope alongside invocation. ADR-086 D4 lists the same key scope set in lockstep.

D4 — Role grants align

ADR-086 D5’s role grants align: the admin and contributor roles carry read:actions (was read:agents); observer is unchanged (read:domain only). Role names and the rest of each role’s grants are unchanged.

D5 — Module approval is operator-only; admin:domain does not grant approve:modules

This resolves the ADR-086 D5 deferred note. approve:modules is the enshrinement gate (ADR-080 D3) — a Spectral-operator authority. No customer role, including the domain admin, carries it: enshrinement is not a customer action, and the customer dashboard cannot approve modules (per ADR-077). approve:modules lives only with the operations role (Spectral staff). admin:domain grants domain administration — membership, API keys, domain settings — and stops there.

Alternatives considered

Keep read:agents. Rejected — it names a non-resource and misleads a reader about what the scope grants.

Drop the scope; have API keys carry decide:domain only. Rejected — programmatic callers and MCP clients genuinely need to discover available actions. The need is real; only the scope’s name and breadth were wrong.

Widen discovery to read:domain. Rejected — over-broad. It pulls in decision history and other domain state a discovery-only caller never needs, violating least privilege.

Defer the admin:domain / approve:modules question again. Rejected — it is a one-line clarification with an unambiguous answer (module approval is operator-only); deferring it a second time leaves the customer role model needlessly ambiguous.

Consequences

  • read:actions is the action-registry read scope (D1) and the admin/contributor role grants carry it (D4); ADR-086 D4/D5 list the aligned scope and role model in lockstep. The ADR-086 D5 deferred permission-review note is resolved here: module approval is operator-only (D5).
  • Codex sweep: read:agentsread:actions across the access-control, API-usage, and authentication pages and the rest of the corpus; the API-key scope set reconciles to read:actions + decide:domain everywhere.
  • approve:agents — the other agents-named scope in ADR-086 D4/D5, marked there as “deprecation status separate” — is not addressed by this ADR. It is tied to the retired Spectral Agent (ADR-078); its cleanup is tracked separately.
  • Scope-literal rename in schema and auth middleware is Phase 4 implementation work, in the same migration epic as ADR-086 D8’s rename scope.
  • The action registry read:actions grants read access to is realized by ADR-104 (worlds.actions + worlds.action_rules); approve:modules (D5) approves modules per declared action once ADR-104 deploys one module per action rather than the single v0 wildcard.