---
name: security-gate
description: Pre-ship / pre-merge security + correctness gate on a payments or financial module, route, or diff — especially after a self-audit has "converged". Two legs (deterministic complete-mediation oracle + adversarial LLM review) over target AND critical imports; config/validation history in VALIDATION.md. Triggers on "review before merge", "is this payment path safe", "audit this webhook/refund/payout/ledger code", "did the self-review miss anything", "IDOR", "self-deal", "complete mediation", "missing authorization check".
---

# Security-gate

audience: AI coding agents first. Optimize for activation, not prose. Do not prettify into narrative — the ladder IS the tool.

## BLUF — what to do
Run the orchestrator. It is the gate. Do NOT hand-review instead of running it; the gate's value is the two
VALIDATED legs + cross-file scope resolution that a single inline pass does not reproduce.

```
SG_LLM_BACKEND=codex python3 /home/user/Projects/security-gate/orchestrator/gate.py <target-file> \
    --alias '@/=<repo>/apps/web/src' --model gpt-5.6-sol --effort low --k 1 --depth 2 --report <out>.md
```
- `--alias` maps each path alias to its root so imports resolve (read the repo's `tsconfig.json` `paths` first; copy
  the real mapping — do NOT guess). Repeatable. Relative imports need no alias.
- `SG_LLM_BACKEND=codex` + `--model gpt-5.6-sol --effort low` = current operating LLM leg (see "LLM leg config" below).
  `--k 1` = single roll. NOTE: k=1 recall floor was earned on gpt-5.4/low, NOT re-validated on gpt-5.6-sol — raise `--k` if unsure. `--depth 2` = cross-file escalation hops.
- Omit `--config-dir` for real runs (it exists only for blind validation). Read the report; the canonical defect is
  near the top, ranked by severity, tagged with how many rolls found it.

## The two legs — deploy EXACTLY the validated config; never swap it casually
1. **Oracle (deterministic)** — `oracle/oracle2.ts`, run per in-scope file. Catches the complete-mediation /
   identity-collision class ONLY (a guard present for one principal/party but missing on a sibling — e.g. buyer
   ≡ vendor-owner self-deal). This is the ONLY leg that catches that class; no LLM config reliably did. The
   matching review *procedure* is `references/complete-mediation-scan.md` (merged from the former
   complete-mediation-scan skill) — read that, do not re-derive it here.
   - **Cross-file caveat — run it on the file IN ITS REPO.** The oracle resolves imported identity-predicate bodies
     from sibling files on disk (relative imports; NOT tsconfig aliases). It catches C02 only when those siblings are
     present. On an extracted / partial-checkout / aliased copy the predicate is invisible.
   - **No silent false-clean.** If a guard-position callee's import body can't be resolved, the oracle prints
     `unresolved-imports: N>0` + an `UNRESOLVED IMPORTS` block and the gate marks the result **"SILENT but UNRELIABLE
     — do NOT read as clean."** Treat it exactly like an unverified cross-file dependency: run from the full repo /
     fix the alias, never pass. **DO NOT** read a SILENT oracle as clean without confirming `unresolved-imports: 0`.
2. **LLM (stochastic)** — config = **`prompt_v2.txt` (10-pass adversarial payments review),
   backend=codex, model=gpt-5.6-sol, effort=low**, run k=1. Current operating default; NOT yet re-validated on this
   model (validated floor was gpt-5.4/low — see "LLM leg config" below).
   - **DO NOT** substitute the stock `security-guard` agent (a different general prompt) as this leg — it was
     never measured on the baseline-missed set, so its catch rate is unknown. Shipping it = shipping an unproven
     config. Use the measured config above.

## The cross-file rule — REQUIRED, proven (do not skip)
A defect whose fix lives in an imported function is INVISIBLE from the importer. Real case: a webhook's
concurrent-claim TOCTOU was 7 lines in `webhook.ts` but 50 lines in the imported `claimWebhookEvent` — uncatchable
from `webhook.ts` alone. The gate handles this two ways; both are on by default in `gate.py`:
- **Proactive:** pull in imports whose symbol name is critical (`claim|lock|guard|settle|payout|refund|charge|
  transfer|release|ledger|escrow|hold|idempoten|…`).
- **Reactive:** any finding tagged **"unverified cross-file dependency"** is a REQUIRED escalation — resolve the
  named import, pull it into scope, re-run the legs. **DO NOT** treat such a flag as a clean pass.

## LLM leg config (current model gpt-5.6-sol/low — NOT re-validated; k=1 floor earned only on gpt-5.4/low)
Validation history: sonnet/medium k≥3 UNIONED (single-roll recall on the variance-prone cell — config-divergence in
SQL — measured ≈3/4, so 3 rolls closed the hole, (1/4)^3 ≈ 1.6% miss) → **re-validated gpt-5.4/low k=1** 2026-07-02
(sufficient recall on the baseline-missed set, superseded k≥3). **Current default gpt-5.6-sol/low is user-directed and
NOT yet re-validated** — the k=1 recall floor is earned per-config, not inherited across a model swap. Re-validate on
gpt-5.6-sol before trusting k=1; until then raise `--k` (≥3) if a miss is costly, or fall back to the validated
gpt-5.4/low.

## Scope & honest limits — state these, do not overclaim
- **Validated for: payments/financial modules in file/diff review** (the v2 prompt is payments-specific; the
  validation corpus is one codebase). The claim is "catches the baseline-missed set in file/diff review," NOT "in a
  full-repo audit," and NOT "general security across all languages." A general / all-projects gate needs the LLM
  leg's prompt generalized and RE-VALIDATED on non-payments code — that work is not done.
- **Precision is a known gap.** A run emits ~10 findings per file with ~1 being the pinned canonical; paraphrases of
  the same bug may appear twice (semantic dedup is word-overlap only). Triage the list; do not assume one finding =
  one bug. Improving merge precision is tracked as future work in `GATE_DESIGN.md`.
- A CHECKED/SILENT oracle result means "a dominating guard is present," NOT "the guard's logic is correct" — and a
  SILENT result carrying `unresolved-imports: N>0` is UNRELIABLE (predicate bodies were invisible), not clean.

## Done-check
- [ ] Alias(es) copied from the repo's real `tsconfig.json` — imports actually resolve (check the `[scope]` stderr).
- [ ] `--k 1`, `--model gpt-5.6-sol --effort low`, `SG_LLM_BACKEND=codex` set; report shows the per-finding roll count.
- [ ] Every in-scope file got both legs; every "unverified cross-file dependency" was escalated or explained.
- [ ] Report triaged against the limits above — canonical(s) identified, scope/precision caveats stated, not implied.

## Provenance
Design: `~/.claude/security-gate/GATE_DESIGN.md`. Validation (n≥3, git-pinned canonicals, F3 variance, acceptance
test): `~/.claude/security-gate/VALIDATION.md`.
