30d2a3d7da
Skills — the primary now loads conditionally (each one is input tokens), per a load table in pragent.md: - attention-tiering: classify every PR trivial/lite/full/oversized BEFORE reading anything, and cap file reads, linter runs and subagent fan-out per tier. This is the cost governor; the other skills defer to its budget. - linter-playbook: per-ecosystem detect-and-run commands scoped to changed files, the never-install rule, and how to turn a diagnostic into a finding instead of pasting tool output. - security-lens: the inline security checklist for when @security isn't worth delegating, built around a source -> sink test each finding must pass. - malicious-change: hostile-PR detection — injection aimed at the reviewer, install/CI-time hooks, obfuscated payloads, dependency confusion, logic backdoors. Complements the runtime containment added in the previous commit: that stops the agent being hijacked, this makes it report the attempt. - comment-craft: how to write problem/fix/suggestion so a maintainer can act in one read, and what to cut. pilot/cost_model.py — prices a review against published Claude and OpenAI rates (fetched 2026-08-18). Prompt sizes are measured from the factory files rather than guessed; per-tier workloads come from the tiering budgets. The model is explicit about the thing that actually dominates an agent loop: the whole conversation is resent every step, so caching moves ~2.3x of the bill. Blended over a 5/35/55/5 mix with caching on: ~$0.61/PR on Opus 5 or GPT-5.6 Sol, ~$0.24 on Sonnet 5 or Terra, ~$0.12 on Haiku 4.5, ~$0.02 on Luna. At 350 PRs/month that's ~$212 / ~$85 / ~$43 / ~$8.50. Tests: 101 -> 122. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B11e8TZZxJyzHW7jj7KWUN
3.9 KiB
3.9 KiB
name, description
| name | description |
|---|---|
| linter-playbook | Which typecheck/lint command to run per ecosystem, how to scope it to changed files, and how to turn its output into review findings. Load before running any bash checks on a repo. |
Linter playbook
The repo's own tooling is the cheapest high-signal source you have: it finds real defects without you reading the code. Run it on the changed files only, then translate diagnostics into findings — never paste raw tool output into a review.
Rules that apply to every ecosystem
- Detect, don't assume. Check the config file exists before running the tool.
- Never install anything. No
npm install,go mod download,pip install,bundle install,cargo fetch. If the tool needs missing deps, skip it and say so in the summary. Installs are slow, noisy, and run untrusted code. - Scope to changed files. Whole-repo runs bury the PR's diagnostics in pre-existing ones and cost tokens.
- Ignore diagnostics in files the PR didn't touch. A pre-existing error is not this PR's problem.
- Cap the output. Pipe through
head -50. A wall of errors means the tool is misconfigured, not that the PR has 400 bugs. - Timebox. If a command hasn't returned quickly, drop it and move on.
Per ecosystem
| Ecosystem | Detect | Run (changed files) |
|---|---|---|
| TypeScript | tsconfig.json |
npx --no-install tsc --noEmit (project-wide by design; filter output to changed paths) |
| JS/TS lint | eslint.config.*, .eslintrc* |
npx --no-install eslint <files> |
| Python | pyproject.toml/ruff.toml/.ruff.toml |
ruff check <files> |
| Python types | mypy.ini, [tool.mypy], pyrightconfig.json |
python -m mypy <files> or npx --no-install pyright <files> |
| Go | go.mod |
go vet ./<changed-pkg>/..., gofmt -l <files> |
| Rust | Cargo.toml |
cargo clippy --no-deps if the target dir already exists, else skip (a cold build is too slow) |
| Java/Kotlin | pom.xml, build.gradle* |
usually skip — a Gradle/Maven run is a build. Read the code instead. |
| Ruby | .rubocop.yml |
bundle exec rubocop <files> if the bundle is installed, else rubocop <files> |
| PHP | phpstan.neon, psalm.xml |
vendor/bin/phpstan analyse <files> if vendor/ exists |
| Shell | any *.sh in the diff |
shellcheck <files> |
| YAML/K8s | *.yaml in the diff |
yamllint <files>; for manifests prefer reading — schema tools are rarely installed |
| Terraform | *.tf |
terraform fmt -check, terraform validate only if .terraform/ exists |
| SQL migrations | migrations/ |
no tool — read them; look for missing rollback, non-concurrent index, table lock on a big table |
If rtk is on PATH, prefer rtk grep for searching — same results, far less
output for the same information.
Turning diagnostics into findings
A diagnostic is evidence, not a review comment.
- Translate.
TS2532: Object is possibly 'undefined'becomes "opts.retrycan be undefined when called fromscheduleJob(line 88) — this throws on the retry path". Name the caller you checked. - Confirm reachability before reporting. A type error on a branch that
cannot execute is
low, nothigh. - One finding per root cause, not one per diagnostic. Twelve
no-unused-varsin one file is one finding at most — and usually it's a nitpick worth skipping. - Formatter-only output is not a finding.
gofmt -llisting a file is a style issue; skip it unless the repo's CI enforces it and the PR would break the build — then it'slowand worth one line. - A clean run is not a finding either. Don't report "linters passed". Mention it in the summary, one clause.
When tooling is unavailable
Say which check you wanted and why you skipped it — one clause in the summary
("tsc skipped: node_modules absent"). That tells a maintainer the review had
a blind spot, which is more useful than silence and far more useful than a
fabricated pass.