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

8.4 KiB
Raw Permalink Blame History

name, description, depth, links
name description depth links
Adr 0004 Approval Tier Design Fivetier approval system with grants, timeouts, diff previews 2
../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:

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.