---
name: "Adr 0004 Approval Tier Design"
description: "Five‑tier approval system with grants, timeouts, diff previews"
depth: 2
links: ["../index.md", "../modules/core-approvals-submodule-spec.md"]
---

# ADR 0004: approval tier design with scoped grants, timeouts, and mandatory diff previews

**status:** accepted  
**date:** 07.05.2026 (May)
**deciders:** Kami

---

## context

Correx executes potentially destructive operations (shell commands, file mutations, network calls) based on model proposals. To maintain bounded autonomy and auditability, every such operation must pass through an approval gate. The system needs a configurable, replay‑safe approval model that is neither overly intrusive in safe contexts nor permissive in risky ones.

Key requirements:

- tier‑based risk classification for all tools and operations
- session‑scoped elevation of trust (modes) and fine‑grained grants
- first‑class support for user steering during approval
- timeout handling when the user is not available
- mandatory diff/preview of proposed changes before irreversible actions
- replay determinism without user interaction

## decision

We adopt a **five‑tier approval system** (T0–T4) with configurable timeouts, session/stage/project‑scoped grants, and mandatory diff previews. The approval engine is implemented in `:core:approvals`, called by `:core:validation`, and coordinates with `:core:orchestration` and `:core:sessions`.

### 1. tier definitions

| tier | meaning | default auto‑approve mode | examples |
|------|---------|---------------------------|----------|
| T0 | inference only | auto (always) | generating text, plan, summary |
| T1 | read‑only tools | auto (but mode can restrict) | `ls`, `cat`, `git status`, `grep` within allowlisted paths |
| T2 | reversible mutation | prompt | `git commit`, file edits inside versioned project, `pip install` in venv |
| T3 | external/network | prompt or deny (configurable) | `curl`, `git push`, `npm publish`, remote API calls |
| T4 | destructive | deny | `rm -rf`, database drop, force push to main |

Every tool and stage declares its required tier. The highest tier among a chain (e.g., a pipe involving `curl`) determines the approval requirement.

### 2. session modes

Session mode sets the maximum tier that is automatically approved without user interaction.

| mode | auto‑approve up to | use case |
|------|-------------------|----------|
| `deny` | nothing (prompt for T0+) | maximum safety, untrusted instructions |
| `prompt` | T1 | normal daily use (default) |
| `auto` | T2 | trusted, focused development session |
| `yolo` | T4 (all) | testing, debugging; heavily logged, explicit opt‑in required |

Modes can be changed mid‑session via `/mode <name>`, which emits an `ApprovalModeChanged` event. Yolo mode requires a confirmation prompt and enables verbose execution logging (full stdin/stdout/stderr, timing, scrubbed env vars) stored in a per‑session debug directory.

### 3. session, stage, and project‑scoped grants

Beyond the mode, users may issue fine‑grained, replayable grants that elevate auto‑approval for specific actions or time windows.

| scope | duration | example | event emitted |
|-------|----------|---------|---------------|
| **session** | until session end | "auto‑approve all T2 for this session" | `ApprovalSessionGrantAdded` |
| **stage** | until current stage completes | "auto‑approve T2 for the rest of this stage" | `ApprovalStageGrantAdded` |
| **project** | persists across sessions (stored in project config) | "always auto‑approve `git commit` in this repo" | `GrantLoadedFromProject` (on load) |

Grants are additive; they cannot lower the effective tier of a tool (a T3 tool remains T3, but a grant may auto‑approve it within scope). The approval engine evaluates grants in order of specificity: session overrides stage, stage overrides project.

### 4. approval lifecycle (end‑to‑end)

1. An artifact or tool invocation is about to proceed.
2. Validation pipeline’s approval layer queries `:core:approvals` with the current tier, session mode, and active grants.
3. If the tier is **auto‑approved** (by mode or grant), an `ApprovalGrantedAuto` event is recorded; execution proceeds immediately.
4. If the tier requires **user prompting**, an `ApprovalRequired` event is emitted. The session enters `AWAITING_APPROVAL` and orchestration suspends.
5. Router presents the approval prompt, including any generated diff/preview.
6. User responds with `approve` (optionally with steering text), `reject`, or an auto‑approve grant. The response is recorded as `ApprovalGranted` or `ApprovalRejected`.
7. If approved and steering text is present, it is captured verbatim and injected into the next context pack as a `UserSteering` entry.
8. Orchestration resumes (or transitions to recovery on rejection).

### 5. approval timeouts

Default timeout behavior for all tiers is **pause indefinitely** (`AWAITING_APPROVAL` persists until user response). For local‑first safety, this ensures no action is taken if the user steps away.

Users may override timeout actions per tier in global or project config:

```yaml
approvals:
  timeouts:
    T0: auto
    T1: auto
    T2: pause
    T3: reject
    T4: reject
```

Supported timeout actions: `reject`, `pause`, `auto_deny` (records rejection, does not pause). If a timeout fires, the corresponding decision event is recorded.

### 6. diff/preview mandatory for T2+

Before any T2+ operation is approved, the user must see exactly what will happen.

- **File mutations (filesystem tool):** a unified diff of proposed changes. Generated by writing the proposed content to a temporary file and computing a `git diff` against the original (using the real file or a temporary copy). The diff is included in the approval prompt.
- **Shell commands:** the exact command string, plus if the command is known to modify files (e.g., `rm`, `mv`), a list of affected paths via dry‑run inspection.
- **Git operations:** the diff of working tree changes or commit message/patch.
- **Scripts (bash, python):** the full script content is shown as a preview.

Tools that cannot produce a preview are capped at T1 (read‑only) and may not mutate state.

### 7. nested/chained commands

- **Simple pipes** (`cat | grep | awk`): a single `ToolInvocationRequested` event; the entire pipeline inherits the highest risk tier of its components (e.g., if any part reaches network, T3).
- **Multi‑command scripts:** executed as a single tool invocation; tier determined by tool definition; preview shows the full script content.
- **Interactive sessions** (`python -i`, `bash -i`): treated as T3 minimum; short default timeout (30 s); user must be present. Output is captured as a side‑channel log; not treated as an artifact until the session ends and a receipt is produced.

### 8. replay determinism

During replay, all approval decisions are re‑applied from the event log. No user interaction is required. If a session is replayed in a mode different from the original (e.g., yolo → prompt), replay still uses the recorded decisions; the mode change only affects future new events.

## consequences

**positive:**

- transparent, auditable, fine‑grained control over risk
- mandatory diff previews prevent blind execution of destructive commands
- session/stage/project grants balance safety with usability
- steering injection turns human approval into active guidance for models
- replay works without user presence, preserving audit integrity
- yolo mode enables efficient testing with full debug logs

**negative:**

- more complex orchestration (suspension/resumption, timeout enforcement) compared to a simpler approve‑everything or deny‑everything model
- mandatory diff previews impose a requirement on tool implementations (must support preview interface); tool authors bear a small additional burden
- interactive session handling is intentionally limited in v1

## alternatives considered

- **binary approve/deny without tiers:** too coarse; some operations are inherently safe and should not require user presence; others must never auto‑approve.
- **per‑stage approval only (no tool‑level):** insufficient granularity; a single stage may run multiple independent risky commands.
- **no yolo mode:** slows down development and testing; rejected.

## status

This decision defines the v1 approval subsystem. Future extensions may add evaluator‑model‑based approvals, batch‑approve for multiple tool calls, and richer sandbox isolation. The core tier + grant + preview model is expected to remain stable.