repo-hygiene-audit-skill/repo-hygiene-audit/SKILL.md
Travis Herbranson 31c67b9573 Drop Obsidian vault (root domain) from default audit scope
wiki-vault/wiki-context are an MCP-owned, managed surface — a second scanner
risks interfering with how the vault MCP maintains its context. Remove the
'root' domain from DEFAULT_DOMAINS so a plain run no longer touches the vault;
the --scan/--repo escape hatch still allows manual one-offs. Updated SKILL.md,
check-catalog, findings-format, README, CLAUDE.md with the rationale.

Default run: 3 domains, 37 repos, 54 findings (was 61); clears the vault-only
false positives (herbygitea doc-mention, wiki-context uv-init stub).
2026-05-23 06:49:50 -04:00

136 lines
6.2 KiB
Markdown

---
name: repo-hygiene-audit
description: >
Repeatable hygiene audit across the homelab / pbs / docker project
domains. Runs scripted, read-only checks for git auditability and .git
ownership drift, .env file permissions, inline secrets in compose files,
non-git directories, uv-init leftover stubs, docker-compose.yml -> compose.yml
naming, branch and master-vs-main default-branch state, dirty trees, stray
worktrees, and herbygitea SSH-alias residue — then emits a findings report
mapped to the remediation-plan phases. Use when asked to audit repos, run a
repo-hygiene sweep, check project folders for drift, do the weekly/recurring
guided review, or verify repos against conventions before a commit/merge.
---
# Repo Hygiene Audit
Makes the one-time 2026-05-19/20 documentation/repo audit **repeatable** so the
project folders don't silently drift back into inconsistency. The mechanical
checks are scripted (`scripts/audit.py`); the judgement calls (semantic doc
freshness, policy decisions) stay with the human/agent driving the review.
This skill is **read-only against the trees it scans.** It never edits, commits,
or pushes anything in the audited repos — it reports. Remediation is a separate,
deliberate step the operator drives.
## When to run
- The recurring guided review (current cadence: **weekly, manually triggered**).
- After landing a batch of new repos or scaffolds, to confirm they start clean.
- Before a commit/merge sweep, to catch state that should be resolved first.
- Whenever asked to "audit repos", "check for drift", "repo hygiene sweep".
## The method
The original audit scored docs on a five-dimension rubric — **accuracy
(heaviest) → freshness → internal contradiction → completeness → role fit** —
and verified documented commands/paths against the real tree (a confidently
wrong doc is worse than a missing one). That semantic scoring is the human part.
This skill automates the **mechanical enumeration** underneath it: sweep every
repo in every domain and flag the objective signals that drift produces.
Default domains scanned:
| Domain | Roots scanned |
|--------|---------------|
| `homelab` | `/opt/projects/homelab/*` |
| `pbs` | `/opt/projects/pbs/*` |
| `docker` | `/opt/projects/docker/*`, `~/projects/docker/*` (compose stacks) |
`docker` repos are treated as compose stacks: README expected, **no per-stack
CLAUDE.md** (per the locked doc-role convention).
**The Obsidian vault (`wiki-vault`, `wiki-context`) is intentionally out of
scope.** It's an MCP-owned, managed surface — the vault MCP maintains its own
context — so a second scanner poking at it risks interference. The original
audit covered it as a "root" domain; this recurring skill deliberately does
not. The `--scan` / `--repo` escape hatch can still point at it manually if
ever truly needed; don't re-add it to the defaults.
## Workflow
### Step 1 — Run the scripted sweep
```bash
cd repo-hygiene-audit
# Human-readable, all domains (default config):
python scripts/audit.py
# Phase-mapped markdown you can paste into the remediation plan:
python scripts/audit.py --format md -o findings-$(date +%Y%m%d).md
# Machine-readable for diffing week-over-week:
python scripts/audit.py --format json -o findings-$(date +%Y%m%d).json
```
No third-party dependencies — plain `python3` (or `uv run python`) is enough.
### Step 2 — Triage by phase
Findings are grouped into the categories that map onto the remediation plan's
phases (see `references/findings-format.md`):
- **SECURITY** (Phase 2) — world-readable / wrong-mode `.env`, inline compose secrets.
- **AUDITABILITY** (Phase 3) — missing `.git`, dubious ownership, `.git`/dir owner drift, non-git dirs with content.
- **CLEANUP** (Phase 4) — uv-init stubs, compose naming, stale review docs, herbygitea residue, stray backup dirs.
- **GIT_STATE** — master-vs-main default, dirty trees, leftover `docs-audit` branches, unpushed commits, stray worktrees.
- **DOCS** — missing README/CLAUDE.md, oversized CLAUDE.md (possible README clone).
Start at `high`, work down. The `INVENTORY` block also lists every repo (even
clean ones) so you can confirm the sweep saw what you expected.
### Step 3 — Semantic doc pass (human judgement)
For each repo with stale-looking docs, apply the five-dimension rubric by hand:
read README + CLAUDE.md against the actual tree. The script flags *candidates*
(missing docs, bloated CLAUDE.md, stale review artifacts); it can't judge
whether the prose is *accurate*. That's the reviewer's call.
### Step 4 — Decide remediation, don't auto-fix
The hint on each finding is the suggested remediation, **not** an action the
skill takes. Sequence fixes through the normal flow (and the manual-push rule
for GitHub-destined repos). For the safe.directory caveat specifically: prefer a
**scoped** `safe.directory <path>` entry over the `*` wildcard, which disables
the dubious-ownership safety check everywhere.
### Step 5 — Persist the run
Save the markdown report alongside the project plan (or as a vault session note)
so the next run can diff against it. Drift shows up as new findings; remediation
shows up as findings that disappear.
## Scripted checks
`scripts/audit.py` is the engine. Each check is read-only and resilient (one
repo erroring never aborts the sweep). Full catalogue with rationale in
`references/check-catalog.md`. Selected flags:
| Flag | Purpose |
|------|---------|
| `--format {text,json,md}` | output format (default `text`) |
| `-o, --output PATH` | write report to a file instead of stdout |
| `--config FILE.json` | override the domain map (`{domain: {scan:[], repos:[]}}`) |
| `--scan NAME=PATH` | ad-hoc: scan immediate children of PATH as repos |
| `--repo NAME=PATH` | ad-hoc: treat PATH itself as one repo |
| `--only CATEGORY` | restrict output to one or more categories |
| `--fail-on SEVERITY` | exit non-zero if any finding at/above that severity (CI/automation hook) |
## Available resources
| Resource | When to load | Purpose |
|----------|--------------|---------|
| `scripts/audit.py` | Every run | The read-only check engine |
| `references/check-catalog.md` | Interpreting a finding | What each check looks for + why it matters |
| `references/findings-format.md` | Wiring output into the plan | Output schema + category→phase mapping |