pending-approvals ยท diff

git:20260915.e888e07 to git:20260924.ad0abaa

28 added, 69 removed. Audit A to A.

---
name: pending-approvals
description: Use when the user asks to list, inspect, approve, reject, or revoke pending approvals
---
# Pending Approvals
- Pending approvals are not automatically resurfaced in later sessions. Recovery
- is explicit: the user asks to list/search pendings or supplies an approval id.
-
- Use the unified CLI and treat the DB as primary:
+ Pending approvals are not resurfaced in later sessions on their own. Recovery
+ is explicit: the user asks to list pendings or supplies an approval id. Every
+ lookup and single-item decision takes the complete canonical
+ `P-<32 lowercase hex>` id; a short label is never a lookup key.
- - `gaia approvals pending` -- the undecided pendings only (all sessions by
- default; `--session <id>` narrows to one orchestrator session)
- - `gaia approvals list` -- the DB-backed grants table plus the undecided
- pendings beneath it; it takes `--session` / `--orphans-only` / `--json` and
- has NO `--status` filter. Filtering by decision lives on
- `gaia approvals history --status <pending|approved|rejected|revoked>`
- (`--limit N`, default 50).
- - `gaia approvals show <approval_id>` -- ordinary human detail; `--json` keeps
- the machine detail shape, while the mutually exclusive `--consent-surface`
- returns trusted `visible_text`, `visible_lines`, `metadata`, and the exact
- resolver-compatible `approve_label` for an undecided pending only
- - `gaia approvals approve <approval_id>`
- - `gaia approvals reject <approval_id>`
- - `gaia approvals revoke <approval_id>`
+ ## Reading
- Every lookup and single-item decision requires the complete canonical
- `P-<32 lowercase hex>` id. A short display label or raw nonce is never a lookup
- key. Full-id lookup may cross session boundaries. Always re-present exact content,
- risk, rollback, and verification before an approve decision; for COMMAND_SET,
- show the full indexed ordered set. Never infer approval from conversational
- language alone or select a similarly prefixed id.
+ - `gaia approvals pending` -- the undecided requests, all sessions by default
+ (`--session <id>` narrows).
+ - `gaia approvals list` -- grants with STATUS, GRANT_STATE and OUTCOME
+ columns, and the undecided requests beneath them with their STATE
+ (`--session`, `--orphans-only` for the orphaned ones, `--json`).
+ - `gaia approvals show <approval_id>` -- one approval: State, Outcome, Reason,
+ Requester, Window, Directory, the grant and the sealed payload.
+ - `gaia approvals history [<approval_id>]` -- recent approvals, or one
+ approval's event chain.
+ - `gaia approvals stats` -- counts by state and outcome.
- **These verbs are not all the orchestrator's to run.** The trusted-CLI
- role guard (`hooks/modules/security/gaia_cli_only_guard.py`) splits them
- along a read/write line, not a T3 line: `approvals list` / `show` / `pending`
- / `history` / `stats` are in `ALLOWED_READ_PHRASES` -- the orchestrator reads
- these directly. `approvals approve` / `revoke` / `reject` / `reject-all` /
- `clean` / `replay` are in `EXPLICITLY_DENIED_PHRASES` -- categorically denied
- for the orchestrator role, not approvable, regardless of mode. **Reads are the
- orchestrator's; decisions are not.** A decision is dispatched to a specialist
- (or, for the CLI-only admin case below, made explicitly by the user) -- never
- run bare by the orchestrator itself.
+ Every reader names an approval by one derived state; `reference.md` lists them.
- **For a SINGULAR approval, `gaia approvals approve <approval_id>` is an admin
- decision-recording verb, not executable activation.** It writes `APPROVED`
- directly to the `approvals` row, but creates **no executable grant** and triggers
- **no automatic re-dispatch**. The originally blocked command therefore remains
- unexecutable from that admin decision alone. When the blocked command still
- needs to run, this verb **MUST NEVER** substitute for the structured decision
- path owned by the active host adapter. Load the adapter skill declared for the
- active host by
- `hooks/adapters/registry.py::registered_adapter_skill_documents` for the
- mechanism it owns; `orchestrator-present-approval` owns the neutral presentation
- and activation contract. Use the bare `approve` CLI verb only for the
- audit/CLI-only case -- for example, marking a row from a different session as
- decided when the command it covers will not be re-run.
+ ## Deciding
- **COMMAND_SET now activates through the same writer from either entry point.**
- For a plan-first `request_type: "COMMAND_SET"` pending (minted by `gaia
- approvals request-set`, which carries a `request_fingerprint`),
- `activate_db_pending_by_id` takes a dedicated branch that calls
- `insert_plan_command_set` -- the identical call the CLI admin verb `gaia
- approvals approve` makes, and the only shape the runtime's execution check
- (`reserve_plan_command`, keyed on `source='plan-first'`) finds. The structured
- decision path and the admin verb therefore activate a `request-set` pending
- identically. The
- legacy `hooks/modules/security/approval_grants.py::create_command_set_grant` route survives only for a multi-command
- payload with NO `request_fingerprint`, a chain-intake shape production no longer
- emits; it is kept as a defensive fallback, not as the plan-first path. The
- earlier defect -- a correctly labeled Approve reporting success while the
- retried commands re-blocked, because every multi-command payload went through
- the legacy route -- is closed.
+ The user decides on the signature Gaia shows (`orchestrator-present-approval`).
+ From a terminal, the user can also run `gaia approvals approve <approval_id>`:
+ it creates the grant exactly as Approve on the question does.
- Legacy filesystem records may still be read as a compatibility fallback for
- older singular approvals; new COMMAND_SET requests and grants are DB-backed.
- See `reference.md` for current precedence, legacy locations, ambiguity handling,
- expiry, and batch rejection rules. Never edit the DB or legacy files directly.
+ The orchestrator withdraws but never approves. It may run
+ `gaia approvals reject <approval_id>` (a pending request becomes `rejected`) and
+ `gaia approvals revoke <approval_id>` (an armed grant can no longer be used) --
+ for example to retire an orphaned request. Approving, replaying, and the bulk
+ verbs (`reject --all`, `reject-all`, `clean`) are the user's, or a specialist's
+ when the user asks for them.
- A COMMAND_SET grant in `FAILED` is not pending or resumable. Show its completed,
- failed, and untouched indexes as history; require a new request and approval for
- all retry/remainder work.
+ A set that ended `failed` is frozen: show its completed, failed and untouched
+ commands as history; anything still needed is a new request.