# AI Permissions

> The two independent permission gates for AI surfaces.

_Verified against `app/Http/Middleware/McpAuth.php`, `AiAbilities`, and `app/Services/Agents/*` on 2026-07-23._

---

## Gate 1 — MCP Connector (Flow A)
Defense-in-depth: **both** must permit.

| Layer | Mechanism |
|-------|-----------|
| Bearer-level | Sanctum abilities `numu:read` and `numu:full` (`AiAbilities`). Two connectors: **Read** = `[numu:read]`; **Full** = `[numu:read, numu:full]`. |
| User-level | A matching Spatie permission on the Admin role: **`ai-reader`** (read) vs **`ai-operator`** (full). |
| Transport | `McpAuth:{read|full}` middleware accepts OAuth 2.1 bearer **or** legacy Sanctum; tier enforced by URL. |
| Runtime guard | Per-token circuit breaker (`ai.cb`) → HTTP 423 when tripped. |

## Gate 2 — Managed Agent Runtime (Flow B)
Governance around whether a proposed action executes (orchestrated by `AgentDecisionService` — full flow in [`decisions.md`](decisions.md)):

- **Runtime pause/disable gate** (`AgentRuntimeService`): a global `*` row overrides individual agents; a paused or `is_active=false` agent is blocked before acting (`pause_new_only` semantics; `cancel_queued` / `cancel_running_if_safe` documented as future).
- **Action policy** (`ActionPolicyService::resolve()`): `always_allow` / `needs_approval` / `blocked`, resolved as active runtime **override** → **designed** policy (versioned, environment-scoped, full history) → **default `blocked`**.
- **Approval** (`ApprovalService`): `needs_approval` creates a PENDING `AgentApprovalRequest` (DB-unique: one pending per run; risk defaults HIGH for `reject`); a human decides one-shot.
- **Expected-state validation** (`ExpectedStateValidator`): rejects stale decisions when the entity's group/status changed since the proposal.

## Note deletion (DR-010, DR-011)
- **Admin dashboard:** may create, edit, **and delete** notes (`NotesController`, `admin.auth`-gated). Deletion is a **hard delete** but is **audited before removal** — `ActivityLogger::recordNoteDeletion` writes an `activity_logs` row (`action=note_deleted`, full body snapshot) in the same transaction (DR-011).
- **AI / API / MCP:** create + edit only — **no deletion** (no `note_delete` tool, no DELETE route; `AiRollbackService` refuses to reverse a note-create into a delete).
- Rationale: human administrative control is deliberately broader than autonomous AI permissions.

## Environment enum caveat
Two intentional environment enums coexist: Knowledge/Runtime use `dev/staging/production`; Decision/Policy/Agents use `dev/test/prod`. This is by design, not a bug (see [`../06-history/decisions-log.md`](../06-history/decisions-log.md)).
