# Approval Rescue Loop

> Defaults (2026): the approver is **allow-by-default** — it auto-approves
> everything except the dangerous-command blocklist (the denylist, ON by default,
> editable in the Permission Manager). The LLM verifier is opt-in (off by
> default) and provider-agnostic via `callBackgroundLlm`; auto-approvals send the
> one-time "yes" (never durable allow-all). See `approval-ai-refinement.md`.

CTM has two approval layers:

1. The deterministic approval pipeline is the source of truth. The headless terminal detects provider-specific approval widgets, structural gates reject stale/non-widget text, and `approval-agent.js` decides whether to approve, escalate, or block.
2. The rescue loop is a bounded observer for misses. It only runs when the deterministic pipeline sees approval-shaped text but rejects it before policy can act, such as a `gate-miss` from the headless terminal.

The rescue loop must not replace provider parsers. Its job is to recover one missed prompt, verify whether the intervention worked, and create durable evidence so the product can improve the deterministic path.

## Flow

1. Headless terminal emits a normal `approval-alert` when a prompt passes structural validation.
2. If detection sees approval-shaped text but structural validation rejects it, the worker emits `gate-miss` with a redacted/capped screen tail, provider id when known, and the gate reason.
3. Server records the observation, then asks `approval-agent` to evaluate a rescue candidate.
4. The approval agent:
   - parses the prompt using the provider parser, with a conservative generic numbered-approval fallback for unknown providers;
   - checks the dangerous-command blocklist and normal heuristics;
   - asks the background LLM, when available, whether this is an active approval prompt that should be tried once;
   - sends only the one-time approval shortcut, never durable allow-all, for rescue attempts;
   - verifies the result by checking output movement and whether the prompt disappeared.
5. The rescue record is updated as success, failure, skipped stale, suppressed, or promoted.

## Suppression

Rescue is intentionally stingy:

- A fingerprint covers provider, normalized command/context, and the gate reason.
- The same fingerprint is not retried during a cooldown.
- Repeated failures suppress future attempts for that fingerprint.
- User warnings are throttled per fingerprint, so one bad pattern does not flood the UI.
- A skipped stale prompt is recorded but not warned, because it usually means the user or provider already moved on.

## Rule Promotion

A successful rescue does not automatically create a broad approval rule.

After success, the agent classifies why the deterministic path missed it:

- `structural_gate_miss`, `parser_bug`, or `race`: record the evidence and keep the deterministic code path as the fix target.
- `new_provider_pattern`: promote the exact rescue fingerprint into a deterministic rescue rule for future matching and telemetry.

The promotion decision is deterministic. If a known provider parser already detected the prompt and a structural gate rejected it, CTM records the rescue as `structural_gate_miss` even when the LLM labels it `new_provider_pattern`. The LLM can decide whether a one-shot rescue is safe; it cannot promote a known-provider gate bug into a durable rule.

Promotion is still bounded to the exact fingerprint. Shipping broader provider support requires adding or updating the provider parser, then covering it with regression tests.

## Telemetry Contract

The local DB stores `approval_rescue_patterns` rows with:

- fingerprint, provider, source, and gate reason;
- attempts, successes, failures, and consecutive failures;
- last decision, outcome, diagnosis, and cooldown;
- promoted rule metadata and last warning time.

This gives operators enough evidence to answer:

- Did CTM miss an approval?
- Did the rescue attempt work?
- Was the miss a new provider pattern or a bug in an existing detector/gate?
- Is CTM repeatedly trying and failing on the same pattern?

## Provider Guidance

New coding agents should add a provider parser under `providers/` first. The rescue loop can help discover a new shape, but a provider is considered supported only after:

- parser detection and parse tests pass;
- headless terminal structural validation accepts the live widget;
- rescue telemetry is no longer needed for the common prompt shape.
