agent-roast 0.11.0 → 0.12.1
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.
- package/README.md +97 -492
- package/dist/{chunk-WMHB65AL.js → chunk-AP5Y4CH6.js} +712 -63
- package/dist/cli.js +243 -14
- package/dist/index.d.ts +105 -2
- package/dist/index.js +43 -1
- package/package.json +12 -4
package/README.md
CHANGED
|
@@ -5,543 +5,148 @@
|
|
|
5
5
|
<img src="docs/assets/logo.svg" alt="agent-roast" width="380">
|
|
6
6
|
</picture>
|
|
7
7
|
|
|
8
|
-
<h2>
|
|
8
|
+
<h2>Your AI agent made the tests pass.<br>Did it fix the code, or the tests?</h2>
|
|
9
9
|
|
|
10
10
|
<p>
|
|
11
|
-
<a href="https://github.com/Nari-Ab/agent-roast/actions/workflows/test.yml"><img src="https://github.com/Nari-Ab/agent-roast/actions/workflows/test.yml/badge.svg" alt="
|
|
11
|
+
<a href="https://github.com/Nari-Ab/agent-roast/actions/workflows/test.yml"><img src="https://github.com/Nari-Ab/agent-roast/actions/workflows/test.yml/badge.svg" alt="CI"></a>
|
|
12
12
|
<a href="https://www.npmjs.com/package/agent-roast"><img src="https://img.shields.io/npm/v/agent-roast?color=cb3837&logo=npm&logoColor=white" alt="npm"></a>
|
|
13
|
-
<a href="https://www.npmjs.com/package/agent-roast"><img src="https://img.shields.io/npm/
|
|
14
|
-
<a href="https://nari-ab.github.io/agent-roast/"><img src="https://img.shields.io/badge/site-live-1f9d55" alt="site"></a>
|
|
13
|
+
<a href="https://www.npmjs.com/package/agent-roast"><img src="https://img.shields.io/npm/dm/agent-roast?label=downloads&color=0969da" alt="downloads"></a>
|
|
15
14
|
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-0969da" alt="license"></a>
|
|
15
|
+
<img src="https://img.shields.io/badge/runs-offline%20·%20no%20tokens-1f9d55" alt="offline, no tokens">
|
|
16
16
|
</p>
|
|
17
17
|
|
|
18
18
|
</div>
|
|
19
19
|
|
|
20
|
-
**
|
|
20
|
+
**agent-roast** catches the changes that turn a red build green by weakening the *checks* instead of fixing the *code*: a
|
|
21
|
+
loosened assertion, a skipped or deleted test, a lowered coverage threshold, a CI step allowed to fail. A linter cannot see
|
|
22
|
+
these, because every new line looks fine on its own. agent-roast compares the **before and after** of each change, as an
|
|
23
|
+
agent hook, a Git hook and a pull request check. It is deterministic and offline: no model, no tokens, no network.
|
|
21
24
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
exactly these moves. It is offline, runs no project code, and needs only Git. It was built to
|
|
27
|
-
review AI-assisted changes, but it cannot tell who wrote a change, and the measurements below
|
|
28
|
-
show these moves are not specific to AI.
|
|
29
|
-
|
|
30
|
-
```bash
|
|
31
|
-
npx agent-roast replay # your last 50 commits, one at a time
|
|
32
|
-
npx agent-roast review --base origin/main # a pull request
|
|
33
|
-
npx agent-roast --staged # what you are about to commit
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
| Rule | Fires when | By default |
|
|
37
|
-
| --- | --- | --- |
|
|
38
|
-
| `assertion-weakened` | an exact check is replaced by a weaker one: `toBe(42)` → `toBeDefined()`, `assert x == 1` → `assert x`, `isinstance(...)` → `is not None` | blocks |
|
|
39
|
-
| `guard-weakened` | coverage threshold lowered, `strict`/`noImplicitAny` off, lint rule downgraded, an existing CI step allowed to fail, a workflow that stops running tests or stops running on push and pull requests, a test script that cannot fail | blocks |
|
|
40
|
-
| `assertion-removed` | a test keeps its name but loses assertions | warns |
|
|
41
|
-
| `test-removed` | a test (or test file) disappears and no identical body appears elsewhere | warns |
|
|
42
|
-
| `expectation-rewritten` | an expected value changed, test inputs did not, and production code changed in the same PR | warns |
|
|
43
|
-
| `test-skip`, `test-focus`, `trivial-assert`, `swallowed-error`, `type-escape`, `stub-abandonment` | the classic shortcuts, compared before/after so old debt does not block | skip, focus and trivial-assert block |
|
|
44
|
-
|
|
45
|
-
Renames, moved tests, parametrization, assertions moved into a new helper, and formatter
|
|
46
|
-
rewraps (prettier, black) are recognized and not reported. So is a skip that depends on the
|
|
47
|
-
environment: `test.skip(isMobile, 'reason')` and Playwright's other condition forms,
|
|
48
|
-
`if not has_gpu(): pytest.skip(...)`, and a skip inside a fixture or helper. A skip that names a
|
|
49
|
-
test, stands alone in a test body or depends on `CI` is still reported. Intentional cases can be accepted
|
|
50
|
-
one by one with an expiring, reviewed exception (see below). Policy is read from the base
|
|
51
|
-
branch, so a PR cannot switch the rules off for itself.
|
|
52
|
-
|
|
53
|
-
### How well does it work?
|
|
54
|
-
|
|
55
|
-
Measured, not promised; method, corpus and raw numbers are in
|
|
56
|
-
[docs/benchmarks.md](docs/benchmarks.md). **Short version: weakened checks are rare, usually
|
|
57
|
-
deliberate, and not specific to AI. The tool finds them; a person decides.**
|
|
58
|
-
|
|
59
|
-
- **Labeled changes** (`benchmarks/cases.mjs`): 43 cheating edits across JS/TS, Python, Go and
|
|
60
|
-
CI/config files are caught and 36 harmless look-alikes (reformat, rename, move, parametrize,
|
|
61
|
-
helper extraction, raised thresholds, CI restructuring, skips that depend on the environment) stay silent. The cases were written
|
|
62
|
-
together with the detector, so they guard against regressions; they do not prove accuracy.
|
|
63
|
-
Three known gaps are listed in the file and still open: mocking the module under test,
|
|
64
|
-
hardcoding the tested value, and vacuous matchers such as `expect.anything()`.
|
|
65
|
-
- **Five mature open-source projects** (commander.js, execa, click, requests, zod; 606 commits
|
|
66
|
-
that touch tests or CI/config): the blocking rules fired on 3 commits (0.5%), all of them
|
|
67
|
-
deliberate. `assertion-removed` fired on 5 (0.8%), including one real defect:
|
|
68
|
-
a refactor in `requests` dropped the `assert` keyword and left a comparison with no effect.
|
|
69
|
-
- **AI-signed commits in 166 public repositories** (signatures from Claude, Copilot, Cursor,
|
|
70
|
-
Codex, Devin and aider; 12,645 commits that touch tests or CI/config):
|
|
71
|
-
|
|
72
|
-
| | AI-signed | Other commits |
|
|
73
|
-
| --- | ---: | ---: |
|
|
74
|
-
| Repositories | 160 | 141 |
|
|
75
|
-
| Commits reviewed | 6,895 | 5,750 |
|
|
76
|
-
| Blocking rules | 0.3% (20) | 0.3% (16) |
|
|
77
|
-
| `assertion-removed` (warns) | 2.4% | 2.2% |
|
|
78
|
-
| `test-removed` (warns) | 15.4% | 12.7% |
|
|
79
|
-
| `expectation-rewritten` (warns) | 5.7% | 5.5% |
|
|
80
|
-
|
|
81
|
-
Compared inside the same repository (135 repositories with both kinds), the odds of a
|
|
82
|
-
blocking finding in an AI-signed commit are 0.81 times those of another commit (95% CI
|
|
83
|
-
0.40-1.67): no sign that AI-signed commits weaken checks more often. Claude accounts for
|
|
84
|
-
5,380 of the AI-signed commits; the other agents are too few to compare.
|
|
85
|
-
- **Hand-read precision.** Findings were read without knowing who wrote the commit (one
|
|
86
|
-
reader, an AI assistant; not independently verified). On repositories the detector had never
|
|
87
|
-
seen, the 11 blocking findings were 2 unexplained relaxations (TypeScript strictness switched
|
|
88
|
-
off in commits titled "Changes"), 7 deliberate and explained changes, and 2 false alarms. Of
|
|
89
|
-
24 `assertion-removed` warnings, 1 was an unexplained weakening, 12 deliberate, 6 unclear and
|
|
90
|
-
5 false alarms. So most findings are decisions a reviewer should see rather than mistakes,
|
|
91
|
-
and roughly one in five is a false alarm. Earlier rounds (76 findings) were used to fix the
|
|
92
|
-
detector and are not counted here.
|
|
93
|
-
|
|
94
|
-
Caveats. A signature shows a tool was involved, not how much, and unsigned AI help in the
|
|
95
|
-
"other" group would hide a difference. The repositories came from searching for those
|
|
96
|
-
signatures, so many are small personal projects. This measures what was committed, not what an
|
|
97
|
-
agent tries mid-session: a weakened check fixed before the commit is invisible. Labels are one
|
|
98
|
-
reader's opinion. Three small fixes (assertion helpers, throwing test-library queries,
|
|
99
|
-
scheduled-only workflows) were added after the labeling and have regression cases, but were not
|
|
100
|
-
re-labeled.
|
|
101
|
-
|
|
102
|
-
It is a **review aid**: it cannot prove who wrote a change or that code is correct, and it
|
|
103
|
-
reads text, not program semantics. A green result is not a quality certificate.
|
|
104
|
-
|
|
105
|
-
**Want to see what it says about your repository?** [docs/pilot.md](docs/pilot.md) has a
|
|
106
|
-
two-minute replay of your last commits, an advisory workflow that never fails a pull request, and a
|
|
107
|
-
feedback form. Counts are enough; no code is needed.
|
|
108
|
-
|
|
109
|
-
### Quick start
|
|
110
|
-
|
|
111
|
-
```bash
|
|
112
|
-
# Everything that is true of the code you have now
|
|
113
|
-
npx agent-roast check
|
|
114
|
-
npx agent-roast explain swallowed-error
|
|
115
|
-
|
|
116
|
-
# Adopt in an existing project without blocking on historical debt
|
|
117
|
-
npx agent-roast check --write-baseline .agentroast-baseline.json
|
|
118
|
-
npx agent-roast check --baseline .agentroast-baseline.json
|
|
119
|
-
|
|
120
|
-
# Optional: score a repository's history
|
|
121
|
-
npx agent-roast --all --verbose
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
A finding includes its file and line, the evidence, and a concrete next step.
|
|
125
|
-
`--format json|markdown|sarif|github` are available; terminal output shows the 20 most
|
|
126
|
-
important findings (rules that block first, then the before/after rules, then the noisier
|
|
127
|
-
line patterns).
|
|
128
|
-
|
|
129
|
-
### Replay recent history
|
|
130
|
-
|
|
131
|
-
```bash
|
|
132
|
-
# Review each of the last 50 commits on its own, as a pull-request check would have seen it
|
|
133
|
-
npx agent-roast replay
|
|
134
|
-
npx agent-roast replay --last 200 --json # more history, every finding
|
|
135
|
-
npx agent-roast replay --all-rules # also list line patterns such as `as any`
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
`replay` answers "what would this have said about our last 50 changes?" before you put it in CI.
|
|
139
|
-
It follows the first-parent history, so a squash, a merge and a direct commit each count as one
|
|
140
|
-
change, reviews each against its parent with the policy at that parent, and lists the commits
|
|
141
|
-
that would have been blocked or drew a warning about a weakened check. It never fails because of a
|
|
142
|
-
finding, and a commit too large to read is reported, not fatal. Line patterns (`type-escape`,
|
|
143
|
-
`swallowed-error`, `stub-abandonment`) are counted and listed only with `--all-rules`.
|
|
25
|
+
<p align="center">
|
|
26
|
+
<img src="docs/assets/demo-terminal.svg" alt="Terminal: npx agent-roast demo reports a skipped test, a trivial assertion, a weakened assertion, a CI step allowed to fail and two lowered coverage thresholds, and blocks the change" width="820">
|
|
27
|
+
<br><sub>Real output of <code>npx agent-roast demo</code>, shortened. The scenario is scripted; the review is not.</sub>
|
|
28
|
+
</p>
|
|
144
29
|
|
|
145
|
-
|
|
30
|
+
## Try it
|
|
146
31
|
|
|
147
32
|
```bash
|
|
148
|
-
#
|
|
149
|
-
agent-roast review
|
|
150
|
-
agent-roast review --base origin/main --format markdown
|
|
151
|
-
|
|
152
|
-
# Export a report for tools that consume SARIF 2.1.0
|
|
153
|
-
agent-roast review --base origin/main --format sarif > agentroast.sarif
|
|
154
|
-
agent-roast check --format sarif > current-code.sarif
|
|
33
|
+
npx agent-roast demo # ten seconds: a scripted example in a temporary repository, nothing of yours is read
|
|
34
|
+
npx agent-roast # review what you have not committed yet (on a clean tree: replay your last commits)
|
|
155
35
|
```
|
|
156
36
|
|
|
157
|
-
|
|
158
|
-
handler inside a `catch` is detectable even when no suspicious line was added.
|
|
159
|
-
It reports new findings, existing findings, and patterns no longer detected. Only
|
|
160
|
-
new blocking findings fail the check; no manually generated baseline is required.
|
|
161
|
-
Deleting a file is reported as disappearance of a pattern, not proof of a correct fix.
|
|
162
|
-
|
|
163
|
-
Source is read directly from immutable Git blobs at the merge-base and head. The
|
|
164
|
-
worktree and index are left untouched, and project code, hooks, diff programs and
|
|
165
|
-
package scripts are never executed. Use `check` for uncommitted working files, and
|
|
166
|
-
`--staged` for the index. Source comparison and policy are pinned to the reported SHAs.
|
|
167
|
-
|
|
168
|
-
Policy comes from the explicitly supplied **base ref tip**; source comparison starts
|
|
169
|
-
at the merge-base. Candidate changes to `.agentroastrc.json`, `.agentroastrc` or
|
|
170
|
-
`agent-roast.config.json` are reported and ignored for this review. Adopt policy
|
|
171
|
-
changes through separate human review. An explicit `--fail-on` overrides blocking
|
|
172
|
-
severity but does not enable rules disabled by base policy.
|
|
37
|
+
## Stop your coding agent from doing it
|
|
173
38
|
|
|
174
39
|
```bash
|
|
175
|
-
|
|
176
|
-
agent-roast review --base origin/main --advisory
|
|
177
|
-
# Gate the shortcuts the team agrees matter
|
|
178
|
-
agent-roast review --base origin/main --fail-on test-skip,trivial-assert,swallowed-error
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
Matching uses rule, source snippet and occurrence count within a file pair. Line
|
|
182
|
-
shifts and Git-recognized pure renames do not turn old debt into new debt. A second
|
|
183
|
-
identical occurrence is new. This is not semantic tracking: rewritten snippets or
|
|
184
|
-
language changes can require fresh review, and Git's rename recognition is heuristic.
|
|
185
|
-
|
|
186
|
-
Limits: 1,000 changed files, 2 MiB per source blob, 50 MiB across inspected versions,
|
|
187
|
-
30 seconds per Git command. Symlinked/binary/oversized source and missing comparison
|
|
188
|
-
objects yield exit 2, never a clean pass. Partial clones and ambiguous multiple
|
|
189
|
-
merge-bases are unsupported; fetch complete local history (`fetch-depth: 0`). The
|
|
190
|
-
report counts excluded and unsupported changes explicitly. Only changed supported
|
|
191
|
-
files are compared; this is not a whole-repository safety certification.
|
|
192
|
-
|
|
193
|
-
SARIF includes rule metadata, relative locations, stable occurrence fingerprints
|
|
194
|
-
and whether the scan completed. Source snippets are omitted from SARIF. JSON and
|
|
195
|
-
Markdown reports contain evidence snippets and should be handled as source code.
|
|
196
|
-
SARIF output is generated offline; uploading it is a separate, optional operation.
|
|
197
|
-
GitHub supports SARIF ingestion, but this release has not been tested with a live
|
|
198
|
-
code-scanning upload. Some findings on unchanged lines (including deletion-only
|
|
199
|
-
regressions) may appear in the report/summary rather than as an inline PR annotation.
|
|
200
|
-
Use a separate SARIF category for introduced-findings review; it does not represent
|
|
201
|
-
a full scan of all outstanding alerts.
|
|
202
|
-
|
|
203
|
-
Install 0.11.0: `npx agent-roast@0.11.0 review --base origin/main`. For local development,
|
|
204
|
-
run `npm run build` and use `node dist/cli.js`.
|
|
205
|
-
|
|
206
|
-
### Precise exceptions (since 0.8.0)
|
|
207
|
-
|
|
208
|
-
Use `npx agent-roast@0.11.0 check --format json`, or build this checkout with
|
|
209
|
-
`npm run build` and run `node dist/cli.js`.
|
|
210
|
-
|
|
211
|
-
Some skipped integrations or abstract stubs are intentional. In 0.8.0 you can accept
|
|
212
|
-
one exact finding without switching off the rule or ignoring its whole file:
|
|
213
|
-
|
|
214
|
-
1. Run `node dist/cli.js check --format json` and inspect the evidence. Findings
|
|
215
|
-
include a stable `fingerprint` (rule, relative filename and trimmed source snippet).
|
|
216
|
-
2. Add a reviewed `exceptions` entry to the root `.agentroastrc.json`, with the
|
|
217
|
-
fingerprint, the **total** allowed occurrence count, a reason and an expiry date.
|
|
218
|
-
3. Run the check again. Accepted findings remain visible with their reason/date;
|
|
219
|
-
additional duplicate occurrences still block. Expired exceptions stop applying.
|
|
220
|
-
Use a full `check` to revisit old findings; `review` continues gating newly
|
|
221
|
-
introduced debt, so expiration does not turn unchanged old debt into a new finding.
|
|
222
|
-
|
|
223
|
-
```json
|
|
224
|
-
{
|
|
225
|
-
"exceptions": [
|
|
226
|
-
{
|
|
227
|
-
"fingerprint": "f4d1212234f00381d0eaed359e3229fe4ff47388affc9eac0d0d89daffea13d6",
|
|
228
|
-
"count": 1,
|
|
229
|
-
"reason": "Integration sandbox unavailable; restore under ENG-42",
|
|
230
|
-
"expires": "2026-11-15"
|
|
231
|
-
}
|
|
232
|
-
]
|
|
233
|
-
}
|
|
234
|
-
```
|
|
235
|
-
|
|
236
|
-
The example fingerprint is for `it.skip('temporary');` in `auth.test.ts`; copy the
|
|
237
|
-
fingerprint from **your** report. Expiry includes the specified day in UTC. Changing
|
|
238
|
-
the rule, path or source snippet requires review again. Counts from an exception and
|
|
239
|
-
baseline do not add together to accept extra identical occurrences.
|
|
240
|
-
|
|
241
|
-
For committed/staged context reviews the allowance must already exist in the trusted
|
|
242
|
-
base/HEAD policy. A candidate PR or unstaged config cannot approve itself; first
|
|
243
|
-
commits use built-in defaults. `check` intentionally reads the current root policy.
|
|
244
|
-
Use `--ignore-exceptions` with `check`, `review` or `--staged` to inspect accepted
|
|
245
|
-
findings again. This flag does not disable a separately supplied baseline.
|
|
246
|
-
Exceptions apply to these context/current-source workflows; historical and legacy
|
|
247
|
-
added-line audits do not apply them. A full scan can use baseline adoption for old
|
|
248
|
-
debt, while exceptions record temporary reviewed decisions about particular findings.
|
|
249
|
-
|
|
250
|
-
JSON/Markdown/terminal reports separate accepted exceptions from actionable findings.
|
|
251
|
-
SARIF retains accepted records with `suppressions` and their justification/expiry;
|
|
252
|
-
JSON and SARIF include stable occurrence identities. SARIF omits code snippets but
|
|
253
|
-
includes exception reasons, so review those before sharing reports.
|
|
254
|
-
|
|
255
|
-
An offline editor schema is included as `schema.json` in the npm package. When using
|
|
256
|
-
a local dev dependency, set `$schema` to `./node_modules/agent-roast/schema.json`.
|
|
257
|
-
Ignore policies are bounded to 1,000 patterns of at most 1,024 characters each.
|
|
258
|
-
Unknown settings, invalid severities and malformed exceptions fail with exit 2.
|
|
259
|
-
Root policy must be a regular UTF-8 file no larger than 64 KiB. Simple ignore globs
|
|
260
|
-
support `*`, `?`, `**` and directory prefixes; they are not full `.gitignore` syntax.
|
|
261
|
-
|
|
262
|
-
### Baseline adoption
|
|
263
|
-
|
|
264
|
-
A baseline explicitly accepts the findings present at adoption time. It does not fix
|
|
265
|
-
or hide new code. Fingerprints use the rule, repository-relative filename and source
|
|
266
|
-
snippet; line numbers are excluded so adding clean code above a finding does not
|
|
267
|
-
break it. Occurrence counts ensure a second identical shortcut is still new. Moving
|
|
268
|
-
a finding to another file or changing its snippet requires a fresh review. Baselines
|
|
269
|
-
contain hashes and counts, not source code. The report shows both accepted and new
|
|
270
|
-
findings and counts accepted findings no longer present.
|
|
271
|
-
|
|
272
|
-
Baseline use is explicit (`--baseline`), and writes refuse to overwrite existing
|
|
273
|
-
files. To refresh accepted debt, inspect the full scan and write a **new** baseline
|
|
274
|
-
file for review. Treat baseline changes as policy changes in code review. Do not
|
|
275
|
-
silently regenerate one in CI. Input/output baseline paths are relative to the
|
|
276
|
-
directory passed to `check` (or the current directory if omitted).
|
|
277
|
-
|
|
278
|
-
`check` scans the repository containing the supplied path, applies its root config,
|
|
279
|
-
respects Git ignores for untracked files, and excludes generated dependencies/build
|
|
280
|
-
outputs. It checks working files, while `--staged` checks the index. Git submodules
|
|
281
|
-
are not recursively scanned. A supported file that is binary, too large, unreadable
|
|
282
|
-
or a symlink makes the scan incomplete (exit 2); incomplete scans cannot create a
|
|
283
|
-
baseline. Limits are 2 MiB per source file and 50 MiB per scan. Intentional generated
|
|
284
|
-
or unsupported input should be excluded in `ignorePaths`, not silently counted clean.
|
|
285
|
-
|
|
286
|
-
History auditing remains available when you want an overview of prior shortcuts:
|
|
287
|
-
|
|
288
|
-
What a report looks like (a real repository audit, trimmed):
|
|
289
|
-
|
|
290
|
-
```text
|
|
291
|
-
agent-roast — git discipline audit (304 AI commits in Soup)
|
|
292
|
-
|
|
293
|
-
SCORE: 55 / 100 · The Panic Looper
|
|
294
|
-
Prone to rapid-fire fix and revert thrashing cycles when wrestling stubborn bugs.
|
|
295
|
-
|
|
296
|
-
INFRACTIONS DETECTED (42)
|
|
297
|
-
|
|
298
|
-
● panic-loop (15) — consecutive quick fixes < 15m apart
|
|
299
|
-
latest: 3df29a1 -> a910f21 (3m apart)
|
|
300
|
-
|
|
301
|
-
● type-escape (19) — compiler type bypasses in added lines
|
|
302
|
-
e.g. src/client.ts:42 — response.body as any
|
|
303
|
-
|
|
304
|
-
● test-skip (6) — disabled or skipped test suites
|
|
305
|
-
e.g. tests/test_outbound.py:18 — pytest.skip(...)
|
|
306
|
-
|
|
307
|
-
● trivial-assert (2) — tautological assertions faking test coverage
|
|
308
|
-
e.g. tests/auth.test.ts:31 — expect(true).toBe(true)
|
|
309
|
-
|
|
310
|
-
● stub-abandonment (1) — incomplete stubs left behind
|
|
311
|
-
e.g. src/payment.ts:18 — throw new Error("Not implemented")
|
|
312
|
-
|
|
313
|
-
● swallowed-error (2) — empty catch blocks or except: pass
|
|
40
|
+
npx agent-roast hook install # Claude Code, Codex and Cursor
|
|
314
41
|
```
|
|
315
42
|
|
|
316
|
-
|
|
43
|
+
For Claude Code there is also a plugin: `/plugin install agent-roast --marketplace Nari-Ab/agent-roast`.
|
|
317
44
|
|
|
318
|
-
|
|
45
|
+
When the agent is about to finish, agent-roast reviews everything it changed. If a check got weaker, the agent is **sent back
|
|
46
|
+
once**, with the file, the line and the before and after, and told to fix the code instead. If it tries again, you get a
|
|
47
|
+
warning rather than an endless loop. Anything agent-roast cannot check lets the agent finish: it never breaks a session. An
|
|
48
|
+
intended change is accepted by committing it. Setup for each agent, what it does not do, and how to pause it:
|
|
49
|
+
[docs/integrations.md](docs/integrations.md).
|
|
319
50
|
|
|
320
|
-
|
|
51
|
+
> The agent hooks follow each agent's documented hook protocol and are tested with simulated hook input, not yet in long live
|
|
52
|
+
> sessions with every agent. Reports of what you see are welcome.
|
|
321
53
|
|
|
322
|
-
|
|
54
|
+
## Check every pull request
|
|
323
55
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
"trivial-assert": "warn",
|
|
338
|
-
"stub-abandonment": "warn"
|
|
339
|
-
},
|
|
340
|
-
"scoreThreshold": 70
|
|
341
|
-
}
|
|
342
|
-
```
|
|
343
|
-
|
|
344
|
-
---
|
|
345
|
-
|
|
346
|
-
### Check code before committing
|
|
347
|
-
|
|
348
|
-
```bash
|
|
349
|
-
npm install --save-dev agent-roast
|
|
350
|
-
npx agent-roast --staged
|
|
351
|
-
npx agent-roast init --hook
|
|
56
|
+
```yaml
|
|
57
|
+
# .github/workflows/agent-roast.yml
|
|
58
|
+
name: Agent Roast
|
|
59
|
+
on: pull_request
|
|
60
|
+
permissions: { contents: read, pull-requests: write }
|
|
61
|
+
jobs:
|
|
62
|
+
audit:
|
|
63
|
+
runs-on: ubuntu-latest
|
|
64
|
+
steps:
|
|
65
|
+
- uses: actions/checkout@v4
|
|
66
|
+
with: { fetch-depth: 0 }
|
|
67
|
+
- uses: Nari-Ab/agent-roast@v1
|
|
68
|
+
with: { comment: true } # one comment, updated on every push; add `advisory: true` to try it without failing PRs
|
|
352
69
|
```
|
|
353
70
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
The older 0.6.0 `--staged` command checks only added index lines. Since
|
|
359
|
-
0.8.0, it also reads Git's temporary candidate index during real `commit -a` / `--only`
|
|
360
|
-
hooks. An alternate `GIT_INDEX_FILE` outside this checkout's Git metadata is refused
|
|
361
|
-
with exit 2; it cannot silently redirect the check to a different commit.
|
|
71
|
+
Also: `npx agent-roast review --base origin/main` in any CI, `npx agent-roast init --hook` or the
|
|
72
|
+
[pre-commit framework](docs/integrations.md#the-pre-commit-framework) for the next commit, and SARIF output for code scanning.
|
|
73
|
+
Policy is read from the **base** branch, so a pull request cannot switch off the rules that review it. Before you decide,
|
|
74
|
+
`npx agent-roast replay` shows what it would have said about your last 50 commits.
|
|
362
75
|
|
|
363
|
-
|
|
364
|
-
are reported; adopt policy changes in a separately reviewed commit. With no HEAD,
|
|
365
|
-
the first commit uses built-in defaults; use `--fail-on` for an explicit override.
|
|
366
|
-
For example: `node dist/cli.js review --staged --fail-on test-skip,test-focus,swallowed-error`.
|
|
367
|
-
Source is pinned by Git blob IDs. If HEAD or the staged inventory changes before the
|
|
368
|
-
scan finishes, it exits 2 and asks for a rerun. The report's `source` is `index`;
|
|
369
|
-
`headSha` is an `INDEX:` snapshot digest, not a commit SHA, and an unborn base is `EMPTY`.
|
|
370
|
-
The JSON schema version for context reviews is now `1.1.0`.
|
|
76
|
+
## What it catches
|
|
371
77
|
|
|
372
|
-
|
|
373
|
-
|
|
78
|
+
| Rule | Fires when | By default |
|
|
79
|
+
| --- | --- | :-: |
|
|
80
|
+
| `assertion-weakened` | an exact check is replaced by a weaker one: `toBe(42)` → `toBeDefined()`, `assert x == 1` → `assert x` | blocks |
|
|
81
|
+
| `guard-weakened` | a coverage threshold is lowered, `strict` is switched off, a lint rule is downgraded, a CI step may fail, a workflow stops running tests | blocks |
|
|
82
|
+
| `test-skip` | a test is switched off: `it.skip`, `@pytest.mark.skip`, `t.Skip`, `#[ignore]`, `@Disabled`, `[Ignore]`, `xit`, ... | blocks |
|
|
83
|
+
| `test-focus`, `trivial-assert` | `.only` / `fit`; `expect(true).toBe(true)` and its relatives in ten languages | blocks |
|
|
84
|
+
| `assertion-removed`, `test-removed`, `expectation-rewritten` | a test loses assertions, disappears, or its expected value is edited next to production code | warns |
|
|
85
|
+
| `swallowed-error`, `type-escape`, `stub-abandonment` | `catch {}` / `except: pass`, `as any` / `# type: ignore`, `throw new Error("Not implemented")` | warns |
|
|
374
86
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
87
|
+
Renames, moved tests, parametrization, moved assertions and reformatting are recognized and not reported, and neither is a skip
|
|
88
|
+
that depends on the environment (`if testing.Short() { t.Skip() }`, `test.skip(isMobile, ...)`). JavaScript/TypeScript, Python
|
|
89
|
+
and Go get the full before/after comparison; skips and placeholder assertions also cover Rust, Java, Kotlin, C#, Ruby, PHP and
|
|
90
|
+
Swift; CI and config files are checked whatever the language. Each rule with examples and limits: [docs/rules.md](docs/rules.md).
|
|
379
91
|
|
|
380
|
-
|
|
92
|
+
## Configure
|
|
381
93
|
|
|
382
|
-
|
|
94
|
+
Rules, severities and paths live in `.agentroastrc.json` at the repository root (`npx agent-roast init` writes a starting file):
|
|
383
95
|
|
|
384
96
|
```json
|
|
385
97
|
{
|
|
386
|
-
"
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
"trivial-assert": "error",
|
|
390
|
-
"swallowed-error": "warn",
|
|
391
|
-
"type-escape": "off"
|
|
392
|
-
},
|
|
393
|
-
"ignorePaths": ["**/generated/**", "**/vendor/**"],
|
|
394
|
-
"panicLoopWindowMinutes": 10,
|
|
395
|
-
"scoreThreshold": 70
|
|
98
|
+
"failOn": ["assertion-weakened", "guard-weakened", "test-skip", "test-focus", "trivial-assert"],
|
|
99
|
+
"rules": { "type-escape": "off" },
|
|
100
|
+
"ignorePaths": ["**/generated/**"]
|
|
396
101
|
}
|
|
397
102
|
```
|
|
398
103
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
that array. History and badge audits honor disabled rules and the panic-loop window.
|
|
403
|
-
History exit status is controlled by `scoreThreshold` or `--fail-under`.
|
|
404
|
-
Invalid JSON, unknown rule names and invalid settings stop the audit with exit code 2.
|
|
405
|
-
Configurations generated by older versions may contain unsupported rules such as
|
|
406
|
-
`hallucinated-package`, `empty-catch` or `mock-leak`; remove these entries and use
|
|
407
|
-
`swallowed-error` for empty catches.
|
|
104
|
+
A change cannot edit this file to relax its own review, because it is read from `HEAD` or the base branch. To accept one
|
|
105
|
+
intentional finding, add an expiring exception instead of turning a rule off, and adopt an existing project without blocking on
|
|
106
|
+
old debt with a baseline: [docs/reference.md](docs/reference.md#configuration).
|
|
408
107
|
|
|
409
|
-
|
|
410
|
-
Git refs or an incomplete scan. Since 0.8.0, legacy/history CLI audits also return 2
|
|
411
|
-
when lines were skipped or supported source was marked binary by Git attributes,
|
|
412
|
-
instead of treating missing coverage as a clean result. `--format json` also works for history audits. A history
|
|
413
|
-
sample below 200 added lines has no score, but its findings are still shown.
|
|
108
|
+
## Does it work?
|
|
414
109
|
|
|
415
|
-
|
|
110
|
+
Measured, with the denominators and the caveats in [docs/benchmarks.md](docs/benchmarks.md). **Weakened checks are rare, mostly
|
|
111
|
+
deliberate, and not specific to AI. agent-roast is a tripwire that stays silent until a check gets weaker.**
|
|
416
112
|
|
|
417
|
-
|
|
113
|
+
- **64 of 64** hand-labeled cheating edits are caught and **44 of 44** harmless look-alikes (reformats, moves, raised
|
|
114
|
+
thresholds, environment-gated skips) stay silent. The cases were written with the detector, so they guard against regressions;
|
|
115
|
+
they do not prove accuracy. Three gaps are documented and still open.
|
|
116
|
+
- **12,645 commits** that touch tests or CI/config in 166 public repositories: blocking rules fired on 0.3% of AI-signed commits
|
|
117
|
+
(Claude, Copilot, Cursor, Codex, Devin, aider) and on 0.3% of the others. Inside the same repositories the odds for an
|
|
118
|
+
AI-signed commit were 0.81 times those of another commit (95% CI 0.40–1.67). There is no sign that AI-signed commits weaken
|
|
119
|
+
checks more often.
|
|
120
|
+
- **About one finding in five was a false alarm**, and most of the rest were deliberate, explained changes a reviewer should
|
|
121
|
+
see rather than mistakes. The labels come from one reader (an AI assistant), so treat them as a first estimate.
|
|
122
|
+
- `replay` ran on 179 public repositories that use coding agents: no commit would have been blocked in 149 of them.
|
|
418
123
|
|
|
419
|
-
|
|
420
|
-
name: Agent Roast
|
|
421
|
-
on:
|
|
422
|
-
pull_request:
|
|
423
|
-
branches: [main]
|
|
124
|
+
## How it compares
|
|
424
125
|
|
|
425
|
-
|
|
426
|
-
|
|
126
|
+
TDD Guard and Probity enforce a *process* (tests first) through agent hooks; aislop scans for broad "AI slop" across 11
|
|
127
|
+
languages; hallucinot, fakegreen and SlopGate look at diffs for the same shortcuts; linters and coverage gates see the current
|
|
128
|
+
code but not that a check got weaker. agent-roast narrows to one question, reads the full before and after, and says how it was
|
|
129
|
+
measured. Most of these tools are complementary. [docs/comparison.md](docs/comparison.md) has the details.
|
|
427
130
|
|
|
428
|
-
|
|
429
|
-
audit:
|
|
430
|
-
runs-on: ubuntu-latest
|
|
431
|
-
steps:
|
|
432
|
-
- uses: actions/checkout@v4
|
|
433
|
-
with:
|
|
434
|
-
fetch-depth: 0 # Required for complete merge-base history
|
|
435
|
-
|
|
436
|
-
- name: Run agent-roast audit
|
|
437
|
-
uses: Nari-Ab/agent-roast@v1
|
|
438
|
-
with:
|
|
439
|
-
format: github
|
|
440
|
-
```
|
|
131
|
+
## Safe to run on code you do not trust
|
|
441
132
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
> **Security Note:** `agent-roast` runs in an isolated runner directory (`$RUNNER_TEMP`) with `--ignore-scripts` to neutralize malicious `.npmrc` files in untrusted pull requests. `pull_request_target` is not supported.
|
|
449
|
-
|
|
450
|
-
---
|
|
451
|
-
|
|
452
|
-
### What it checks
|
|
453
|
-
|
|
454
|
-
`check` scans current source. `review` scans both full versions of changed source;
|
|
455
|
-
the 0.7.0 `--staged` command does the same for HEAD and index. Legacy
|
|
456
|
-
`--base` and history audits inspect added lines. Comment/literal masking reduces
|
|
457
|
-
noise; these are heuristic checks and can have false positives and missed findings.
|
|
458
|
-
|
|
459
|
-
- **Focused tests (`test-focus`):** Exclusive `test.only`, `it.only`, `describe.only`, `suite.only`, Playwright `test.describe.only` and common modifier chains in JS/TS-family sources. `fit` / `fdescribe` aliases are recognized in conventional test paths. Supports contiguous multiline chains and ignores literal/comment examples. Default blocking rule; an existing explicit `failOn` list must include it to block. This does not resolve imports or custom aliases; configure your runner's native forbid-only option too when available ([Playwright](https://playwright.dev/docs/api/class-testconfig#test-config-forbid-only), [Jest API](https://jestjs.io/docs/api#testonlyname-fn-timeout)).
|
|
460
|
-
- **Skipped tests (`test-skip`):** `it.skip`, `describe.skip`, `xit`, `pytest.skip`, `test.todo` added to silence failing tests. (Default blocking gate).
|
|
461
|
-
- **Trivial assertions (`trivial-assert`):** Tautological or dummy assertions added to fabricate test green checks (`expect(true).toBe(true)`, `expect(1).toBe(1)`, `assert True`, `self.assertTrue(True)`).
|
|
462
|
-
- **Stub abandonment (`stub-abandonment`):** Dummy stubs left unimplemented in committed code (`throw new Error("Not implemented")`, `raise NotImplementedError`, Rust `todo!()` / `unimplemented!()`).
|
|
463
|
-
- **Type escapes (`type-escape`):** `as any`, `as unknown as`, `as never`, `// @ts-ignore`, `// @ts-nocheck`, `// @ts-expect-error`, `# type: ignore`, `# noqa`, `eslint-disable`.
|
|
464
|
-
- **Swallowed errors (`swallowed-error`):** Empty `catch {}` or `except: pass` blocks hiding runtime failures.
|
|
465
|
-
- **Panic fix loops (`panic-loop`):** Heuristic chain analysis of rapid-fire fix/revert commits by the same author within 15 minutes touching overlapping files. *(Observational metric for repository audits; not a PR blocker)*.
|
|
466
|
-
|
|
467
|
-
---
|
|
468
|
-
|
|
469
|
-
### Scoring Methodology
|
|
470
|
-
|
|
471
|
-
- **Repository Audit Score:** Uses size-invariant defect density:
|
|
472
|
-
$$\text{Score} = 100 \cdot \exp\left(-\left(\frac{\text{codeDensity}}{R_0} + \frac{\text{panicDensity}}{R_1}\right)\right)$$
|
|
473
|
-
where defect points are normalized by effective sample volume ($k\text{Loc} + 0.5$). Repositories with fewer than 200 lines added return `null` (insufficient data) rather than an arbitrary 100.
|
|
474
|
-
- **Pull Request Mode:** Avoids unstable micro-sample scores. Reports verified infractions, pass/fail status, and reference defect density per 100 LOC. Author identities are hidden by default to prevent team blame games.
|
|
475
|
-
|
|
476
|
-
---
|
|
477
|
-
|
|
478
|
-
### Requirements & Privacy
|
|
479
|
-
|
|
480
|
-
- **Requirements:** Git 2.30+ (for `--end-of-options` argument isolation), Node.js 18+.
|
|
481
|
-
- **100% Local:** Instant streaming parser runs directly on your machine.
|
|
482
|
-
- **Zero Telemetry:** No cloud dependencies, no LLM API calls, no network traffic. Verified by static bundle inspection.
|
|
483
|
-
- **Git isolation:** Forces stable diff paths, disables fsmonitor/external diff execution, and reads review content by object identity.
|
|
484
|
-
|
|
485
|
-
---
|
|
486
|
-
|
|
487
|
-
### Scope and limitations
|
|
488
|
-
|
|
489
|
-
Use this as a focused review aid for skipped tests, compiler suppressions and obvious
|
|
490
|
-
stubs, alongside a compiler, test runner and language-aware linter. Findings identify
|
|
491
|
-
patterns, not proof of AI authorship or developer intent. Deliberate skips and abstract
|
|
492
|
-
methods can be legitimate. Since 0.8.0, the analyzer improves language-specific checks, multiline skips/assertions,
|
|
493
|
-
Rust raw strings/lifetimes and Java/Kotlin text blocks. Contiguous comments, Python
|
|
494
|
-
docstrings and multiline empty handlers are recognized. Template interpolation, complex language syntax and context outside
|
|
495
|
-
the added diff in legacy audit modes are not fully parsed. Custom test API aliases,
|
|
496
|
-
computed property calls and type/import resolution are not handled. Panic loops are an observational history heuristic.
|
|
497
|
-
Test registration options such as Node's `test('name', { skip: true }, fn)` and
|
|
498
|
-
`{ only: true }` are currently not inspected; use explicit `.skip`/`.only` APIs and
|
|
499
|
-
the runner's CI safeguards. A skip that depends on the environment (`test.skip(isMobile, 'reason')`,
|
|
500
|
-
`pytest.skip` under an `if`, or in a fixture) is not reported, because such skips are overwhelmingly
|
|
501
|
-
platform or service gates; one that hides a failing test behind a condition is therefore missed unless
|
|
502
|
-
the condition mentions `CI`. Regexp masking is contextual, not a full JavaScript
|
|
503
|
-
parser. Formatting across contiguous lines is supported for casts, empty catches
|
|
504
|
-
and placeholder errors, but legacy added-line audits cannot recover missing hunks.
|
|
505
|
-
The discipline score summarizes added history; it does not certify current code quality.
|
|
506
|
-
`check` reports actionable findings without assigning a personality or quality score.
|
|
507
|
-
History attribution is based on explicit text/identity heuristics; it does not prove
|
|
508
|
-
AI authorship. Since 0.8.0, the analyzer avoids classifying ordinary people by name fragments
|
|
509
|
-
or treating every automation bot as an AI author. Use `--all` for all commits.
|
|
510
|
-
|
|
511
|
-
The website demos are synthetic illustrations, labeled as such. Older GIF/video
|
|
512
|
-
assets are legacy demonstrations, not performance measurements or adoption evidence.
|
|
513
|
-
See [related tools and design choices](docs/product/related-tools.md) for approaches
|
|
514
|
-
reviewed in Semgrep, Ruff, Gitleaks, reviewdog and eslint-plugin-jest. No third-party
|
|
515
|
-
implementation was copied.
|
|
516
|
-
|
|
517
|
-
### Development
|
|
518
|
-
|
|
519
|
-
Develop/build/test with Node.js 20.19+ (or 22.12+). The distributed CLI still supports
|
|
520
|
-
Node.js 18+. Patched Vitest 4 and build tooling are development dependencies only;
|
|
521
|
-
CI runs the current test runner on Node 22 while CLI subprocesses and actual Git hooks
|
|
522
|
-
are exercised on Node 18/20/22 using `AGENT_ROAST_TEST_NODE`.
|
|
523
|
-
|
|
524
|
-
```bash
|
|
525
|
-
npm ci
|
|
526
|
-
npm run typecheck
|
|
527
|
-
npm test
|
|
528
|
-
|
|
529
|
-
# Optional end-to-end CLI checks in disposable Git repositories
|
|
530
|
-
node scripts/verify-user-workflows.mjs dist/cli.js
|
|
531
|
-
```
|
|
133
|
+
- **Offline.** No network request, no model call, no telemetry; a test inspects the bundle for network modules.
|
|
134
|
+
- **Reads, never runs.** It reads Git objects (or regular files inside the repository for `--worktree`) and never executes
|
|
135
|
+
project code, hooks, diff programs or package scripts. Reports escape control characters.
|
|
136
|
+
- **Policy from the trusted side.** The rules come from `HEAD` or the base branch, not from the change under review.
|
|
137
|
+
- **Honest about gaps.** An incomplete scan exits 2, never "clean". [SECURITY.md](SECURITY.md) says how to report a problem.
|
|
532
138
|
|
|
533
|
-
|
|
534
|
-
against current code. Regression tests use disposable Git repositories. The programmatic
|
|
535
|
-
`auditPullRequest({ staged: true })` API retains the legacy added-lines contract;
|
|
536
|
-
use `reviewStagedChanges()` for the new context comparison and `ReviewResult`.
|
|
537
|
-
The composite Action installs exactly 0.11.0. For local development, use
|
|
538
|
-
`node dist/cli.js --staged` after building.
|
|
139
|
+
It is a review aid. It cannot prove who wrote a change or that code is correct, and it reads text, not program meaning.
|
|
539
140
|
|
|
540
|
-
|
|
141
|
+
## Documentation
|
|
541
142
|
|
|
542
|
-
|
|
143
|
+
[Rules](docs/rules.md) · [Integrations](docs/integrations.md) · [Reference](docs/reference.md) (commands, exit codes,
|
|
144
|
+
configuration, exceptions, baselines, limits) · [Comparison](docs/comparison.md) · [Measurements](docs/benchmarks.md) ·
|
|
145
|
+
[Try it on your repository](docs/pilot.md) · [Contributing](CONTRIBUTING.md) · [Changelog](CHANGELOG.md)
|
|
543
146
|
|
|
544
|
-
|
|
147
|
+
Found a finding that was wrong, or a shortcut it missed? That is the most useful report there is:
|
|
148
|
+
[open an issue](https://github.com/Nari-Ab/agent-roast/issues/new/choose).
|
|
545
149
|
|
|
546
|
-
|
|
150
|
+
## License
|
|
547
151
|
|
|
152
|
+
[MIT](LICENSE). Created by Nariman Abdukarimov.
|