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
70 lines
3.9 KiB
Markdown
70 lines
3.9 KiB
Markdown
---
|
|
name: linter-playbook
|
|
description: 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.retry`
|
|
can be undefined when called from `scheduleJob` (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`, not `high`.
|
|
- **One finding per root cause**, not one per diagnostic. Twelve `no-unused-vars`
|
|
in one file is one finding at most — and usually it's a nitpick worth skipping.
|
|
- **Formatter-only output is not a finding.** `gofmt -l` listing a file is a
|
|
style issue; skip it unless the repo's CI enforces it and the PR would break
|
|
the build — then it's `low` and 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.
|