swarm-orchestrator 11.2.0 → 12.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/README.md +72 -211
  2. package/dist/src/audit/cheat-detector/diff-chunker.js +83 -5
  3. package/dist/src/audit/cheat-detector/diff-walker.js +5 -0
  4. package/dist/src/audit/cheat-detector/judge-primary.js +8 -1
  5. package/dist/src/audit/cheat-detector/judge-prompts/v2-balanced.js +46 -7
  6. package/dist/src/audit/cheat-detector/llm-judge/index.js +44 -9
  7. package/dist/src/audit/cheat-detector/llm-judge/local-judge.js +10 -2
  8. package/dist/src/audit/cheat-detector/llm-judge/ollama-judge.js +150 -0
  9. package/dist/src/audit/cheat-detector/mock-delta.js +101 -0
  10. package/dist/src/audit/cheat-detector/mock-of-hallucination.js +9 -1
  11. package/dist/src/audit/cheat-detector/verify-findings.js +34 -0
  12. package/dist/src/audit/execution-grounded/corroborate.js +5 -0
  13. package/dist/src/audit/execution-grounded/dead-branch-restoration.js +457 -0
  14. package/dist/src/audit/execution-grounded/fake-refactor-restoration.js +399 -0
  15. package/dist/src/audit/execution-grounded/index.js +719 -8
  16. package/dist/src/audit/execution-grounded/issue-repro.js +5 -2
  17. package/dist/src/audit/execution-grounded/mock-restoration.js +396 -0
  18. package/dist/src/audit/execution-grounded/no-op-fix-restoration.js +376 -0
  19. package/dist/src/audit/execution-grounded/test-restoration.js +906 -0
  20. package/dist/src/audit/execution-grounded/type-suppression-restoration.js +496 -0
  21. package/dist/src/audit/gate/block-eligibility.js +35 -14
  22. package/dist/src/audit/gate/block-trigger-types.js +6 -0
  23. package/dist/src/audit/gate/block-triggers.js +320 -4
  24. package/dist/src/audit/gate/gate-decision.js +30 -12
  25. package/dist/src/audit/gate/self-certifying.js +74 -0
  26. package/dist/src/audit/gate/wilson.js +26 -0
  27. package/dist/src/audit/oracle/evade/evaders.js +77 -1
  28. package/dist/src/audit/report-comment/block-trigger-section.js +146 -3
  29. package/dist/src/audit/report-comment/index.js +8 -0
  30. package/dist/src/cli/v8/audit-handler.js +363 -112
  31. package/dist/src/cli/v8/resume-handler.js +5 -59
  32. package/dist/src/cli/v8/run-handler.js +133 -142
  33. package/dist/src/cli/v8/session-utils.js +121 -0
  34. package/dist/src/persona/verifier-persona.js +2 -1
  35. package/dist/src/population/persona-message.js +2 -1
  36. package/dist/src/session/bounded-session.js +94 -0
  37. package/dist/src/shared/child-env.js +94 -0
  38. package/dist/src/shared/obligation-rendering.js +80 -0
  39. package/dist/src/shared-predicates/predicate-runner.js +2 -1
  40. package/dist/src/verification/run-verifier.js +3 -2
  41. package/package.json +13 -2
package/README.md CHANGED
@@ -1,51 +1,42 @@
1
- <div align="center">
2
-
3
- <img src="docs/assets/hero.svg" alt="Swarm Orchestrator" width="100%">
1
+ <p align="center">
2
+ <img src="./assets/cover.svg" alt="Swarm Orchestrator cover" width="100%">
3
+ </p>
4
4
 
5
5
  # Swarm Orchestrator
6
6
 
7
- A CLI for auditing AI-generated PRs and grading patches against typed contracts.
7
+ Reads a pull request (PR) diff and flags the shortcuts artificial intelligence (AI) coding agents use to look done without being done. Relaxed tests, swallowed errors, fake renames, eleven categories in all. Grading patches against typed obligation contracts via a multi-persona pipeline is the second surface.
8
+
9
+ For engineers reviewing AI-written PRs at volume. Compliance teams generate CycloneDX-ML (machine learning) and SPDX (Software Package Data Exchange) 3.0 AI-BOM (bill of materials) artifacts. The artifacts map to EU AI Act (European Union Artificial Intelligence Act) Annex IV and CISA (Cybersecurity and Infrastructure Security Agency) reporting. Maintainers keep a hash-chained evidence record for every graded patch.
10
+
11
+ **Flags are tips, blocks are proof.** A flag is a structural detector seeing a cheat-shaped pattern; it is advisory and never blocks a merge (`--mode advise` is the default). A block is a self-certifying runtime result whose per-instance controls are all green, and every block ships the exact command that reproduces it in a fresh checkout. Eight proof protocols back the gate today: six execution-grounded restoration proofs (`test-tamper`, `mock-mutation`, `no-op-fix`, `type-suppression`, `fake-refactor`, `dead-branch`) plus `claim-falsified` and `obligation-failure`. The measured proven-finding precision on the execution-grounded-viable corpus slice, with its plain n, is in [`benchmarks/real-corpus/GATE-PRECISION-REPORT.md`](benchmarks/real-corpus/GATE-PRECISION-REPORT.md).
8
12
 
9
13
  <!-- BADGES:START -->
10
14
  [![CI](https://github.com/moonrunnerkc/swarm-orchestrator/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/moonrunnerkc/swarm-orchestrator/actions/workflows/ci.yml)
11
15
  [![license ISC](https://img.shields.io/static/v1?label=license&message=ISC&color=blue)](LICENSE)
12
16
  [![node >= 20](https://img.shields.io/static/v1?label=node&message=%3E%3D%2020&color=3c873a)](package.json)
13
- [![version 11.2.0](https://img.shields.io/static/v1?label=version&message=11.2.0&color=22d3ee)](package.json)
14
- [![oracle recall 84% (253/300)](https://img.shields.io/static/v1?label=oracle%20recall&message=84%25%20(253%2F300)&color=brightgreen)](benchmarks/results/AB-REPORT.md)
17
+ [![version 12.0.0](https://img.shields.io/static/v1?label=version&message=12.0.0&color=22d3ee)](package.json)
18
+ [![oracle recall 93% (301/325)](https://img.shields.io/static/v1?label=oracle%20recall&message=93%25%20(301%2F325)&color=brightgreen)](benchmarks/results/AB-REPORT.md)
15
19
  [![real-PR false alarms 0.11/PR](https://img.shields.io/static/v1?label=real-PR%20false%20alarms&message=0.11%2FPR&color=brightgreen)](benchmarks/real-prs/REAL-WORLD-REPORT.md)
16
20
  [![real-PR cheats vs linters 4 confirmed (Semgrep+ESLint: 1)](https://img.shields.io/static/v1?label=real-PR%20cheats%20vs%20linters&message=4%20confirmed%20(Semgrep%2BESLint%3A%201)&color=brightgreen)](benchmarks/real-prs/v11-BENEFIT-REPORT.md)
17
21
  <!-- BADGES:END -->
18
22
 
19
- <a href="#install"><b>Install</b></a> ·
20
- <a href="#quick-start"><b>Quick start</b></a> ·
21
- <a href="#what-this-does"><b>What it does</b></a> ·
22
- <a href="#results"><b>Results</b></a> ·
23
- <a href="#cheat-detectors"><b>Detectors</b></a> ·
24
- <a href="#ai-bom"><b>AI-BOM</b></a> ·
25
- <a href="#reference"><b>Reference</b></a>
26
-
27
- </div>
28
-
29
- ---
30
-
31
- <div align="center">
32
-
33
- ## What This Does
34
-
35
- Swarm Orchestrator reads a pull-request diff and flags the shortcuts an AI coding agent takes to look done without being done: relaxed tests, stripped assertions, swallowed errors, fake renames, eleven checks in all.
36
- On a benchmark of planted cheats it recovers 253 of 300 (84%, up 20.5% from the prior version), and on real merged Cloudflare PRs it caught two cheats that Semgrep and the ESLint security rules missed, both reproducible offline.
37
- Findings are advisory by default, so it never blocks a merge unless you turn that on.
38
-
39
- </div>
40
-
41
- ## Who it's for
42
-
43
- - You review AI-written PRs at volume and want a "this change may be gaming the tests" signal that ordinary linters do not give you.
44
- - You have to hand over AI-procurement or compliance paperwork (EU AI Act Annex IV, CISA SBOM-for-AI) and would rather generate the documents than write them by hand.
45
- - You run AI coding agents and want one hard rule: a patch lands only if it builds, passes tests, holds a stated property, and survives a falsifier trying to break it.
23
+ <p align="center">
24
+ <a href="#install">Install</a> ·
25
+ <a href="#quick-start">Quick start</a> ·
26
+ <a href="#results">Results</a> ·
27
+ <a href="#cheat-detectors">Detectors</a> ·
28
+ <a href="#use-as-a-github-action">GitHub Action</a> ·
29
+ <a href="#ai-bom">AI-BOM</a> ·
30
+ <a href="#orchestrator-mode">Orchestrator</a> ·
31
+ <a href="#architecture">Architecture</a> ·
32
+ <a href="#commands">Commands</a> ·
33
+ <a href="#limitations">Limitations</a>
34
+ </p>
46
35
 
47
36
  ## Install
48
37
 
38
+ Node 20 or later. See [`package.json`](package.json).
39
+
49
40
  ```bash
50
41
  git clone https://github.com/moonrunnerkc/swarm-orchestrator.git
51
42
  cd swarm-orchestrator
@@ -55,74 +46,61 @@ npm link
55
46
  swarm --help
56
47
  ```
57
48
 
58
- Node 20 or later. See [`package.json`](package.json).
59
-
60
49
  ## Quick start
61
50
 
62
51
  ```bash
63
- # audit a PR by reference (advisory by default; never blocks the merge)
52
+ # Audit a PR by reference (advisory by default; never blocks the merge)
64
53
  GITHUB_TOKEN=... swarm audit moonrunnerkc/swarm-orchestrator#42
65
54
 
66
- # opt in to merge-blocking gate mode
55
+ # Opt in to merge-blocking gate mode (blocks only on a self-certifying
56
+ # runtime proof; enable the execution-grounded layer in
57
+ # .swarm/audit-config.yaml first; see docs/audit-config.md)
67
58
  GITHUB_TOKEN=... swarm audit moonrunnerkc/swarm-orchestrator#42 --mode gate
68
59
 
69
- # audit a local diff with the experimental detector set (all 11 detectors)
60
+ # Audit a local diff with all 11 detectors
70
61
  git diff main...HEAD | swarm audit --diff-stdin --detectors experimental
71
62
 
72
- # audit + emit a CycloneDX 1.6 ML-BOM
63
+ # Audit and emit a CycloneDX 1.6 ML-BOM
73
64
  swarm audit --diff-file my.patch --emit-aibom cyclonedx-ml
74
65
 
75
- # shadow-mode dogfood: record verdicts to disk, no comment, no gate
66
+ # Shadow mode: record verdicts to disk without commenting or gating
76
67
  swarm audit --pr <ref> --shadow my-org/my-repo
77
-
78
- # single-file shadow output (one JSON per audit invocation; see docs/shadow-mode.md)
79
- swarm audit --pr <ref> --shadow-output ./audit-verdict.json
80
68
  ```
81
69
 
82
- Exit codes: `0` advisory-clean or any advise-mode run, `1` block (gate mode only), `2` usage error.
70
+ Exit codes: `0` advisory or any advise-mode run, `1` block (gate mode only), `2` usage error.
83
71
 
84
72
  ## Results
85
73
 
86
- Every number here is reproducible from this repo, runs offline, and points at the report that produced it.
74
+ Every number here is reproducible from this repo and runs offline.
87
75
 
88
76
  ### Catches cheats that linters miss
89
77
 
90
- Two real cheats in merged Cloudflare PRs reproduce deterministically offline from the committed diffs, and a live differential confirms that Semgrep (210 rules) and the ESLint security ruleset flag neither:
91
-
92
- | PR | Cheat it caught | Semgrep / ESLint |
78
+ | PR | Cheat caught | Semgrep / ESLint |
93
79
  |---|---|---|
94
- | [cloudflare/workers-sdk#14063](https://github.com/cloudflare/workers-sdk/pull/14063) | fake refactor: a function was renamed but two callers still call the old name | not flagged |
95
- | [cloudflare/workers-sdk#14132](https://github.com/cloudflare/workers-sdk/pull/14132) | error swallow: a bare empty `catch` silently hides every error in the block | not flagged |
96
-
97
- This is the cheat class ordinary analyzers do not model: they look for dangerous APIs, not for tests quietly relaxed or errors quietly dropped. Reproduce either catch with `swarm audit --diff-file benchmarks/real-prs/diffs/cloudflare-workers-sdk/<pr>.diff`. The broader study across twelve repos, with findings classified by two independent model families plus the full false-alarm accounting, is in [`benchmarks/real-prs/v11-BENEFIT-REPORT.md`](benchmarks/real-prs/v11-BENEFIT-REPORT.md); two further error-swallow catches in that report came from the pre-upgrade detector flagging comment-only `// skip` catches, which the current version downgrades as usually legitimate.
80
+ | [cloudflare/workers-sdk#14063](https://github.com/cloudflare/workers-sdk/pull/14063) | fake refactor: function renamed but two callers still use the old name | not flagged |
81
+ | [cloudflare/workers-sdk#14132](https://github.com/cloudflare/workers-sdk/pull/14132) | error swallow: bare empty `catch` silently hides every error in the block | not flagged |
98
82
 
99
- ### Measured detection, not asserted
83
+ Both reproduce deterministically from the committed diffs. Semgrep (210 rules) and the ESLint security ruleset flag neither. Reproduce either with `swarm audit --diff-file benchmarks/real-prs/diffs/cloudflare-workers-sdk/<pr>.diff`. Full study across twelve repos in [`benchmarks/real-prs/v11-BENEFIT-REPORT.md`](benchmarks/real-prs/v11-BENEFIT-REPORT.md).
100
84
 
101
- Detection is scored against a defect-injection oracle: an injector splices one labeled cheat into a presumed-clean real PR, so recall is measured against ground truth rather than claimed. The auditor recovers **253 of 300** planted cheats (**84%**), up **20.5%** from the pre-upgrade baseline of 210/300, across twelve categories. Most structural detectors sit at or near 1.00 recall on their own injection class. Reproduce with `npm run benchmarks:full`; the pre/post A/B is in [`benchmarks/results/AB-REPORT.md`](benchmarks/results/AB-REPORT.md) and the per-detector table is in [`benchmarks/oracle-corpus/per-detector-recall.md`](benchmarks/oracle-corpus/per-detector-recall.md).
85
+ ### Detection numbers
102
86
 
103
- ### Low noise on unbiased real PRs
87
+ **301 of 325** planted cheats recovered (92.6%) across thirteen categories scored against a defect-injection oracle: 258/275 structural plus 43/50 on the two semantic categories the judge-primary path covers. The behavioral `cheat-mock-mutation` category drove the latest gain: focusing the judge on the hunks that add a value-injecting mock lifted its recall from 0.16 (the prior rapid-mlx glm47 run) to 0.96 (24/25) on the local qwen3.6 judge, while the clean-PR judge false-positive rate fell from 10% to 0%. Reproduce with `SWARM_JUDGE_PROVIDER=ollama SWARM_JUDGE_MODEL=qwen3.6:35b-a3b npm run benchmarks:full`; A/B with the same-model decomposition in [`benchmarks/results/AB-REPORT.md`](benchmarks/results/AB-REPORT.md) and per-detector recall in [`benchmarks/oracle-corpus/per-detector-recall.md`](benchmarks/oracle-corpus/per-detector-recall.md).
104
88
 
105
- On an 18-PR pilot across five public repos, the post-upgrade auditor's false-alarm burden is **0.11 findings per PR**, at or below the pre-upgrade auditor's, with the oracle recall gain intact ([`benchmarks/real-prs/REAL-WORLD-REPORT.md`](benchmarks/real-prs/REAL-WORLD-REPORT.md)).
89
+ **0.11 findings per PR** on an 18-PR pilot across five public repos. At or below the pre-upgrade auditor's false-alarm burden, with the oracle recall gain intact. Full numbers in [`benchmarks/real-prs/REAL-WORLD-REPORT.md`](benchmarks/real-prs/REAL-WORLD-REPORT.md).
106
90
 
107
- ### A signal no diff-reader can produce
91
+ ### Execution-grounded layer
108
92
 
109
- An optional execution-grounded layer provisions a sandboxed checkout and runs diff-scoped mutation testing, issue-linked repro, and a coverage delta, then correlates the findings against each PR's revert and hotfix history. It surfaced one under-constrained change the diff-only layers cannot see: proof anchor [`trpc/trpc#6098`](https://github.com/trpc/trpc/pull/6098), where mutations survived on covered lines and eight of those lines are the ones the later hotfix changed. Reproduce with `npm run execution-grounded:full` ([`benchmarks/real-prs/v11-EXECUTION-GROUNDED-REPORT.md`](benchmarks/real-prs/v11-EXECUTION-GROUNDED-REPORT.md)).
93
+ Reproduce with `npm run execution-grounded:full`. This optional layer sets up a sandboxed checkout and runs mutation testing, repro execution, and a coverage delta scoped to the lines the PR changed. It surfaced one proof-correlated catch: proof anchor [`trpc/trpc#6098`](https://github.com/trpc/trpc/pull/6098), where 8 lines with surviving mutations were later changed by a hotfix. Evaluation in [`benchmarks/real-prs/v11-EXECUTION-GROUNDED-REPORT.md`](benchmarks/real-prs/v11-EXECUTION-GROUNDED-REPORT.md).
110
94
 
111
95
  ## Cheat detectors
112
96
 
113
- Eleven detectors. Eight load by default; three (`comment-only-fix`,
114
- `exception-rethrow-lost-context`, `dead-branch-insertion`) require
115
- `--detectors experimental` because they have never fired on real PR
116
- data, so there is no signal to gauge them against. The set governs which
117
- detectors load; the precision gate (see [Limitations and what's next](#limitations-and-whats-next))
118
- governs which may emit a blocking finding. Registered in
119
- [`src/audit/cheat-detector/detector-sets.ts`](src/audit/cheat-detector/detector-sets.ts).
97
+ Eleven detectors. Eight load by default; three (`comment-only-fix`, `exception-rethrow-lost-context`, `dead-branch-insertion`) require `--detectors experimental` because they have no real-PR signal yet to measure against. Registered in [`src/audit/cheat-detector/detector-sets.ts`](src/audit/cheat-detector/detector-sets.ts).
120
98
 
121
99
  | Category | Set | Trigger |
122
100
  |---|---|---|
123
101
  | `error-swallow` | default | Bare empty or comment-only `catch` block added in non-test code. |
124
102
  | `mock-of-hallucination` | default | `jest.mock` / `vi.mock` / `@patch` against a module declared in no manifest in the repo. |
125
- | `no-op-fix` | default | Test modified with no source change in the same PR, or vice versa; import-graph reachability fallback when only one side moved. |
103
+ | `no-op-fix` | default | Test modified with no source change, or vice versa; import-graph reachability fallback when only one side moved. |
126
104
  | `fake-refactor` | default | Exported symbol renamed in source, no caller in the diff updates the old name. |
127
105
  | `coverage-erosion` | default | Source branch added with no compensating test addition. |
128
106
  | `test-relaxation` | default | Strict matcher swapped for a loose one, or a test block removed without same-chunk replacement. |
@@ -132,60 +110,9 @@ governs which may emit a blocking finding. Registered in
132
110
  | `exception-rethrow-lost-context` | experimental | `throw err` replaced with `throw new Error(...)` and `{ cause }` not forwarded. |
133
111
  | `dead-branch-insertion` | experimental | Branch guarded by a literal-false condition added. |
134
112
 
135
- Each detector lives in its own file under [`src/audit/cheat-detector/`](src/audit/cheat-detector/).
136
-
137
- Beyond the ten structural detectors, a judge-primary path catches two
138
- semantic categories (`goal-not-fixed`, `cheat-mock-mutation`) that have no
139
- structural tell, by asking the judge whether the diff delivers the PR's
140
- stated claim. Large diffs are split into hunk-grouped chunks rather than
141
- head-truncated, so a defect in the tail still reaches the judge.
142
-
143
- Per-repo configuration in `.swarm/audit-config.yaml`: `excludePaths` exempts
144
- globs from detection, `intentSeverityPolicy` (`strict` | `lenient` | `off`)
145
- controls the PR-intent severity-upgrade layer, and `judgePrimary`
146
- (`enabled`, `categories`) controls the semantic path. See
147
- [`docs/audit-config.md`](docs/audit-config.md).
148
-
149
- ### Reproducible evaluation
150
-
151
- Detection is measured against a defect-injection oracle, not asserted: an
152
- injector splices one labeled cheat into a presumed-clean real PR, and
153
- `npm run benchmarks:full` regenerates per-detector recall, judge
154
- calibration, tail-defect and evasion reports, and `COVERAGE.md`. The pre
155
- vs post A/B is in
156
- [`benchmarks/results/AB-REPORT.md`](benchmarks/results/AB-REPORT.md); the
157
- method and honesty caveats are in
158
- [`docs/audit/methodology.md`](docs/audit/methodology.md).
159
-
160
- Hunk-grouped chunking and per-hunk localization are infrastructure, not
161
- shipped recall wins: their mechanism tests pass, but on the current judge
162
- the tail-defect and per-hunk recall numbers stay low (a localized confirm
163
- prompt lifts tail-defect to 0.5 in measurement but is not shipped pending
164
- real-PR false-positive validation). The numbers are reported honestly in
165
- `benchmarks/oracle-corpus/tail-defect-recovery.md` and
166
- `per-hunk-localization.md`.
167
-
168
- The auditor is also validated on unbiased real PRs: `npm run real-prs:full`
169
- fetches recent merged PRs from public repos, audits them, and has an
170
- independent arbiter classify every finding. On an 18-PR pilot the
171
- post-upgrade false-alarm burden is 0.11 per PR, at or below the pre-upgrade
172
- auditor's, with the oracle recall gain intact
173
- ([`benchmarks/real-prs/REAL-WORLD-REPORT.md`](benchmarks/real-prs/REAL-WORLD-REPORT.md)).
174
-
175
- The optional execution-grounded layer is evaluated separately: `npm run
176
- execution-grounded:full` provisions a sandboxed checkout of each corpus PR
177
- and runs diff-scoped mutation testing, issue-linked repro execution, and a
178
- coverage delta, then correlates the findings against each PR's revert/hotfix
179
- proof. It surfaces under-constrained changed lines that no diff-only tool can
180
- see (proof anchor `trpc/trpc#6098`: 10 mutants surviving on covered lines plus
181
- 6 on uncovered lines, 8 of them on the lines the hotfix later changed), where
182
- the repo's test suite discriminates in a generic sandbox. This is a modest,
183
- honest result (1 proof-correlated catch in the sampled corpus, against a 0.929
184
- advisory-findings-per-clean-PR burden since v11.2 aggregated the
185
- uncovered-survivor floods per file; 3.357 before), measured rather than
186
- asserted; the
187
- per-repo viability and the headline numbers are in
188
- [`benchmarks/real-prs/v11-EXECUTION-GROUNDED-REPORT.md`](benchmarks/real-prs/v11-EXECUTION-GROUNDED-REPORT.md).
113
+ Beyond the structural detectors, a judge-primary path catches two semantic categories (`goal-not-fixed`, `cheat-mock-mutation`) by asking the judge whether the diff delivers the PR's stated claim. For `cheat-mock-mutation` a deterministic pre-filter hands the judge only the test hunks that add a value-injecting mock, so the judge reads the six-line cheat instead of skimming a 40k-char diff, and is never asked on a clean PR that adds no such mock. A proven mock-mutation can also block under `--mode gate` as a self-certifying `mock-mutation-proven` trigger (see [`docs/limitations.md`](docs/limitations.md)).
114
+
115
+ Per-repo configuration in `.swarm/audit-config.yaml`: `excludePaths`, `intentSeverityPolicy` (`strict` | `lenient` | `off`), and `judgePrimary`. See [`docs/audit-config.md`](docs/audit-config.md).
189
116
 
190
117
  ## Use as a GitHub Action
191
118
 
@@ -216,22 +143,28 @@ jobs:
216
143
 
217
144
  Outputs: `audit-pass`, `audit-findings`, `audit-ledger`. Full input list in [`action.yml`](action.yml).
218
145
 
146
+ ### Enabling the execution-grounded layer
147
+
148
+ Structural detectors run with no setup and only ever flag (advisory). The six runtime proofs that can gate a merge (`test-tamper`, `mock-mutation`, `no-op-fix`, `type-suppression`, `fake-refactor`, `dead-branch`) need the execution-grounded layer, which is off until you set `executionGrounded.enabled: true` in [`.swarm/audit-config.yaml`](docs/audit-config.md). Turning it on is what lets `--mode gate` block, and every block it produces ships the exact command that reproduces it in a fresh checkout.
149
+
150
+ What it costs: for each audited PR the layer provisions the repository in a sandbox (clone the head, install dependencies, run the affected tests), so it adds clone, install, and test wall-clock to the job, and only PRs in a Node project with a lockfile and a recognized test runner (jest, vitest, mocha) provision at all. Everything else fails closed to advisory. The static viability screen on the project corpus measured 12 of 197 PRs as provisionable ([`benchmarks/real-corpus/eg-viability.json`](benchmarks/real-corpus/eg-viability.json)); your own repository, where the suite is known to run in CI, provisions far more reliably than an arbitrary sample. Nothing the layer cannot prove ever blocks: with the layer off, or on a non-provisionable PR, gate mode passes on advisory findings alone.
151
+
219
152
  ## AI-BOM
220
153
 
221
- `--emit-aibom cyclonedx-ml | spdx-ai | both` writes one document per format per run under `.swarm/aibom/`. Emitters in [`src/audit/aibom/`](src/audit/aibom/) produce hand-rolled JSON against the upstream specs; no third-party AI-BOM runtime dep.
154
+ `--emit-aibom cyclonedx-ml | spdx-ai | both` writes one document per format per run under `.swarm/aibom/`. Emitters in [`src/audit/aibom/`](src/audit/aibom/) produce hand-rolled JSON against the upstream specs with no third-party AI-BOM runtime dependency.
222
155
 
223
156
  Procurement mappings:
224
157
 
225
- - [`docs/eu-ai-act-mapping.md`](docs/eu-ai-act-mapping.md): EU AI Act Article 11 + Annex IV fields.
226
- - [`docs/cisa-sbom-ai-mapping.md`](docs/cisa-sbom-ai-mapping.md): CISA SBOM-for-AI minimum elements.
158
+ - [`docs/eu-ai-act-mapping.md`](docs/eu-ai-act-mapping.md): EU AI Act (European Union Artificial Intelligence Act) Article 11 + Annex IV fields.
159
+ - [`docs/cisa-sbom-ai-mapping.md`](docs/cisa-sbom-ai-mapping.md): CISA (Cybersecurity and Infrastructure Security Agency) SBOM-for-AI minimum elements.
227
160
 
228
161
  ## Orchestrator mode
229
162
 
230
- Use this when you want Swarm to grade patches against a typed contract instead of auditing a PR diff.
163
+ Grades patches against a typed contract instead of auditing a PR diff.
231
164
 
232
165
  ```bash
233
- swarm init # scaffold contract.yaml + patches.jsonl
234
- swarm run --goal "check this project builds" # deterministic provider, no API key
166
+ swarm init # Scaffold contract.yaml + patches.jsonl
167
+ swarm run --goal "check this project builds" # Deterministic provider, no API key
235
168
  ```
236
169
 
237
170
  Minimal contract:
@@ -265,13 +198,9 @@ Provider details in [`docs/providers.md`](docs/providers.md). Obligation taxonom
265
198
 
266
199
  ## Architecture
267
200
 
268
- Two CLI surfaces share one core.
269
-
270
- `swarm run` drives the v8 pipeline (extractor, session, predicate-runner, falsifier, verifier). No patch reaches `main` without passing both `verifyObligation` and `postMergeVerify`.
201
+ Two command-line interface (CLI) surfaces share one core. `swarm run` drives the v8 pipeline (extractor, session, predicate-runner, falsifier, verifier). No patch reaches main without passing both `verifyObligation` and `postMergeVerify`.
271
202
 
272
- `swarm audit` reuses the verifier and falsifier layers against a unified diff. It needs no session, no extractor, and no model credentials.
273
-
274
- Both surfaces write to the same append-only hash-chained ledger ([`src/ledger/ledger.ts`](src/ledger/ledger.ts)). Tampering breaks the chain.
203
+ `swarm audit` reuses the verifier and falsifier layers against a unified diff, with no session, extractor, or model credentials needed. Both surfaces write to the same append-only hash-chained ledger ([`src/ledger/ledger.ts`](src/ledger/ledger.ts)); tampering breaks the chain.
275
204
 
276
205
  ## Commands
277
206
 
@@ -283,6 +212,7 @@ Both surfaces write to the same append-only hash-chained ledger ([`src/ledger/le
283
212
  | `swarm run <contract-dir>` | Grade against a pre-compiled contract directory. |
284
213
  | `swarm resume <run-id>` | Resume a killed run from its ledger. |
285
214
  | `swarm stats <run-id>` | Aggregate diagnostic counts from a run ledger. |
215
+ | `swarm ledger verify <run-id>` | Verify a hash-chained ledger at rest. |
286
216
  | `swarm init` | Scaffold `contract.yaml` and `patches.jsonl`. |
287
217
  | `swarm doctor [--fix] [--connectors]` | Probe local prerequisites. |
288
218
 
@@ -303,91 +233,22 @@ Both surfaces write to the same append-only hash-chained ledger ([`src/ledger/le
303
233
 
304
234
  ## Integrations
305
235
 
306
- - Claude Code slash command: [`.claude/commands/swarm-audit.md`](.claude/commands/swarm-audit.md).
307
- - Cursor rule pack: [`integrations/cursor/swarm-audit.mdc`](integrations/cursor/swarm-audit.mdc).
308
- - Aider pre-commit hook: [`integrations/aider/pre-commit-swarm-audit`](integrations/aider/pre-commit-swarm-audit).
309
-
310
- ## Versions
311
-
312
- `10.3.0-advisory` finishes the four solo-doable items left after
313
- `10.2.0-advisory`. `no-op-fix` bumps to 2.0.0 with a gated Anthropic
314
- Haiku judge (off by default; opt in with `--enable-llm-judge` or
315
- `SWARM_AUDIT_LLM_JUDGE=1`), content-addressed cache at
316
- `.swarm/llm-judge-cache/`, and a new `llm-judge-result` ledger entry
317
- that pins the model id so replay is deterministic. The real-corpus
318
- baseline is re-scored against the v2.0 detectors: overall F1 0.167
319
- (P 0.100, R 0.500), with `mock-of-hallucination` picking up 2 TPs the
320
- v1.x shape missed. A static dashboard fetches the score snapshot
321
- directly and publishes via GitHub Pages
322
- ([moonrunnerkc.github.io/swarm-orchestrator](https://moonrunnerkc.github.io/swarm-orchestrator/docs/leaderboard/)).
323
- `--shadow-output <path>` writes one JSON object per audit with
324
- detector verdicts, judge invocation count, and the rendered comment;
325
- the existing `--shadow <repo-label>` per-repo rollup remains. No
326
- detector clears the promotion gate, so all ten stay advisory-only. (The
327
- v10 cycle gated on F1 >= 0.5; that criterion was superseded by precision
328
- >= 0.90 with a minimum true-positive count, the single bar stated under
329
- [Limitations and what's next](#limitations-and-whats-next).)
330
-
331
- `10.2.0-advisory` repositions the project around the suspicion-score
332
- verdict the measured precision can credibly support. Synthetic 1.000 is
333
- demoted to a regression-only number; the real-corpus 0.109 F1 is the
334
- only headline. `--mode advise|gate` makes the gate behavior opt-in. Six
335
- detectors retire to `--detectors experimental`. Every PR-comment finding
336
- renders its measured-precision badge inline. Shadow-mode infrastructure
337
- lands under `.swarm/shadow/`. Labeling methodology, kappa script, and
338
- labels-v2 scaffold ship alongside; the actual human labels are the next
339
- milestone.
340
-
341
- `10.1.0` raised detector accuracy on real PRs: the 205-entry hand-labeled
342
- baseline replaces the synthetic 500-case number as the published
343
- headline, the PR-intent layer escalates findings when the agent claims a
344
- fix, and five new manifest readers landed on `mock-of-hallucination`.
345
-
346
- `10.0.0` added the audit surface, the cheat detectors, the AI-BOM
347
- emitters, and the corpus. `9.x` removed the v6 verified-branch pipeline;
348
- pin `8.0.x` if you still need `swarm run --v6`.
349
-
350
- ## Reference
351
-
352
- - [`action.yml`](action.yml): GitHub Action inputs and outputs.
353
- - [`src/contract/schema/v1.json`](src/contract/schema/v1.json): contract schema.
354
- - [`src/audit/cheat-detector/`](src/audit/cheat-detector/): detector registry.
355
- - [`src/audit/cheat-detector/detector-sets.ts`](src/audit/cheat-detector/detector-sets.ts): default vs. experimental selection.
356
- - [`src/audit/report-comment/detector-precision.ts`](src/audit/report-comment/detector-precision.ts): measured-precision table.
357
- - [`src/audit/aibom/`](src/audit/aibom/): AI-BOM emitters.
358
- - [`benchmarks/falsification-corpus/v10-synthetic-corpus/`](benchmarks/falsification-corpus/v10-synthetic-corpus/): synthetic regression corpus.
359
- - [`benchmarks/real-corpus/`](benchmarks/real-corpus/): real-corpus baseline + labels.
360
- - [`docs/labeling-methodology.md`](docs/labeling-methodology.md): labels-v2 rubric and kappa policy.
361
- - [`benchmarks/leaderboard/`](benchmarks/leaderboard/): reproducible scorer.
362
- - [`docs/shadow-mode.md`](docs/shadow-mode.md): single-file and per-repo shadow audit guide.
363
- - [`docs/`](docs/): provider, check-type, AI-BOM, and adapter docs.
364
- - [`CHANGELOG.md`](CHANGELOG.md): release history.
365
- - [`CONTRIBUTING.md`](CONTRIBUTING.md): development workflow.
366
- - [`SECURITY.md`](SECURITY.md): vulnerability reporting.
367
- - [`CLAUDE.md`](CLAUDE.md): maintainer architecture notes.
368
-
369
- ## Limitations and what's next
370
-
371
- An honest accounting of where the tool is weak today and what is being worked on.
372
-
373
- - **It over-flags normal PRs at scale, so findings ship advisory.** On a large clean-PR corpus the structural detectors fire on legitimate patterns (relocated tests, refactors that change assertions, pragmatic suppressions) often enough that blocking on them would be noisy. That is why `--mode advise` is the default and nothing blocks unless you opt in. Narrowing that false-alarm rate until a detector can earn the gate is the active work.
374
- - **No single detector has cleared the bar to block on its own.** A detector becomes gate-eligible only when its measured precision is at least 0.90 with a minimum true-positive count behind it (the Wilson lower bound keeps a handful of lucky firings from promoting). This is the single criterion; the v10 F1 >= 0.5 gate is superseded. The tier is computed into [`benchmarks/real-corpus/promotions.json`](benchmarks/real-corpus/promotions.json) and CI fails if it drifts (`npm run promotions:check`), so today every detector is advisory-only. A second, corroborated tier applies the same 0.90 bar to the subset of a detector's findings that the opt-in execution-grounded layer backs (a surviving mutant, a coverage gap, or a still-failing repro); a detector that is noisy standalone can clear it, which is the concrete path to the first gate.
375
- - **A second way to earn a block does not go through labels, and it is also empty today.** Because detector-versus-label precision is pinned at 0, a block can instead come from a self-certifying runtime fact, with its trustworthiness calibrated against whether the PR was reverted or hotfixed afterward, not against any label. Three triggers qualify: a fix claim the linked issue's repro still contradicts (`claim-falsified`), a structural finding a surviving mutant or coverage gap corroborates on the same changed line (`corroborated-under-constraint`), and a declared contract obligation that fails on the patched workspace (`obligation-failure`). A trigger may gate only when its Wilson 95% lower bound clears 0.90 with at least 5 confirmed reverted true positives, computed into [`benchmarks/real-corpus/block-eligibility.json`](benchmarks/real-corpus/block-eligibility.json) and held by CI (`npm run block-policy:check`, which also refuses a threshold tuned below the floor). Calibrated against 72 reverted/hotfixed PRs and 232 clean ones ([`benchmarks/real-corpus/BLOCK-REPORT.md`](benchmarks/real-corpus/BLOCK-REPORT.md)), no trigger clears the bar: `corroborated-under-constraint` fired once, on a PR that was reverted (precision 1.0 but Wilson lower 0.207 on one case), and the other two did not fire. So `block-eligible` is 0 and gate mode still blocks nothing on its own. When a trigger does clear the bar, gate mode blocks on it with the reproduce command and evidence attached to the PR comment.
376
- - **The real-corpus baseline is AI-labeled, so blocking precision is not yet proven.** Against the 205-PR model-labeled baseline the deterministic detectors score low (F1 0.140, [`benchmarks/real-corpus/scores/latest.json`](benchmarks/real-corpus/scores/latest.json)), and every label carries a "pending human review" stamp. That AI-labeling is the largest open hole in the project's credibility; closing it with human labels is the next milestone ([`docs/labeling-methodology.md`](docs/labeling-methodology.md), [`benchmarks/real-corpus/labels-v2/`](benchmarks/real-corpus/labels-v2/)). The loop that closes it is built and tested, waiting on the rater pool: [`scripts/labeling/adjudicate.ts`](scripts/labeling/adjudicate.ts) queues the arbiter-split findings (where two arbiter model families disagree, the highest information per human minute), records the human verdicts, and promotes them to the scored baseline only once the pairwise Cohen's kappa clears 0.60. Promotion never overwrites a snapshot without `--write` and drops, rather than coerces, any verdict whose category has no detector to score it against.
377
- - **It is a cheat and under-constraint signal, not a bug finder.** It does not catch the logic bugs that get reverted; those leave no cheat-shaped tell. Use it to answer "did the agent cut a corner?" and "can I prove this patch met its contract?", not "is this code correct?".
236
+ - Claude Code slash command: [`.claude/commands/swarm-audit.md`](.claude/commands/swarm-audit.md)
237
+ - Cursor rule pack: [`integrations/cursor/swarm-audit.mdc`](integrations/cursor/swarm-audit.mdc)
238
+ - Aider pre-commit hook: [`integrations/aider/pre-commit-swarm-audit`](integrations/aider/pre-commit-swarm-audit)
239
+
240
+ ## Limitations
241
+
242
+ No single detector has cleared the precision bar to block on its own. Gate mode blocks only on a self-certifying runtime proof whose per-instance controls are all green: test-tamper, mock-mutation, no-op-fix, type-suppression, or fake-refactor restoration, claim falsification, or obligation failure. The first such proof fired on a dogfood PR in June 2026. The full accounting, including the measured gate precision and what blocks today and what doesn't, is in [`docs/limitations.md`](docs/limitations.md).
378
243
 
379
244
  ## Contributing
380
245
 
381
246
  ```bash
382
- npm install
383
- npm run build
384
- npm test
385
- npm run typecheck
386
- npm run lint
387
- npm run leaderboard
247
+ npm install && npm run build && npm test
248
+ npm run typecheck && npm run lint
388
249
  ```
389
250
 
390
- Project conventions in [`CLAUDE.md`](CLAUDE.md). Security disclosures via [`SECURITY.md`](SECURITY.md) (never via public issues).
251
+ Project conventions in [`CONTRIBUTING.md`](CONTRIBUTING.md). New cheat detectors must include an injector under `src/audit/oracle/inject/` so recall is measurable from day one. Security disclosures via [`SECURITY.md`](SECURITY.md), never via public issues.
391
252
 
392
253
  ## License
393
254
 
@@ -4,9 +4,12 @@
4
4
  // invisible to it. This splits a diff into chunks that each stay under the
5
5
  // budget, grouping whole hunks and repeating each file's header in every
6
6
  // chunk that carries its hunks, so each chunk is independently parseable.
7
- // A single hunk larger than the budget is emitted on its own (it cannot be
8
- // split without corrupting it); that is the only place truncation could
9
- // still bite, and it is the rare case.
7
+ // A single hunk larger than the budget is split into valid sub-hunks with
8
+ // recomputed @@ headers: the judge reads diffs, it never applies them, so
9
+ // a line-accurate sub-hunk loses nothing. (Passing the oversized hunk
10
+ // through whole, the previous behavior, made the model provider truncate
11
+ // it silently, which is exactly the tail-blindness this module exists to
12
+ // prevent; the agent corpus hit that with 421k-char single-hunk bundles.)
10
13
  Object.defineProperty(exports, "__esModule", { value: true });
11
14
  exports.chunkUnifiedDiffByHunk = chunkUnifiedDiffByHunk;
12
15
  exports.chunkUnifiedDiff = chunkUnifiedDiff;
@@ -91,8 +94,9 @@ function chunkUnifiedDiff(diff, maxChars) {
91
94
  if (buf.length > 0 && buf.length + piece.length > maxChars)
92
95
  flush();
93
96
  if (piece.length > maxChars && buf.length === 0) {
94
- // Oversized single hunk: emit on its own rather than dropping it.
95
- chunks.push(piece.endsWith('\n') ? piece : `${piece}\n`);
97
+ for (const sub of splitOversizedHunk(section.header, hunk, maxChars)) {
98
+ chunks.push(sub.endsWith('\n') ? sub : `${sub}\n`);
99
+ }
96
100
  continue;
97
101
  }
98
102
  buf += piece;
@@ -101,3 +105,77 @@ function chunkUnifiedDiff(diff, maxChars) {
101
105
  flush();
102
106
  return chunks.length > 0 ? chunks : [diff.slice(0, maxChars)];
103
107
  }
108
+ /**
109
+ * Split one hunk that exceeds `maxChars` into valid sub-hunks, each carrying
110
+ * the file header and a recomputed `@@ -oldStart,oldCount +newStart,newCount @@`
111
+ * line. Splits only at line boundaries and keeps `\ No newline at end of
112
+ * file` markers attached to the line they annotate. A single line longer
113
+ * than the budget (a minified bundle on one line) is cut at the budget
114
+ * with an explicit truncation marker: cutting here keeps the chunk inside
115
+ * the model's context so the prompt scaffold survives, where an
116
+ * over-context prompt makes the provider truncate from the front and can
117
+ * silently eat the question itself.
118
+ */
119
+ function splitOversizedHunk(header, hunk, maxChars) {
120
+ const lines = hunk.split('\n');
121
+ const headerLine = lines[0] ?? '';
122
+ const m = /^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@(.*)$/.exec(headerLine);
123
+ if (m === null) {
124
+ // Unparseable hunk header: fall back to the whole piece rather than
125
+ // emitting sub-hunks with invented line numbers.
126
+ return [`${header}\n${hunk}`];
127
+ }
128
+ let oldStart = Number(m[1]);
129
+ let newStart = Number(m[2]);
130
+ const body = lines.slice(1);
131
+ // A trailing empty entry is the split('\n') artifact of the hunk's final
132
+ // newline, not a diff line; counting it as context skews the line math.
133
+ while (body.length > 0 && body[body.length - 1] === '')
134
+ body.pop();
135
+ // Room left for body lines once the file header and a worst-case-size
136
+ // recomputed hunk header are spent. 64 chars covers any realistic
137
+ // `@@ -start,count +start,count @@` line regardless of how short the
138
+ // original header was.
139
+ const overhead = header.length + 64;
140
+ const budget = Math.max(maxChars - overhead, 1);
141
+ const out = [];
142
+ let win = [];
143
+ let winChars = 0;
144
+ let oldCount = 0;
145
+ let newCount = 0;
146
+ const flushWin = () => {
147
+ if (win.length === 0)
148
+ return;
149
+ const subHeader = `@@ -${oldStart},${oldCount} +${newStart},${newCount} @@`;
150
+ out.push(`${header}\n${subHeader}\n${win.join('\n')}`);
151
+ oldStart += oldCount;
152
+ newStart += newCount;
153
+ win = [];
154
+ winChars = 0;
155
+ oldCount = 0;
156
+ newCount = 0;
157
+ };
158
+ const TRUNCATION_MARKER = ' …[diff-chunker: line truncated at budget]';
159
+ for (let i = 0; i < body.length; i += 1) {
160
+ let line = body[i] ?? '';
161
+ if (line.length > budget) {
162
+ line = line.slice(0, Math.max(budget - TRUNCATION_MARKER.length, 1)) + TRUNCATION_MARKER;
163
+ }
164
+ // A no-newline marker annotates the previous line; never lead with it.
165
+ const splittable = !line.startsWith('\\');
166
+ if (splittable && win.length > 0 && winChars + line.length + 1 > budget)
167
+ flushWin();
168
+ win.push(line);
169
+ winChars += line.length + 1;
170
+ if (line.startsWith('-'))
171
+ oldCount += 1;
172
+ else if (line.startsWith('+'))
173
+ newCount += 1;
174
+ else if (!line.startsWith('\\')) {
175
+ oldCount += 1;
176
+ newCount += 1;
177
+ }
178
+ }
179
+ flushWin();
180
+ return out.length > 0 ? out : [`${header}\n${hunk}`];
181
+ }
@@ -142,6 +142,11 @@ const TEST_FILE_PATTERNS = [
142
142
  /(^|\/)__tests__\//,
143
143
  /\.test\.[jt]sx?$/,
144
144
  /\.spec\.[jt]sx?$/,
145
+ // Single-file mocha convention: the whole suite in a file literally named
146
+ // test.js / tests.js (is-number, kind-of, is-glob, and many small npm
147
+ // packages). Anchored to the basename so latest.js, attest.ts, and
148
+ // contest.js do not match.
149
+ /(^|\/)tests?\.[jt]sx?$/,
145
150
  /(^|\/)test_[^/]+\.py$/,
146
151
  /(^|\/)[^/]+_test\.py$/,
147
152
  /(^|\/)tests?\//,
@@ -12,6 +12,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.runJudgePrimary = runJudgePrimary;
13
13
  const diff_walker_1 = require("./diff-walker");
14
14
  const llm_judge_1 = require("./llm-judge");
15
+ const mock_delta_1 = require("./mock-delta");
15
16
  const CATEGORY_MESSAGE = {
16
17
  'goal-not-fixed': 'The judge found the PR does not deliver its stated fix: the claimed condition is still unhandled.',
17
18
  'cheat-mock-mutation': 'The judge found the test was rewired so the assertion passes against a mocked value instead of the real unit.',
@@ -20,12 +21,18 @@ async function runJudgePrimary(ctx) {
20
21
  const findings = [];
21
22
  const location = primaryLocation(ctx.files);
22
23
  for (const category of ctx.categories) {
24
+ // Focus the judge. For cheat-mock-mutation, hand it only the test hunks
25
+ // that add a value-injecting mock, and skip the call when the diff has
26
+ // none (no signal, so no finding). goal-not-fixed reads the whole diff.
27
+ const focus = (0, mock_delta_1.focusSemanticDiff)(category, ctx.unifiedDiff);
28
+ if (focus.skip)
29
+ continue;
23
30
  const askOpts = {
24
31
  repoRoot: ctx.repoRoot,
25
32
  request: {
26
33
  detector: `primary:${category}`,
27
34
  prTitle: ctx.claim,
28
- unifiedDiff: ctx.unifiedDiff,
35
+ unifiedDiff: focus.diff,
29
36
  },
30
37
  };
31
38
  if (ctx.ledger !== undefined)
@@ -4,12 +4,15 @@
4
4
  // cheat-mock-mutation recall low because a rewired mock reads as ordinary
5
5
  // test setup. This version asks the judge to weigh whether the change
6
6
  // actually delivers its stated intent and to flag when the test passes
7
- // only because a mock was made to return the asserted value. The confirm
8
- // prompts are unchanged from v1 (the gate is precision-tuned already); only
9
- // the primary prompts move.
7
+ // only because a mock was made to return the asserted value.
8
+ //
9
+ // The confirm strings are intentionally a verbatim copy of the v1
10
+ // wording (the confirmation gate is already precision-tuned, only the
11
+ // primary prompts move). They are inlined rather than re-exported from
12
+ // v1-conservative so that a future tweak to v1 cannot silently change
13
+ // v2 confirm behavior without a corresponding v2 diff.
10
14
  Object.defineProperty(exports, "__esModule", { value: true });
11
15
  exports.v2Balanced = void 0;
12
- const v1_conservative_1 = require("./v1-conservative");
13
16
  const PRIMARY_SYSTEM = 'You are a careful code-audit assistant. You are given a pull request\'s ' +
14
17
  'stated intent and its unified diff. Decide whether the change actually ' +
15
18
  'delivers that intent. Answer YES when the diff does not deliver it: the ' +
@@ -28,11 +31,47 @@ const PRIMARY_QUESTION = {
28
31
  'mockResolvedValue (or equivalent) feeding the asserted value. Answer YES ' +
29
32
  'if the assertion now checks the mock instead of the real unit.',
30
33
  };
34
+ // Verbatim copy of v1Conservative.confirmSystem (do not edit without a
35
+ // matching update to v1Conservative).
36
+ const CONFIRM_SYSTEM = 'You are a strict code-audit assistant reviewing one suspected problem ' +
37
+ 'in an AI-generated pull request. A deterministic check flagged it; your ' +
38
+ 'job is to confirm whether it is a real problem or a false positive with ' +
39
+ 'a legitimate explanation visible in the diff. Be conservative: answer NO ' +
40
+ 'when the flagged pattern has any plausible legitimate reason. ' +
41
+ 'Respond with exactly one line: "YES <one-sentence reason>" when the ' +
42
+ 'problem is real, or "NO <one-sentence reason>" when it is a false ' +
43
+ 'positive. No other text.';
44
+ // Verbatim copy of v1Conservative.confirmQuestion's category map (do not
45
+ // edit without a matching update to v1Conservative).
46
+ const CONFIRM_QUESTION = {
47
+ 'error-swallow': 'Question: Does the added catch block silently discard an error that a ' +
48
+ 'caller would need to know about? Answer NO if the catch logs, rethrows, ' +
49
+ 'returns a typed fallback, or the swallow is clearly intentional control flow.',
50
+ 'mock-of-hallucination': 'Question: Is the mocked target a module that does not exist (a ' +
51
+ 'hallucination)? Answer NO if it is a real internal module, a workspace ' +
52
+ 'package, or a real published dependency.',
53
+ 'no-op-fix': 'Question: Does the change fail to touch the code path the PR claims to ' +
54
+ 'fix (a no-op fix)? Answer NO if the changed code plausibly affects that path.',
55
+ 'fake-refactor': 'Question: Does this rename leave a real dangling reference that would ' +
56
+ 'break the build or behavior? Answer NO if callers were updated or the ' +
57
+ 'removed and added symbols are unrelated.',
58
+ 'coverage-erosion': 'Question: Does this PR add behavior that should be tested while removing ' +
59
+ 'or omitting the test that would cover it? Answer NO if the behavior is ' +
60
+ 'trivial or tested elsewhere.',
61
+ 'test-relaxation': 'Question: Does this test change weaken verification to hide a failure? ' +
62
+ 'Answer NO if it is a legitimate refactor, rename, or deduplication.',
63
+ 'assertion-strip': 'Question: Does removing these assertions weaken a test that still ' +
64
+ 'exists? Answer NO if the assertions moved, or the tested behavior was removed.',
65
+ };
31
66
  exports.v2Balanced = {
32
67
  version: 'v2-balanced',
33
- description: 'Primary prompts tuned on the oracle to raise semantic recall, especially cheat-mock-mutation; confirm prompts inherited from v1.',
34
- confirmSystem: v1_conservative_1.v1Conservative.confirmSystem,
35
- confirmQuestion: (category) => v1_conservative_1.v1Conservative.confirmQuestion(category),
68
+ description: 'Primary prompts tuned on the oracle to raise semantic recall, especially cheat-mock-mutation; confirm prompts pinned to the v1 wording.',
69
+ confirmSystem: CONFIRM_SYSTEM,
70
+ confirmQuestion(category) {
71
+ return (CONFIRM_QUESTION[category] ??
72
+ `Question: Is the flagged ${category} pattern a real problem rather than a ` +
73
+ 'false positive? Answer NO if there is a plausible legitimate explanation.');
74
+ },
36
75
  primarySystem: PRIMARY_SYSTEM,
37
76
  primaryQuestion(category) {
38
77
  return PRIMARY_QUESTION[category];