Files
correx/docs/decisions/adr-0004-approval-tier-design.md
T

145 lines
8.4 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: "Adr 0004 Approval Tier Design"
description: "Fivetier 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, replaysafe approval model that is neither overly intrusive in safe contexts nor permissive in risky ones.
Key requirements:
- tierbased risk classification for all tools and operations
- sessionscoped elevation of trust (modes) and finegrained grants
- firstclass 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 **fivetier approval system** (T0T4) with configurable timeouts, session/stage/projectscoped 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 autoapprove mode | examples |
|------|---------|---------------------------|----------|
| T0 | inference only | auto (always) | generating text, plan, summary |
| T1 | readonly 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 | autoapprove 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 optin required |
Modes can be changed midsession 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 persession debug directory.
### 3. session, stage, and projectscoped grants
Beyond the mode, users may issue finegrained, replayable grants that elevate autoapproval for specific actions or time windows.
| scope | duration | example | event emitted |
|-------|----------|---------|---------------|
| **session** | until session end | "autoapprove all T2 for this session" | `ApprovalSessionGrantAdded` |
| **stage** | until current stage completes | "autoapprove T2 for the rest of this stage" | `ApprovalStageGrantAdded` |
| **project** | persists across sessions (stored in project config) | "always autoapprove `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 autoapprove it within scope). The approval engine evaluates grants in order of specificity: session overrides stage, stage overrides project.
### 4. approval lifecycle (endtoend)
1. An artifact or tool invocation is about to proceed.
2. Validation pipelines approval layer queries `:core:approvals` with the current tier, session mode, and active grants.
3. If the tier is **autoapproved** (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 autoapprove 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 localfirst 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 dryrun 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 (readonly) 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).
- **Multicommand 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 (30s); user must be present. Output is captured as a sidechannel log; not treated as an artifact until the session ends and a receipt is produced.
### 8. replay determinism
During replay, all approval decisions are reapplied 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, finegrained 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 approveeverything or denyeverything 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 autoapprove.
- **perstage approval only (no toollevel):** 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 evaluatormodelbased approvals, batchapprove for multiple tool calls, and richer sandbox isolation. The core tier + grant + preview model is expected to remain stable.