prompting:scan ยท diff
git:20261103.e240905 to git:20261103.02d6c50
2 added, 0 removed. Audit A to A.
---
name: prompting:scan
description: "Report prompt defects a lexical scan finds in documents a model executes. Use to audit a repository's skills, CLAUDE.md, agent definitions, or a product's prompt files, or to gate them in CI or a pre-commit hook, or propose cuts a reading pass finds."
argument-hint: "[audit] [<path>...] [--all] [--quiet] [--score]"
effort: low
allowed-tools:
- Bash(bun ${CLAUDE_SKILL_DIR}/scripts/scan.ts:*)
- Bash(bun ${CLAUDE_SKILL_DIR}/scripts/audit.ts:*)
- Read
- Edit
---
# Scan
Run the scanner over the paths in `$ARGUMENTS`, defaulting to the current directory:
```bash
bun ${CLAUDE_SKILL_DIR}/scripts/scan.ts [<path>...] [--all] [--quiet] [--score]
```
It reports each alert as `file:line:col: level: check: message` on stdout and a per-check count table on stderr, and exits non-zero on any alert. A check is an `error` once the precision eval confirms it and a `warning` until then. `--quiet` drops the table. `--score` replaces it with one row per scanned file, clean files included, giving its alerts, its prose words outside frontmatter and code, and alerts per 1,000 words, plus a total. Run it before and after an edit round over the same paths to compare them.
Without `--all` it reads the paths that hold documents a model executes: `SKILL.md`, `CLAUDE.md`, `AGENTS.md`, `.claude/agents`, `.claude/commands`, `.claude/rules`, a skill's `references/`, and a `prompt/` or `prompts/` directory holding `.md` or `.txt` files. Use `--all` for a prompt that lives somewhere else, such as one extracted from a string in a product repo. It reads `.md` and `.txt` under either setting, so `--all` widens which documents count without pulling in source files. Either way it also scans the files a scanned `CLAUDE.md` or `AGENTS.md` imports with `@path`, up to four hops deep, and lists on stderr each import outside the scanned paths or missing. It skips `node_modules`, `.git`, `fixtures`, `__snapshots__`, and `evals`.
## Checks
#### Rewrites
Sharpen the sentence the alert names.
- `Prompting.WeakModality`: the instruction is a suggestion the model can decline, so whether it fires is left to the run.
- `Prompting.VagueCriterion`: the done-state is one the model cannot check. The model decides for itself when to stop.
#### Deletions
Delete the span the alert names.
- `Prompting.NoOp`: the instruction restates a default the model already follows, spending context and changing nothing.
- `Prompting.StaleMeasurement`: the document reports a tally a past run counted. The run prints the number again, so the document caches its own output and drifts as the work behind it moves.
- `Prompting.TicketRef`: a bare issue number points at a tracker the model cannot read, and the prose around it goes stale when the issue closes.
## Acting on an Alert
An alert is a candidate. The scanner matches words rather than intent, so read the sentence and confirm it is an instruction before acting on it. A descriptive sentence that happens to contain the phrase is a false hit.
Load the `prompting` skill for the fix. Each check maps to a section: Sentence Form for weak modality, Completion Criteria for a vague criterion, and Pruning for the rest, under No-ops for a no-op, Cache for a stale measurement, and Relevance for a ticket reference, project status, or a maintainer aside.
## Audit
With `audit` in `$ARGUMENTS`, run one round of the prune loop instead of the lexical scan. A judge reads each document and proposes cuts of lines a current model no longer needs, Ben accepts or rejects each in review, and the verdicts become telemetry and rule-precision labels. The round is done when every ledger proposal has a verdict event and the per-class table is printed.
List the units, defaulting to the current directory:
```bash
bun ${CLAUDE_SKILL_DIR}/scripts/audit.ts units [<path>...]
```
+ Where the `observability:session` index is available, rank the units by its `memory-loads` query, which multiplies each rule or nested `CLAUDE.md`'s load count by its injected size, and judge the costliest first.
+
Judge one unit inline. Judge more than one through a Workflow with one judge per batch of units, kept within the session's workflow size, since this skill's instruction to run one is the opt-in. [references/judge.md](references/judge.md) holds the judge prompt and output schema, and [references/classes.md](references/classes.md) the classes it tags. Write the returned proposals to `tmp/prompting-audit/<run>/proposals.jsonl` in the audited repo, one per line with a unique `id`, where `<run>` names the round.
Then drive the ledger through `audit.ts`:
- `apply <ledger>` runs each `enforced` proposal's planted-violation check, applies every proposal to the working tree, and lists any check that did not hold and any quote it could not locate. Either drops the proposal out of the round.
- Ben reviews the diff with `plannotator review`, which opens a browser and runs outside the sandbox. An annotation on a hunk rejects that proposal.
- `revert <ledger> <id...> --reason <annotation>` restores each rejected span.
- Commit the round with nothing else in the commit, and open it as its own pull request, so rule-precision can read its squash commit as labels.
- `verdict <ledger> --commit <sha>` reconciles each proposal against the commit, so a hand edit Ben makes before committing counts too. It prints the per-class table and the `labels.json` entry. Once the round's pull request merges, add its squash SHA under `agent` in `plugins/prompting/evals/rule-precision/labels.json` in `bendrucker/claude`.
## Gotchas
- Fenced blocks, inline code, quoted phrases, and blockquotes are exempt. A document that teaches a rule by showing the wording it rejects reports nothing.
- A linked issue keeps its context, so `TicketRef` reads only a bare number. A number that ends a range or follows a comparator is a threshold, so `StaleMeasurement` passes `20-29 of 40` and `>= 36 of 40`.
- Frontmatter is data, so a `description` field never raises an alert.
- An explicit path that is not agent-facing scans nothing and exits 0, which reads the same as a clean pass. Pass `--all` when naming a file outside the recognized paths.
- The checks cover what a lexical match can find with precision. Sprawl, duplication, and pointer wording need a reading pass, which `audit` runs.
- The ledger copies source text, so it stays untracked under `tmp/`.
- Retire audit mode when two consecutive rounds fall below 50% overall acceptance in the `prune-acceptance` session query. [references/classes.md](references/classes.md) holds the per-class retirement rule.