Read-only scripted audit across homelab/pbs/docker/root domains: git auditability + .git ownership/dubious, .env perms (0660/devprojects), inline compose secrets, non-git dirs, uv-init stubs, compose naming, master-vs-main default, dirty trees, worktrees, herbygitea residue, doc presence. Emits text/json/md findings mapped to remediation-plan phases. SKILL.md + scripts/audit.py (stdlib-only) + references + packed .skill. First run: 39 repos, 62 findings, ruff-clean.
130 lines
5.9 KiB
Markdown
130 lines
5.9 KiB
Markdown
---
|
|
name: repo-hygiene-audit
|
|
description: >
|
|
Repeatable hygiene audit across the homelab / pbs / docker / root 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.
|
|
|
|
Domains (the four the audit covered):
|
|
|
|
| Domain | Roots scanned |
|
|
|--------|---------------|
|
|
| `homelab` | `/opt/projects/homelab/*` |
|
|
| `pbs` | `/opt/projects/pbs/*` |
|
|
| `docker` | `/opt/projects/docker/*`, `~/projects/docker/*` (compose stacks) |
|
|
| `root` | `/opt/projects/wiki-vault`, `/opt/projects/wiki-context` |
|
|
|
|
`docker` repos are treated as compose stacks: README expected, **no per-stack
|
|
CLAUDE.md** (per the locked doc-role convention).
|
|
|
|
## 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 |
|