Refine Rules
Relies on BDK foundation (STARTUP_INSTRUCTIONS.md). Assumes environment discovery has already run (language, test runner, build tool are known).
Rewrite .claude/rules/ (or the directory at $ARGUMENTS) into rule files that describe
only the current, verified state of the code — no history, no changelog, no guesses —
in one uniform voice across every file.
The core problem this solves: rule files in a long-lived project were written by whichever agent or engineer was in the seat on a given day, across many sessions, often under time pressure. Wording accumulates — some of it stays true, some goes stale the moment the code it described changed, and some was never verified in the first place. Treat every existing sentence as an unverified claim, not a fact. Prior wording earns no trust just by being there; it earns trust by matching the code you actually read.
Workflow
1. Discover Rule Files
Run the discovery script instead of hand-parsing with Read/Glob:
python3 ${CLAUDE_SKILL_DIR}/scripts/list_rule_files.py $ARGUMENTS
Defaults to .claude/rules when no argument is given. Returns, per file: frontmatter_paths
(the paths: glob list, if any), headings, line_count, char_count.
Then run the mechanical linter for a budget/narrative baseline (same argument handling):
python3 ${CLAUDE_SKILL_DIR}/scripts/lint_rules.py $ARGUMENTS
Its findings (over-budget files, narrative-marker lines, code-mirror and
ticket-only-rationale bullets) tell you where the worst accretion is before you read a
single file. _inbox.md is exempt by design - it stages uncurated candidates.
Health heuristic: a rule file that changes more often than roughly once a quarter is a code-mirror suspect - rules churn with decisions, not with code. Use it to decide where to look hardest, not as a metric to compute.
Skip any file whose frontmatter or header explicitly marks it as auto-generated — treat
those like CHANGELOG.md, never rewrite them.
2. Classify Content: Verdict per Bullet
Read references/rule-admission.md (the prime directive, the admission test - durability
/ decision / visibility / derivability) and references/uniform-rule-format.md (the
"What is a rule vs. what is noise" table with worked examples). For each file, go
bullet-by-bullet (or paragraph-by-paragraph for prose sections) and tag each with one of
six verdicts:
- RULE - a present-tense, falsifiable claim that passes all four admission tests
- NARROW-GLOB - a true rule, but relevant only to a subset of this file's
paths:. Move it to (or create) a file whosepaths:names just that subset - SKILL - procedural how-to behind an intent trigger no glob expresses, with a deterministic backstop that catches a missed invocation. Extract to a project skill; skills fail open, rules fail closed, so never route an ambient constraint here
- SIGNPOST - a true constraint a test/lint already enforces, compressed to one line naming the enforcer
- RELOCATE - true and valuable but pull-based (the trap is visible at the code site): API/protocol description, single-file subtleties, design rationale worth keeping. Belongs in a doc comment at the code site, with at most a one-line pointer left in the rule file
- NOISE - changelog/history/narration/hedged guess/TODO. Drop these outright, no matter how confidently the original file phrased them
When several destinations fit, prefer in this order:
narrow glob > wide glob > skill > doc comment > nothing. Never park content in
docs/: content that fails admission is relocated to a code site or deleted. Deleting
is a successful outcome; a shorter directory that instructs better is the goal.
This is a fast text-judgment pass — do it in main context, no code access needed yet.
3. Verify Every Surviving RULE Claim Against Real Code
This is the step that makes the output trustworthy. Do not skip it because a claim "sounds right" — that's exactly the failure mode this skill exists to catch.
!python3 ${CLAUDE_PLUGIN_ROOT}/scripts/inject.py --chain ${CLAUDE_PLUGIN_ROOT}/fragments/tool-tiers/search.chain.json
3.1 — Split checkable vs. non-checkable claims.
- Checkable (has ground truth in the repo): file/dir locations, exports, config settings, exclusion lists, banned/required API patterns, index names, version pins, "X calls Y" relationships.
- Non-checkable (process/preference statements): "ask before assuming", "prefer composition over inheritance", team workflow conventions. Grep can't confirm or refute a preference — don't force verification on these, just sanity-check they still make sense given what step 1–2 surfaced.
3.2 — Dispatch verification.
For each rule file with paths: frontmatter, scope the search to those globs — that's
the surface the rule claims to govern. For files with many checkable claims or broad
path scope, dispatch to one or more Explore-type subagents rather than verifying
serially in the main context — verification means reading real source across
potentially many files, and doing that inline for every rule file burns context fast.
Small rule sets (a handful of files, a few claims each) can be verified inline instead.
Brief each subagent explicitly to default to skepticism: a claim is CONFIRMED only with positive evidence found in code. Otherwise it's CONTRADICTED (found evidence it's false) or UNVERIFIED (no evidence either way — e.g. the code is too dynamic to grep, or the claim describes intent rather than a checkable artifact).
3.3 — Resolve each verdict.
| Verdict | Action |
|---|---|
| CONFIRMED | Keep, rewrite into uniform format (Step 4) |
| CONTRADICTED | Drop — or correct, only if you have positive evidence of the replacement; never invent one |
| UNVERIFIED | Keep, but flag it in the plan (Step 5) for the user to confirm |
| NOISE (from Step 2) | Drop, regardless of verdict — noise doesn't get verified, it gets removed |
RELOCATE and SIGNPOST candidates from Step 2 go through the same verification (a claim must be true before it earns a doc comment; an "Enforced by" clause must name a test that exists).
4. Rewrite Into Uniform Format
Use references/uniform-rule-format.md as the template for every file: same bullet
shape (**bold falsifiable claim.** why it matters), same tone, paths: frontmatter
preserved verbatim. Converge ALL files onto this one shape — including files that were
already reasonably well-written — so the whole directory reads as one voice instead of
one style per author.
Apply the structure and budgets from references/rule-admission.md:
- Each file opens with
## Critical Invariants— the 3–6 constraints whose violation means data loss, divergence, or silent corruption. - File budget 150 lines / 8 KB, bullet budget ~5 lines. If genuinely all RULE-grade
content still exceeds the budget, plan a split into narrower
paths:scopes rather than accepting the overage. - For each RELOCATE item, draft the doc comment (target file, insertion point, text) alongside the rule-file rewrite — relocation happens in the SAME change set, move first, cut second. Match the target file's existing comment style.
5. Present the Plan, Get Approval
Rewriting hand-maintained docs is destructive to whatever the user or prior agents
wrote — confirm before touching disk. Use a single AskUserQuestion call listing, per
file:
<file>: KEEP unchanged / COMPACTED (N noise lines removed) /
NARROWED (paths: tightened to N globs, or split into <new-file>) /
EXTRACTED-TO-SKILL (N items → <skill>, with its backstop named) /
RELOCATED (N items → doc comments, with target files) /
SIGNPOSTED (N bullets compressed to enforcer pointers) /
CONTRADICTED (N claims removed, with evidence) /
FLAGGED (N unverified claims to confirm) / REFORMATTED (structure only)
RELOCATED, NARROWED, and EXTRACTED-TO-SKILL items all change more than the rule file -
source files, paths: scoping, or a new skill. List every target in the plan so the
user sees the full blast radius before approving.
Options: "Approve" / "Approve with changes" / "Cancel". For CONTRADICTED items, show the evidence (file:line or a short quote from the source) so the user can check your verdict rather than just trust it — the whole point of this skill is not asking anyone to trust unverified claims, including yours.
Only proceed to Step 6 after approval. On "Approve with changes", incorporate the feedback before writing.
6. Write
Order matters: write the RELOCATE doc comments into their target source files first,
then overwrite the rule files — cutting before relocating destroys knowledge if the
run is interrupted. Preserve paths: frontmatter exactly — it's functional metadata
read by the drift-detection hook, not prose. Before writing, check git status on the
target directory; if it's not clean or not tracked, tell the user and suggest
committing first so the rewrite stays trivially reversible.
7. Lint Gate & Report
Re-run the linter over the rewritten directory:
python3 ${CLAUDE_SKILL_DIR}/scripts/lint_rules.py $ARGUMENTS
Zero errors is the exit criterion — a remaining budget error means Step 4 under-planned a split; a remaining narrative marker means a NOISE item slipped through. Fix and re-lint; never ship a rewrite that fails its own contract.
Then summarize: files touched, total noise lines removed, items relocated (target file each), claims contradicted (one-line evidence each), claims flagged unverified for follow-up, final lint status.
Resources
references/rule-admission.md
What belongs in a rule file at all: the prime directive, push vs. pull, the four-part admission test, the six verdicts (RULE/NARROW-GLOB/SKILL/SIGNPOST/RELOCATE/NOISE), the destination priority order, the duplication policy, where each kind of knowledge lives, and the file/bullet budgets. Read before Step 2.
references/uniform-rule-format.md
Template, the rule-vs-noise table, and a full before/after worked example. Skim it before Step 2 for noise calibration; use it directly while rewriting in Step 4.
scripts/lint_rules.py
Mechanical enforcement of the admission contract: budgets, paths: frontmatter,
## Critical Invariants presence, narrative markers, plus admission:code-mirror
(a bullet enumerating 3+ paths from one directory with no pinning test) and
admission:ticket-only-rationale. Run at discovery (Step 1 baseline) and as the exit
gate (Step 7). Also used standalone by /bdk:add-rule.
Quick Reference
Checklist
- Ran discovery script + linter, got file list, budgets, narrative baseline
- Classified every bullet/paragraph as RULE / NARROW-GLOB / SKILL / SIGNPOST / RELOCATE / NOISE, preferring the narrowest destination that fits
- Split checkable vs. non-checkable claims
- Verified checkable claims against real code (subagents for large scope), skeptical default
- Rewrote all files: uniform bullets, Critical Invariants section, budgets, frontmatter preserved
- Drafted doc comments for every RELOCATE item (move first, cut second)
- Reduced every duplicated fact to one home + one-line pointers elsewhere
- Presented plan via single AskUserQuestion - including relocation, narrowing, and skill-extraction targets - got approval
- Checked git status / suggested a commit before writing if uncommitted
- Wrote doc comments, then rule files; linter exit gate passes with zero errors
- Reported noise/relocation/contradiction/flag counts + lint status