AGENTS.md · diff

git:20261006.5da8b53 to git:20261006.0c6a8b0

38 added, 147 removed. Audit A to A.

# AGENTS.md
- `claude-code-gauntlet` is a Claude Code marketplace plugin — no server, database, or Docker.
- "Running it" means running the test suites and the stdlib-only Python pipeline scripts.
+ `claude-code-gauntlet` is a Claude Code marketplace plugin: a multi-agent code review pipeline.
+ There is no server, database, or Docker. Running it means running the suites below.
- Directory-scoped rules live in nested `AGENTS.md` files: `workflows/`, `scripts/`, `agents/`.
- Read the one for the directory you are editing.
+ `workflows/` and `agents/` each carry a short `AGENTS.md` of their own. `CONTRIBUTING.md` holds the
+ CI coverage gates and everything else a human contributor needs.
## Design
- **Build the mechanism, not the instruction.** Whatever code, a schema, a data structure, or a
- removed capability can enforce, it must. Prose in an instruction, prompt, or agent file is the
- fallback for what cannot be made structural.
-
- **"Add more text" is a design smell.** If that is the fix under consideration, the shape is
- wrong — change the shape.
-
- **Extending should cost one edit.** A new dimension, field, or agent belongs in one place. If it
- takes coordinated edits across N files, fix the shape rather than documenting the ritual.
-
- ## Tooling boundary (no npm in the tree)
-
- - No `package.json`, lockfile, or `node_modules` is tracked, except the vendored scorer's own
- `bench/vendor/code-review-benchmark/uv.lock`.
- - The shipped pipeline runtime (`workflows/src`, `workflows/pipeline.js`) has zero
- dependencies — language globals plus the host-injected `agent`/`parallel`/
- `pipeline`/`args` only. No import survives into the bundle: `build.js` strips
- relative sibling imports and *fails* on any other specifier, which would ship
- as an undefined reference. See `workflows/AGENTS.md` for the sandbox surface.
- - Pinned static binaries in CI are permitted (e.g. Biome for the `js-lint` job).
- Bumps are manual and deliberate; do not add a package manifest to get Dependabot
- coverage for those pins.
- - This does not forbid npm on the CI runner (`validate.yml` already installs the
- Claude Code CLI globally).
-
- ## Python
-
- - **stdlib only, Python 3.10.** Nothing under `scripts/` imports a non-stdlib module;
- `[dependency-groups]` CI tooling is exempt.
- - **Language-agnostic.** Never assume the reviewed codebase's language: `--exclude-dir` for
- non-source directories, never `--include=*.py`. `scripts/gauntlet/fix_tasks.py` documents the one
- exception, its build-system table.
- - **Shape of new and touched code.** A JSON wire shape is a `TypedDict`; an internal record is a
- frozen slotted dataclass; a closed vocabulary is a `Literal`. A function unless state or
- polymorphism earns a class. One implementation per mechanism.
- - **New and rewritten tests are pytest functions**: fixtures, `parametrize` tables, builders that
- produce inputs only. Prove a behaviour once, at the boundary callers use.
- - **Comments say why:** a constraint, a measurement and how it was taken, or a twin. No history
- or issue numbers in code.
-
- ## Tests
-
- ```bash
- python -m pytest tests/ -q # pipeline + boundary parity
- python -m pytest bench/tests/ -q # benchmark harness self-tests
- node --test workflows/test/*.test.js # needs Node 24; the bare directory is not a valid target
- ```
-
- Coverage gates (CI; JS on Node 24.18.0, Python on 3.12). Each command is
- self-contained and byte-identical to the matching `run:` body in
- `.github/workflows/ci.yml`. Locally, coverage data files must stay out of the
- repo tree (an in-tree data file trips the bench plugin-mutation guard):
-
- ```bash
- COVERAGE_FILE="$(mktemp -d)/.coverage" python -m pytest tests/ -q \
- --cov=scripts --cov=.github --cov-fail-under=94.6
-
- COVERAGE_FILE="$(mktemp -d)/.coverage" python -m pytest bench/tests/ -q \
- --cov=bench --cov-fail-under=88.6
-
- LCOV="$(mktemp -d)/js-coverage.lcov" && node --test --experimental-test-coverage \
- --test-coverage-include='workflows/src/*.js' \
- --test-coverage-include='workflows/build.js' \
- --test-coverage-lines=98.6 \
- --test-coverage-branches=91.8 \
- --test-coverage-functions=98.5 \
- --test-reporter=spec --test-reporter-destination=stdout \
- --test-reporter=lcov --test-reporter-destination="$LCOV" \
- workflows/test/*.test.js \
- && node workflows/test/tools/check_coverage_presence.mjs "$LCOV"
- ```
-
- Floors: Python 94.6 / 88.6, JS 98.6 / 91.8 / 98.5, each pinned from a PR's CI measurement;
- the ratchet history is in git. Policy: a floor sits no more than 1.0 pp below the CI
- measurement for that gate; lower a floor only in the PR that causes the drop, with
- the reason in the body; raise when measured headroom exceeds 1.0 pp. A sudden
- multi-point JS drop usually means a deleted fixture group or an unloaded
- module (presence check); a sudden multi-point Python drop means broken
- subprocess capture — fix capture, do not lower.
- `workflows/test/tools/record_parity.py` is test infrastructure, outside Python
- scopes. JS measures `workflows/build.js` + loaded `workflows/src/*.js` via the
- include allowlist; `pipeline_entry.js` is exempt from presence only.
+ - **Build the mechanism, not the instruction.** Whatever code, a schema, a data structure, or a
+ removed capability can enforce, it must. Prose in a prompt or agent file is the fallback.
+ - **"Add more text" is a design smell.** If that is the fix under consideration, change the shape.
+ - **Extending should cost one edit.** If a new dimension, field, or agent takes coordinated edits
+ across several files, fix the shape instead of documenting the ritual.
- After editing `workflows/src/*.js`, rebuild and confirm the bundle is unchanged:
+ ## Commands
```bash
- node workflows/build.js && git diff --exit-code workflows/pipeline.js
- ```
-
- **A regression test must fail against the bug it names.** Verify that by mutating the
- implementation and watching it go red — not by reading the test. Mutate the whole mechanism; a
- partial mutation falls through to a neighbouring fallback and passes misleadingly.
-
- ## Lint
-
- `pre-commit run --all-files` is the gate.
-
- - `markdownlint-fix` rewrites files in place, then reports failure if it changed anything. A
- "Failed / files were modified by this hook" result means the fixes are already applied — re-stage
- and re-run.
- - `CHANGELOG.md` is excluded from that hook deliberately; it is regenerated on every release. Do
- not re-add it.
- - pre-commit sees **git-tracked files only**. A new file is invisible to every hook until
- `git add`, so it can pass locally and fail in CI. Stage new files before trusting a green run.
- - **Never put the literal skip-ci token in a commit message, even when writing about it.** GitHub
- Actions scans the whole message, so the workflows silently never run — and a squash merge carries
- it onto `main`. Call it "the skip-ci token".
-
- ## Writing JSON for pipeline scripts
-
- Use `python3 -c "import json; ..."`. Not the Write tool (it requires a prior Read), and not a
- heredoc (zsh corrupts `!`).
-
- ## Output directory
-
- `{output_dir}` defaults to `.code-gauntlet/` (repo-local, gitignored). Override with
- `$CODE_GAUNTLET_OUTPUT_DIR`.
-
- ## Layout
-
- ```text
- claude-code-gauntlet/ <- plugin root
- ├── agents/ <- subagent contracts
- ├── scripts/ <- retained Python (verify_findings.py, post_review.py, ...)
- ├── workflows/ <- JS pipeline: src/*.js, build.js, pipeline.js (generated), test/
- ├── bench/ <- benchmark harness (stdlib-exempt)
- ├── tests/ <- pytest suite
- └── skills/code-gauntlet/ <- skill base directory
+ python -m pytest tests/ -q # pipeline scripts
+ python -m pytest bench/tests/ -q # benchmark harness
+ node --test workflows/test/*.test.js # Node 24; a bare directory is not a valid target
+ node workflows/build.js # after editing workflows/src; commit the bundle
+ python3 workflows/test/tools/biome_check.py
+ pre-commit run --all-files # the lint gate
```
- `{plugin_root}` is two levels above the skill base directory. Never locate it by searching the
- filesystem — a `find` hit picks arbitrarily among every cached plugin version.
-
- Session scratch — design memos, implementation plans, handoff notes — never lands in the tree; it
- belongs in the PR description or issue thread. Tracked docs are allowlisted by
- `tests/test_docs_registry.py`; a durable doc is added there in the same commit, with a reason.
-
- ## Contribution surface
+ pre-commit sees tracked files only, so `git add` a new file before trusting a green run.
+ `markdownlint-fix` fixes in place and then reports failure: re-stage and re-run.
- `tests/test_contribution_surface.py` covers the issue-form schema, `.github/labels.json`, and the
- CI commands quoted in `CONTRIBUTING.md`. Two things it cannot reach, both needing write access
- after a merge:
+ ## Constraints
- - GitHub serves issue forms from the default branch only, and refuses to render an invalid form
- without erroring. A plan that tests form submission before the merge is mis-ordered.
- - Labels must exist before a form can apply one; GitHub drops unknown labels silently. Sync with
- `python3 .github/labels_diff.py --commands --repo <owner>/<repo>`, confirm with `--live -`. See
- `docs/maintainer-issues.md`.
+ - **No `package.json`, lockfile, or `node_modules`.** `scripts/` is stdlib-only Python 3.10, and
+ the shipped bundle has zero dependencies.
+ - **Language-agnostic.** Never assume the reviewed codebase's language: exclude non-source
+ directories, never include by extension.
+ - **Python shape.** A JSON wire shape is a `TypedDict`, an internal record is a frozen slotted
+ dataclass, a closed vocabulary is a `Literal`. `scripts/*.py` are thin entry files; logic lives
+ in `scripts/gauntlet/`.
+ - **New and rewritten tests are pytest functions** that prove a behaviour once, at the boundary
+ callers use. A regression test must fail against the bug it names: mutate the whole mechanism
+ and watch it go red, because a partial mutation falls through to a neighbouring fallback and
+ passes.
+ - **Comments say why.** No history, dates, or issue numbers in code; those belong in the PR.
+ - **Never write the literal skip-ci token in a commit message**, even when writing about it.
+ GitHub Actions scans the whole message and silently skips every workflow.
+ - **Session scratch stays out of the tree.** Plans, memos, and handoff notes go in the PR or the
+ issue. Coverage data files stay out too: set `COVERAGE_FILE` to a temp path.
- ## Session output style
+ ## Adding to this file
- Canonical rules and the regeneration mechanism are documented in `scripts/gauntlet/style.py`.
+ A line belongs here only if removing it would cause a mistake that no test, lint rule, or comment
+ at the code would catch. A test pins the size of these files, so an addition displaces something.