Self audit
Pre-review self-audit of a PR branch. Use after addressing review feedback, before tagging a human reviewer, or after a significant self-directed refactor. Runs a structured pass looking for the stuff a careful human reviewer will catch but a compiler won't — dead struct fields, rotting comments, duplicate types, test-only exports, scope drift — then dispatches an adversarial agent on the diff for deeper design flaws.From its SKILL.md
npx -y skills add jamestexas/agents --skill self-auditAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
8.3 KB, ~1.9k tokens by cl100k_base, as published. Nobody here has run it
self-audit — Pre-review quality pass
Check your own PR like a grumpy reviewer would, before one sees it.
The compiler and golangci-lint catch the easy stuff. This skill catches the
grown-up stuff that slips past both of them.
When to use
- After addressing review feedback, before asking a reviewer to look again.
- After a significant self-directed refactor, before creating the PR.
- Any time you feel "done" but want a second opinion that isn't a human.
Arguments
$ARGUMENTS — optional base branch name. Defaults to main.
Step 1 — Scope sanity
git diff --stat <base>...HEAD
Read the list. For every file, ask: did I intend to change this? If anything looks surprising — generated files, unrelated packages, formatter drift — stop and investigate before continuing. The rest of this audit is wasted effort if scope is wrong.
Step 2 — Dead struct fields
A struct field that is written but never read is dead code. golangci-lint unused does not catch this — as long as the field appears on either side
of an =, it counts as used.
For every struct field you added, confirm there is a read site outside the place it is written. If the only references are:
- the extractor/constructor that populates it, and
- a test that asserts "this field is non-empty",
...that is circular — the test verifies the field exists, and nothing else cares. Delete the field, its extractor, and that test.
# Rough heuristic — greps for every .FieldName use:
rg '\.FieldName\b' --stats
A real non-test, non-self reader is required.
Step 3 — Comments that rot
Comments die when they reference context that fades. Scan for patterns that will be meaningless in six months:
rg -n '@\w+|comment #|round \d+|per the review|commit [0-9a-f]{7,}|\.go:\d+|renamed from|formerly|was previously|intentionally|rather than' <changed-files>
For each hit ask: will this comment make sense to someone who never saw the PR thread? If no, rewrite. State what the code does, not how it got this way.
Worst offenders, in order of how badly they age:
- Reviewer names in comments ("added per X's suggestion"). People rotate; comments survive.
- Cross-file line numbers ("covers foo.go:123"). Rot the moment anything moves.
- Historical framings ("renamed from X", "formerly Y", "not Z anymore"). Description by negation tells a future reader about a past they don't have.
- "Intentionally" / "deliberately" as defensive filler. If the reason is worth saying, say the reason without the hedge.
- "X rather than Y" where Y never existed in the repo. Compare to alternatives that were actually considered, not to everything you didn't do.
Step 4 — Duplicate or parallel types
# Types with identical field sets:
rg -U 'type \w+ struct \{[^}]+\}' <pkg> | sort | uniq -c | awk '$1 > 1'
Weaker heuristic that catches more: any time you have an exported and an unexported type with the same shape in the same package, ask "what does exporting buy me that an accessor method wouldn't?" If the answer is nothing, merge them.
Step 5 — Test-only exports
Exported API that only has test callers is either dead or wishful.
For each exported function/method/type added:
# Callers outside test files AND outside the defining file:
rg -l '\bFunctionName\b' | rg -v '_test\.go$' | rg -v '<defining-file>'
Zero results → it's exported for future use that may never come. Make it unexported unless the future use is imminent.
Step 6 — Project convention check
Most repos state conventions outside the linter config — CLAUDE.md,
AGENTS.md, CONTRIBUTING.md, .cursorrules, team memory files. Generic
linters catch what's universal; these files catch what's local. Read them
and check your diff against them.
# Skim project conventions before checking:
head -200 CLAUDE.md AGENTS.md CONTRIBUTING.md .cursorrules 2>/dev/null
Classes of convention that generic linters miss:
- Test idioms tied to the Go/language version (e.g.
t.Context()overcontext.Background()in Go 1.24+ repos). - Error wrapping conventions (
%wvs%v; when to wrap). - Log framework preferences (project-specific choice of slog / zerolog / internal logger).
- Import-grouping rules beyond what
goimportsenforces. - Private-type + exported-interface return patterns.
- Subtests vs table-driven test preferences.
If your org runs a policy bot in CI that checks project-specific rules, the CLAUDE.md / convention docs are the local source of truth the bot is mirroring. A hit here saves a CI round-trip.
Step 7 — Adversarial agent pass
This is the highest-leverage step and the one you will most want to skip. Don't skip it.
Dispatch an adversarial agent on the diff. A prompt that works:
Here is the diff for branch
<branch>against<base>. Find the fatal disconnect between what the commits claim and what the code actually does. Be mean. Do not give me a sandwich review. If you find nothing, say "clean" — do not manufacture concerns.
Agents worth using for this:
paradigm-assessor— pure adversarial greybeard. Best default.production-readiness-reviewer— framed around ship-blockers.- A general-purpose subagent with the prompt above.
The agent will either:
- Find something you missed → fix it.
- Say clean → push with confidence.
- Raise something wrong → defend your position clearly in writing. That rehearsal is worth more than the finding itself, because you will use it verbatim when the real reviewer asks the same question.
Outputs
This skill produces no artifacts. Findings go straight into commits that fix them, or are explicitly waived (with reasoning) before pushing. Do not write a "self-audit report" file — the commits are the report.
Anti-patterns to avoid
- Running the audit, finding nothing, and declaring victory. If the adversarial agent in step 7 also says clean, that's fine. If you skipped step 7 because steps 1-6 looked good, you have not actually audited.
- Amending previous commits to fold findings in. The stack should still be readable as a sequence of decisions. New commit per finding.
- Hiding the audit in a side branch and squashing away the evidence. The fact that you caught and fixed something is information for the next person (including future you).
Project-specific extensions (optional)
The steps above are deliberately generic so this skill works in any repo.
For invocations tied to a specific work environment — internal style
bots, generated-code verifiers, API-compatibility checkers, policy gates
— keep them out of this SKILL.md and in a private extension file.
Convention
If ~/.claude/skills/self-audit/extensions.md exists, read and follow it
at the start of Step 6 (project convention check). The extensions
file layers environment-specific checks on top of the generic convention
review.
Why this path:
- Lives in the user's personal Claude config, not in any git repo.
- Never at risk of being committed to a public skills repo.
- Survives re-clones and migrations of the agents repo.
- Mirrors the
skills/<name>/structure so multiple skills can each have their own extensions file without collision.
If the extensions file is absent, step 6 is just the generic convention review — the skill degrades gracefully.
Example extension hooks worth adding
- A local runner for a CI-side policy bot (so you catch issues before push).
- A diff-vs-memory comparison (check your diff against rules stated in
repo-level
CLAUDE.mdor team memory files). - Repo-specific dead-code detectors (e.g. proto field usage scanners, Terraform variable reachability checks).
- Credential/secret sweeps specific to the org's token shapes.
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.
Gives 0 of the 12 instructions most review quality skills give in ~1.9k tokens
Counted across 1,048 of the 1,783 authors here whose files we hold, read 2026-08-07
- Ask questions one at a timein 81 of 1048, across 64 files
- Provide a recommended answer for each questionin 73 of 1048, across 50 files
- Explore the codebase instead of asking answerable questionsin 66 of 1048, across 42 files
- Resolve dependencies between decisions one-by-onein 42 of 1048, across 17 files
- Interview the user relentlessly about the planin 38 of 1048, across 13 files
- Order findings by severityin 31 of 1048
- Resolve each branch of the decision treein 27 of 1048, across 5 files
- Run a grilling sessionin 26 of 1048, across 5 files
- Update CONTEXT.md immediately when a term is resolvedin 26 of 1048, across 11 files
- Propose precise canonical terms for vague languagein 25 of 1048, across 7 files
- Create documentation files lazilyin 24 of 1048, across 5 files
- Assign severity to every findingin 24 of 1048
Said here and by no other author read
- confirm every changed file was intended to change
- verify struct fields are read outside their tests
- delete struct fields with circular test references
- rewrite comments referencing review context
- merge exported types duplicating unexported types
- make test-only exports unexported
Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once. Length counted with cl100k_base; the agent that loads this file may tokenize it differently.