# Security-Gate — Session Handoff

> audience: AI coding agents first. **Forward-only**: the goal, the philosophy, the doctrine, and the next task. No session-history, no error log — only what the next session needs to do the work right.

**For:** a fresh agent continuing the security-gate project.
**Repo:** `/home/user/Projects/security-gate` (branch `master`).
**SoT ledger:** `~/.claude/projects/-home-user/memory/project_security_gate.md` — deep build-history. This handoff is **self-contained** for goal + philosophy + doctrine + next task; read the SoT only when you need provenance for a specific already-built piece.

---

## THE GOAL — what we are actually building

The **best security-gate possible**: a tool that catches **100% of real bugs in ONE run, with no dice-roll.** A "real catch" = the bug is **fixed**, not merely flagged.

- **Two-way protection.**
  - **PREVENT** — pre-edit / pre-commit: block bad code *before it lands*, deterministically, 100% accuracy, no false positives.
  - **DETECT** — post-commit scan: find bugs already in the code.
- **Hybrid, deterministic-first.** One deterministic sweep catches everything structurally expressible; cheap LLM passes handle only the elusive ~1% (e.g. C02-class self-deal) that genuinely needs reasoning. LLM may also *write* the deterministic scans.
- **One orchestrator, surface-routed — NOT "one prompt to rule them all", NOT roll-the-dice.** An opus orchestrator builds or reuses an **attack-surface map**, then dispatches cheap subagents, each given the **right prompt for that surface type**, hunting the right bugs in the right places. One pass, correct routing.
- **Coverage roadmap:** proven on the 17-cell corpus (done) → find **ALL** bugs from the **fable audit** in one round → then the other audits we already have → then **live testing on real projects**. Expand beyond payments to every attack surface, same discipline: *map → delegate*.

## THE PHILOSOPHY — carry into every decision

- **A security tool with finance as ONE domain — not a finance tool.** Think in our terms, never slip back into fable-audit / payments framing.
- **Modular, pluggable, re-swappable: adapters + primitives, designed to fit OUR needs.** Reuse over rewrite. Do **not** clone slopgate's monolithic engine — build the abstraction this project needs. Structured, never a spaghetti monster.
- **Testing-native vocab, our own rules** (this is a testing project, not software architecture): `detector / domain / corpus / cell / canary / band / oracle / roll / recall`; `adapter` is licensed. **NEVER** `module / package / layer / tier / seam / plugin`.
- **Deterministic / programmatic preferred; LLM = backup.**
- **no-false-clean.** Degraded input never reports "clean"; the gate states its own coverage. A real catch is a *fix*, not a *flag*.

## ADAPTERS — the building blocks (what each does)

The testing-native vocabulary IS the system's parts list. Each term names a pluggable, re-swappable building block; learn these before touching anything. Canonical source: `docs/specs/2026-06-17-security-gate-design.md` §1; this only summarizes.

- **detector** — the swappable scan unit that flags one defect class. A drop-in dir + `detector.json` manifest, selected by id (`gate.py --detector <id>`, default `baseline`). Declares its **contract** (scope/globs, cross-file need, supported trigger `pre-edit|pre-commit`, severity — `prevent/contract.py`). Ships **only** when its conformance suite is green (flags every canary, ignores every anti-canary, survives every discriminator).
- **domain** — a test area the gate covers. Security is v1; **finance is one domain under it** (`domains/security/`, `domains/security/detectors/finance/`) — a security tool *with* a finance domain, never a finance tool. Each domain owns its taxonomy + corpus + detectors.
- **corpus** — the measured collection of cells for a domain (`domains/security/corpus/`; 17 LLM cells, classes S1–S11). **Coverage is the product**: a class with 0 cells / unmeasured recall is a flagged **BLIND SPOT** (`docs/taxonomy/security.md`), never "covered."
- **cell** — one test case = a real reconstructed defect. **vuln cell** = `git show <fix>~1:<path>` (buggy), **safe cell** = `git show <fix>:<path>` (fixed); the canonical defect is recorded in `canonical.json`. Ground truth = git **FIX COMMITS**, reconstructed with **RAW git only** (`command git --no-pager show`).
- **canary / anti-canary / discriminator** — the admission gate. **canary** = a planted known-defect fixture the detector MUST flag; **anti-canary** = a safe fixture it must leave alone; **discriminator** = a near-miss that separates a right-reason catch from a trigger-happy one (e.g. the fixed `safe.ts` must NOT re-raise the canonical).
- **band** — the 3-strata detector ladder (route a bench-found miss to a band by judgment; most → band-2, *not* a new band-3):
  - **band-1** always-on, domain-agnostic LLM pass (`baseline` prompt, S1–S11 breadth).
  - **band-2** orchestrated-LLM — cross-file scope + k≥3 union + sub-prompts (`orchestrator/gate.py` + `orchestrator/resolver.py`). Carries the hard-but-not-decidable classes.
  - **band-3** deterministic — narrow **decidable** invariants only (oracle / deps / headers). Build one **only if the delete-test passes**: deleting it loses a catch the LLM bands cannot recover (building SQLi/IDOR detectors is YAGNI — the LLM catches them).
- **oracle** — the first band-3 detector (`domains/security/detectors/oracle/`, TS): decides the self-deal / complete-mediation invariant. Domain-blind; its boundary is a field-name lexicon + `key:value` sink form → on off-lexicon names it must report **UNRELIABLE**, never a false clean.
- **roll** — one LLM sample at a given (model, effort). LLM bands are measured **statistically — k rolls, recall as a rate, NEVER a single-run claim.** k=1 = production (the per-target Opus orchestrator); k≥3 union = the flakiness audit.
- **recall** — the measured catch rate: fraction of cells whose canonical defect is caught **right-reason** (names symbol + why + class). Measured by `bench.py` / `run_bench`. "flagged ≠ caught"; "a real catch is a bug fixed."
- **adapter** *(licensed design noun)* — a pluggable implementation behind a stable contract; swap the impl without touching callers. Today: the LLM-dispatch adapter (`orchestrator/llm_runner.py`, claude-cli backend). Planned (the FIRST TASK): **solution adapters** (fix strategies) + **prevention adapters** (programmatic / LLM-rule). NOT `port-adapter` (banned).

## THE DOCTRINE — how we work (learned here; each rule prevented real rework)

- **No dice-roll.** Never re-roll until it passes. The lever is a **better prompt**, not more rolls and not a heavier model.
- **Incremental escalation.** Climb one rung at a time: `sonnet-med → sonnet-high → opus-med → opus-high`. First rung that catches = the capability threshold. If **all** fail → it's a **prompt/scope** problem, not a capability one. Never jump floor→ceiling (med → ultrathink / opus-xhigh).
- **Pilot before fan-out.** Run ONE, read the result, improve, *then* scale. Never blind-batch experiments.
- **Blind / isolated testing.** Test detection where it cannot "cheat" (no memory holding the answer): a clean `/tmp` config dir or `~/.claude2`. **Right-reason bar:** a catch must name THIS canonical defect (symbol + why + class), not something nearby.
- **Foreground only.** Never start background work without explicit approval. The user runs it in the foreground and hits Ctrl+B if they want it backgrounded — *that* is the approval.
- **Learn from the discovery moment.** When a bug was found, read what the discovering agent actually wrote/looked at, and reverse-engineer that move into a deterministic check or a prompt rule. (The C02 self-deal was found by a **generic** prompt to an opus-xhigh agent — not a magic prompt, not "fable". Detection must become deterministic/blind, not lucky.)
- **Default agent-led.** Prep + recommend + proceed. Do not park work behind invented "user gates"; escalate only a genuine one-way-door decision, and bring a recommendation even then.
- **The ratchet.** A confirmed find becomes a permanent programmatic prevention so it can never return.
- **advisor** before substantive work / before committing to an interpretation / before declaring done. **Auto-commit** completed work, terse, **never co-author**. Implementation on a **worktree**, never bare master. Reconstruct git cells with **RAW git** (`command git --no-pager show`) — routed `git show` is silently truncated.

---

## ⭐ FIRST TASK — the "100%" bug registry + modular scan primitives

The end-state deliverable: a single source-of-truth **registry** of "the 100%" — every protected bug/class/cell with its full lifecycle, every field backed by real, wired, pluggable code, built on a *small* set of **security-scan primitives** so that adding the next bug means *register adapters, never rewrite the engine*.

> **Do NOT blind-build.** This is a designed change: `brainstorm → plan → sdd(ship)`, with **advisor on the primitive/adapter contracts before locking them** (contracts are expensive to change once entries depend on them). The brainstorm **deletion test + single-adapter test** are the guardrails against over-abstraction: a field earns its own primitive only if **≥2** concrete adapters need it; otherwise it stays plain data. This is how we keep it structured and avoid the spaghetti monster.

### Per-entry schema — the hierarchy (one row per bug/class/cell)
1. **Discovered** — provenance: *when*, *how*, *by whom/what* (which audit — zync / fable / 1337 / …; which commit; which agent or human; the original finding). Data + link to the corpus cell.
2. **Re-discovery guarantee** ("ensure we find it again") — the **detector(s)** that re-catch it + the **band** (band-1 LLM roll / band-2 cross-file / band-3 deterministic). Links to the cell + measured recall.
3. **Resolution** ("how to resolve") — possibly **multiple ways = solution adapters** (pluggable, re-swappable fix strategies; e.g. add tenant predicate / parameterize query / escape interpolation).
4. **Prevention** ("never happens again") — two adapters per entry:
   - **Preferred: Programmatic** — a deterministic PREVENT-band detector bound to a **surface** + the **ratchet**.
   - **Backup: LLM rule** — a coding-agent rule (learned rule / CLAUDE.md or skill directive) for classes not yet structurally expressible.

### Surfaces to wire (reuse, don't rebuild)
- **post-write / pre-edit** + **pre-commit** → existing PREVENT band (`prevent/`).
- **post-commit scan** → DETECT / audit side (`orchestrator/detect.py`, `orchestrator/mapper.py`).
- Each prevention adapter **declares** its surface(s) via the existing detector contract (`prevent/contract.py` already carries `supported trigger pre-edit|pre-commit` + scope/globs/cross-file/severity) — **extend the contract, don't fork it.**

### Primitives question (the core design work)
Decide the **minimum** set of security-scan primitives so the registry is fully modular + re-swappable. Candidates: **Detector** (exists), **SolutionAdapter** (new), **PreventionAdapter** (programmatic-or-LLM, surface-bound), **ProvenanceRecord**, **RegistryEntry** + **Registry index**. For EACH schema field ask "does this reserve its own primitive?" via the single-adapter test. Reuse the detector contract + ratchet + path-load convention. **Prove the "add a bug = register, don't rewrite" property end-to-end on ≥1 existing cell** (e.g. S4 SQLi).

### Open design question the user raised
Build the hot-path PREVENT hook in **Rust from the start** (vs Python now, port later when it's too big/slow for a pre-write hook)? Decide this inside the primitive design, not as an afterthought.

---

## WHERE WE ARE (built + measured)

- **17-cell corpus** at `domains/security/corpus/`, classes **S1–S11**. Bands: band-1 LLM rolls (`orchestrator/gate.py`), band-2 cross-file resolver (`orchestrator/resolver.py`), band-3 deterministic (oracle / deps / headers).
- **DETECT orchestrator** (`orchestrator/detect.py`, `run_bench`) + **attack-surface mapper** (`orchestrator/mapper.py`, git-driven enumerate → prioritize → dispatch).
- **PREVENT band** (`prevent/`) — deterministic no-LLM pre-edit/pre-commit BLOCK gate, ratchet-linked, e2e-locked (`tests/test_prevent_e2e.py`).
- **LLM dispatch** factored to `orchestrator/llm_runner.py` (the `adapter` boundary — backend-parametrizable, claude-cli today).
- **Measured recall** (LLM bands are measured **statistically — never a single-run claim**):
  - **Production = k=1 per target → ≈86% right-reason.** 7 of 17 cells sit in a 2/3-flaky tail (~14% k=1 gap, what the unbuilt Opus orchestrator addresses).
  - **k=3 union (flakiness-audit config) → 17/17 right-reason**, AND passes the `safe.ts` discriminator 17/17 (canonical not re-raised on the fixed file). Evidence: `docs/validation/2026-06-18-baseline-generalization-recall.md`. One fresh k=1 pass also scored 17/17 (`docs/validation/2026-06-19-runbench-17cell-recall.md`) — a point estimate, not the rate.
  - **HONEST scope:** measured on the **17-cell (zync-derived) corpus** only — **NOT** a cross-audit aggregate (fable / 1337 audits not yet covered), **NOT** a durability cert. The ~14% flaky tail is distinct from the deferred elusive ~1% (cross-model dice + band-2 cross-file precision).

## WHAT'S NEXT (after the registry design)

1. **Cross-audit coverage** (the real roadmap): fable audit → ALL bugs in 1 round; then other audits; then live projects.
2. **Expand beyond payments**: map every attack surface, dispatch per-surface prompts (orchestrator routing).
3. **Oracle off-lexicon hole**: a non-silent "off-lexicon value-sink — UNRELIABLE" notice (no-false-clean) — **NOT** lexicon-broadening (whack-a-mole). Exact cells + boundary: `docs/validation/heldout-oracle-generalization/`. brainstorm→plan, not a blind patch. (Becomes one prevention adapter in the registry.)
4. **Per-surface prompt breadth** (#16 / #22): blind-author candidate prompts → validate on held-out cells → adopt only on zero regression vs the 17/17.
5. **Scale**: auto-scorer for `run_bench` past 17 cells (#61, deferred — not a coverage blocker).

## KEY PATHS

- **SoT ledger:** `~/.claude/projects/-home-user/memory/project_security_gate.md`
- **Specs:** `docs/specs/` (notably `2026-06-17-security-gate-design.md`, `2026-06-18-prevent-band-design.md`, `2026-06-18-detect-orchestrator-design.md`, `2026-06-18-modular-hierarchy-design.md`).
- **Plans:** `docs/plans/`. **Validation:** `docs/validation/` (recall: `2026-06-19-runbench-17cell-recall.md`; held-out: `heldout-oracle-generalization/`).
- **Conventions:** `CLAUDE.md` (repo root) + `orchestrator/DESIGN.md`.
- **Code:** `orchestrator/{detect,gate,resolver,llm_runner,mapper,semantic_merge,conventions}.py`; `prevent/{contract,registry,runner,prevent}.py` + `confirmed.json`; `domains/security/corpus/`; `domains/security/detectors/oracle/oracle2.ts`.
- **Tests:** `tests/` (+ `golden/`). Run: `rtk proxy python3 -m pytest /home/user/Projects/security-gate -q`.

## SUGGESTED SKILLS

- **`/caveman:caveman ultra`** — invoke first (global mandate): terse working-text + critical/best-practice stance.
- **`brainstorm` → `plan` → `sdd`** — for the registry design and any designed change.
- **`security-guard`** agent — independent adversarial pass when validating a recognition-breadth change.
- **advisor** — before primitive/contract decisions, before adopting any new prompt, before declaring done.
