Audit Project: correct-op multi-agent audit loop
audit-project restores the invariant: no open critical/high findings remain in the selected scope. This is not a one-pass critique; it selects reviewers from evidence, applies fixes in verified batches, re-reviews only changed files, and stops only at zero critical/high, a user decision gate, or the iteration cap.
Bulk reviewer prompts live in references/review-roster.md. Consolidation, dismissal, blocked-ratio, decision-gate, and priority-routing rules live in references/false-positive-contract.md.
Sync lineage: the diff-scoped
review-fix-grill-loopskill carries adapted copies of both reference files. The reviewer prompts, common schema, false-positive clause, blocked-ratio, stall-hash, and routing rules share an ancestor; a canonical edit here must be hand-propagated toskills/review-fix-grill-loop/references/(no CI enforces it).
When to Apply / NOT
Apply when the user asks for a deep code audit, an iterative review until clean, release-readiness review, security/performance/test-quality review, post-refactor risk sweep, or a bug-hunting pass across a scope.
NOT when the user wants a read-only opinion, a single known test failure fixed, a narrow symbol explanation, dependency CVE remediation only, or a pure formatting/lint cleanup. Use the smaller direct operation instead; this loop is intentionally heavyweight.
Inputs and State
Inputs:
scope: path, glob, package, PR/diff, or.. Default..--recent: audit files touched in the last five commits plus unstaged/staged changes.--domain <reviewer>: run one reviewer domain only; still apply the same consolidation contract.--quick: single review pass; no fixes, no iteration.--resume: load.outline/audit/queue.jsonif present.--max-iterations N: default5.
State:
.outline/audit/queue.json: current scope, selected reviewers, raw reviewer output, consolidated findings, low-debt extraction, verification results, decisions, hash history..outline/audit/iterations/<n>.json: per-iteration changed files, batches, verification command/output summary, re-review result hash.
Workflow
1. Resolve scope and detect project shape
- Resolve
scopebefore any agent launch. If--recent, use changed-file scope from the last five commits plus staged/unstaged changes; otherwise use the user path or.. - Read manifests and config, not random files:
package.json,pyproject.toml,requirements.txt,Cargo.toml,go.mod,pom.xml,build.gradle*,Gemfile, CI configs, Dockerfiles, route/framework config, migration dirs. - Count tracked files for the resolved scope. Prefer a tracked-file list (
git ls-files <scope>) when in a git repo; fallback to ODINfindfor non-git workspaces. - Detect framework and flags:
| Flag | Evidence |
|---|---|
| HAS_DB | migrations/schema dirs, schema.prisma, ORM deps, SQLAlchemy/Django/Rails models, TypeORM/Sequelize/Mongoose, raw SQL files |
| HAS_API | route/controller/handler dirs, OpenAPI files, Express/Fastify/Nest/FastAPI/Django/Flask/Rails/Spring deps |
| FRONTEND | .tsx, .jsx, .vue, .svelte, browser entrypoints, React/Vue/Angular/Svelte deps |
| BACKEND | services, workers, queues, server framework deps, CLI/server entrypoints, domain handlers |
| CICD | .github/workflows, .gitlab-ci.yml, .circleci/config.yml, Jenkinsfile, Dockerfile, deploy manifests |
2. Gather native priority signals
Use these signals to route attention, not to auto-dismiss anything.
- Test gaps: high-churn files with no co-changing test file.
- Git recipe: parse
git log --name-only --format='%H%x09%ad%x09%s' --date=short -- <scope>; group files per commit; mark source files whose commit groups rarely includetest,spec,__tests__,tests/, or language-native test suffixes. - Score:
test_gap_score = hotspot_score + 2 * bugfix_toucheswhen test co-change count is0; otherwise dampen by1 / (1 + test_cochanges).
- Git recipe: parse
- Pain / hotspots: files likely to hide bugs.
- Git recipe:
total_touches,recent_touchesover the last 90 days, and bug-fix touches from subjects matchingfix|bug|regress|crash|fault|hotfix|panic|leak. - Complexity proxy: use
codegraph_explore/codegraph_filesfor symbol count and dependency fan-in/fan-out when indexed; fallback toast-grepcounts for functions, conditionals, loops, catches, and nested classes. - Score:
hotspot_score = total_touches + (2 * recent_touches);bug_rate = bugfix_touches / max(total_touches, 1);pain_score = hotspot_score * (1 + bug_rate) * (1 + complexity_band).
- Git recipe:
- Bugspots: files repeatedly touched by fixes.
- Git recipe: filter fix-like commits above, count affected files, rank by
bugfix_touchesthenbug_rate. - Route to security, test-quality, and code-quality reviewers with explicit "fragile file" context.
- Git recipe: filter fix-like commits above, count affected files, rank by
- Slop concentration: files with mechanical cleanliness hazards.
- HIGH-certainty scans:
ast-grep/search for empty catches, blanketcatch {},TODO: implement,throw new Error('not implemented'),console.log/debug prints in production paths,unwrap()/expect()in non-test Rust, hardcoded secrets, commented-out code blocks, dead branches afterreturn, obvious pass-through wrappers. - Rank files with
>=3hits; top 5 feed code-quality first. Cross-file clusters feed architecture if they imply wrapper towers, duplicate implementations, or boundary sprawl.
- HIGH-certainty scans:
- Entry-points / exposed surfaces.
- Primary:
codegraph_explorewith "entry points, handlers, routes, CLIs, jobs, exported API surface" and thencodegraph_callers/codegraph_impactfor risky fan-in. - Fallback:
ast-grepformain, route registration, exported handlers, controllers, Lambda/Cloudflare handlers, CLI command registration, package scripts, framework config, Docker/CI entry commands. - Route to security and devops always; route to API/backend/frontend according to file kind.
- Primary:
Persist a compact prioritySignals object in .outline/audit/queue.json: top 20 test gaps, top 20 pain/hotspots, top 20 bugspots, top 5 slop concentration files, top 20 entry-points.
3. Select reviewers
Always select the 4 core reviewers:
code-qualitysecurityperformancetest-quality
Select up to 6 conditional reviewers:
architecturewhen file count > 50, cross-file slop targets exist, or codegraph impact shows broad fan-in/fan-out.databasewhenHAS_DB.apiwhenHAS_API.frontendwhenFRONTEND.backendwhenBACKEND.devopswhenCICDor entry-points include build/deploy/runtime surfaces.
If --domain is set, run only that domain unless doing so would make the requested domain meaningless (for example, --domain database with HAS_DB=false); then return a clear no-scope result.
4. Launch review pass in parallel
Use generic ODIN reviewer or task agents. Do not name model tiers. Do not spawn bespoke agent IDs as if they exist on disk.
Each selected reviewer receives:
- The resolved scope and framework flags.
- The priority signals relevant to that reviewer.
- Its role prompt from
references/review-roster.md. - The mandatory output schema:
{
"pass": "code-quality|security|performance|test-quality|architecture|database|api|frontend|backend|devops",
"findings": [
{
"file": "path/to/file.ext",
"line": 42,
"severity": "critical|high|medium|low",
"category": "short category",
"description": "what is wrong and why it matters",
"suggestion": "specific fix",
"confidence": "high|medium|low",
"falsePositive": false,
"falsePositiveReason": "required non-empty string only when falsePositive is true"
}
]
}
Reviewer findings must be evidence-based: exact file, exact line, concrete failure mode, and fix. Missing location or vague "consider improving" text is not a finding; downgrade to note or drop.
5. Consolidate findings and apply the false-positive contract
Use references/false-positive-contract.md exactly:
- Normalize all reviewer JSON.
- Honor
falsePositive: trueonly whenfalsePositiveReason.trim()is non-empty. - If the reason is missing, force the finding back to
OPENand setreasonMissing: true. - Deduplicate by
pass:file:line:description. - Sort severity:
critical,high,medium,low. - Count only non-dismissed findings as open.
- Extract LOW findings into a
TECHNICAL_DEBT.mdlist and.outline/audit/queue.json.lowDebt; low items do not block the critical/high loop. - Compute blocked ratio:
dismissed_false_positive / total_findings. Iftotal_findings >= 10 && ratio > 0.5, stop and triggeraskwith:treat-all-as-open(Recommended): strip all false-positive flags from current raw results and continue.override-and-accept-dismissals: accept dismissals as-is and continue/complete.abort: stop with queue intact for manual inspection.
6. Fix loop: critical/high first, verified by batch
Loop condition: openCriticalHigh > 0 && iteration < maxIterations.
- Build a fix queue from open
criticalthenhigh; within each severity, sort by effort small→large, then group by file. - Apply one file batch at a time. Keep the patch minimal; fix the named invariant, not adjacent style.
- After each batch, run the repo's own verification command. Discover from manifests and CI (
test,check,build,lint,cargo test,go test ./...,pytest, etc.). If no verifier exists, ask before mutating more than one batch; otherwise mark remaining fixes as blocked-by-no-verifier. - On regression, run
git restore -- <changed files in that batch>, recordregressed: true, and keep the finding open with the regression note. - Re-review only changed files, using only reviewers whose domain touches those files plus the original reviewers that emitted the fixed findings.
- Re-consolidate. Run the blocked-ratio gate before checking for zero remaining issues.
- Compute
findingsHash = sha256(sorted(open critical/high keys: pass:file:line:severity:description:suggestion)). If the same hash appears in two consecutive iterations, stall. - At every iteration boundary where critical/high remain, trigger
ask:continue-fixing(Recommended when verifier is green and not stalled)create-issues-for-restmove-remainder-to-TECHNICAL_DEBTleave-in-queue
Stall handling: if the hash repeats twice, continue-fixing is not Recommended. Recommend creating issues or leaving the queue unless there is a clear new fix plan.
7. Completion
Complete only when one is true:
- zero open critical/high findings after consolidation and re-review;
- user chose a deferral path at an iteration gate;
- max iterations reached and the queue/debt artifacts are current.
Report: scope, selected reviewers, iterations, critical/high fixed, remaining critical/high, low debt count, verification commands run, regressions rolled back, queue path.
Anti-patterns
- Single-agent audit: defeats domain separation; only valid with explicit
--domain. - Fix before consolidation: raw reviewer output is untrusted until deduped and false-positive-checked.
- Let a high false-positive ratio auto-pass: >50% dismissal on >=10 findings is a prompt-injection smell, not success.
- Re-review the whole repo after every batch: wasteful and noisy; re-review changed files plus impacted entry-points.
- Suppress tests or guards: never disable a verifier to land an audit fix.
- Create public issues for security-sensitive findings: keep exploitable details internal; fix immediately or leave private queue notes.
- Ship placeholders: "TODO: fix later" is a failed audit fix.
Validation Gates
| Gate | Pass Criteria | Blocking |
|---|---|---|
| Scope resolved | Concrete file set or path exists; recent mode has changed files | Yes |
| Context detected | Framework, flags, file count, priority signals collected or marked unavailable with fallback tried | Yes |
| Reviewer roster selected | 4 core reviewers plus justified conditional reviewers; no more than 10 total | Yes |
| Parallel dispatch | Selected reviewers launched in one parallel batch with role prompts and schema | Yes |
| Findings schema valid | Every finding has file, line, severity, category, description, suggestion, confidence, false-positive fields | Yes for queue ingestion |
| False-positive contract | Empty-reason dismissals forced open; blocked-ratio gate applied before zero-check | Yes |
| Low debt extracted | LOW findings copied into TECHNICAL_DEBT.md list and queue lowDebt | No, but must happen before completion |
| Fix ordering | Critical before high; batched by file | Yes |
| Verification | Repo-native verifier run after every batch | Yes when a verifier exists |
| Regression rollback | Failing batch restored with git restore -- <files> and noted | Yes |
| Targeted re-review | Only changed files plus impacted surfaces re-reviewed | Yes |
| Stall detection | Identical open critical/high hash twice triggers decision gate | Yes |
| Completion invariant | Zero open critical/high or explicit user deferral path | Yes |