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