Approval Engine¶
Callback-based approval flow for gating destructive or sensitive operations. Part of the unified CodeGraphHarness service harness.
Overview¶
The approval engine decides whether operations (autofix application, pattern fixes, command execution) require explicit user confirmation before proceeding. It supports three policies:
| Policy | Behavior |
|---|---|
auto |
Auto-approve whitelisted actions, prompt for others |
ask_always |
Require approval for all actions |
read_only |
Decline all write operations automatically |
Architecture¶
User/CLI/IDE ←→ ApprovalEngine ←→ Harness ←→ Analysis Modules
│
┌────┴────┐
│ Policies │
├──────────┤
│ auto │ ← Check whitelist, then prompt
│ ask │ ← Always prompt
│ read_only│ ← Always decline writes
└──────────┘
API Reference¶
ApprovalRequest¶
@dataclass
class ApprovalRequest:
id: str # Unique request ID (UUID)
thread_id: str # Session thread ID
action_type: str # e.g., 'apply_autofix', 'command_execution'
details: Dict[str, Any] # Arbitrary action details
status: str # 'pending' | 'accepted' | 'declined' | 'cancelled'
ApprovalDecision¶
@dataclass
class ApprovalDecision:
decision: str # 'accept' | 'accept_for_session' | 'decline' | 'cancel'
reason: Optional[str] # Optional reason text
scope: Optional[str] # Scope for 'accept_for_session'
ApprovalEngine¶
class ApprovalEngine:
def __init__(self, config: HarnessConfig):
"""Initialize with harness configuration."""
async def request_approval(
self,
action_type: str,
details: Dict,
thread_id: str,
) -> ApprovalDecision:
"""
Request approval for an action. Blocks until resolved.
Auto-approves if action_type is in the whitelist or was
previously approved for this session.
Args:
action_type: Type of action (e.g., 'apply_autofix')
details: Action context (file, line, description)
thread_id: Current session thread
Returns:
ApprovalDecision with the user's decision
"""
def resolve_approval(self, request_id: str, decision: ApprovalDecision) -> None:
"""
Resolve a pending approval request (called by UI/CLI).
If decision is 'accept_for_session', all future requests of the
same action_type in this thread are auto-approved.
"""
def get_pending_request_ids(self) -> list:
"""Return IDs of all pending approval requests."""
def clear_session_approvals(self, thread_id: str) -> None:
"""Clear session-scoped auto-approvals on session end."""
Configuration¶
# config.yaml
harness:
session_dir: ./data/sessions
max_threads: 100
thread_ttl_hours: 168 # 7 days
approval:
default_policy: auto # auto | ask_always | read_only
auto_approve: # Actions auto-approved (no prompt)
- codegraph_query
- codegraph_find_callers
- codegraph_find_callees
- codegraph_explain
require_approval: # Actions that always require approval
- apply_autofix
- apply_pattern_fix
- command_execution
Usage¶
Request Approval¶
from src.harness import get_harness
harness = get_harness()
decision = await harness.approval.request_approval(
action_type="apply_autofix",
details={
"file": "main.c",
"line": 42,
"fix": "Replace strcpy with strncpy",
"cwe": "CWE-120"
},
thread_id="session_abc"
)
if decision.decision == "accept":
apply_fix(...)
elif decision.decision == "decline":
log(f"Fix rejected: {decision.reason}")
Resolve from UI¶
# Called by CLI/TUI/IDE when user makes a decision
harness.approval.resolve_approval(
request_id="req_abc123",
decision=ApprovalDecision(decision="accept")
)
Session-Scoped Approval¶
When a user selects accept_for_session, all subsequent requests of the same action_type within the same thread are auto-approved:
# User approves once for the whole session
decision = ApprovalDecision(decision="accept_for_session")
harness.approval.resolve_approval(request_id, decision)
# Subsequent autofix requests auto-approved (no prompt)
decision = await harness.approval.request_approval(
action_type="apply_autofix", # Same type → auto-approved
details={...},
thread_id="session_abc" # Same thread
)
# decision.decision == "accept" (automatic)
Decision Flow¶
request_approval(action_type, details, thread_id)
│
├─ Is action_type in auto_approve list?
│ └─ YES → return accept immediately
│
├─ Is action_type in session_approvals[thread_id]?
│ └─ YES → return accept (session-scoped)
│
├─ Is default_policy == read_only?
│ └─ YES → return decline("read_only policy")
│
└─ Create pending request, await user decision
├─ accept → apply action
├─ accept_for_session → apply + remember for session
├─ decline → skip action
└─ cancel → abort operation
Default Action Classification¶
| Auto-Approved (read-only) | Requires Approval (write) |
|---|---|
codegraph_query |
apply_autofix |
codegraph_find_callers |
apply_pattern_fix |
codegraph_find_callees |
command_execution |
codegraph_explain |
Integration Points¶
The approval engine is used by:
- Autofix (src/analysis/autofix/) — applying security fixes to source code
- Pattern fixes (src/analysis/patterns/) — applying structural pattern rewrites
- File editing (src/editing/) — AST-based code modifications
- MCP tools — write operations via MCP server
- ACP sessions — IDE-initiated operations
Related Documentation¶
- Security Reference — autofix and security analysis
- Workflows — workflow scenario integration
- REST API — API endpoints for approval
- ACP Integration — IDE approval flow
Module: src/harness/
Last updated: February 2026