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.
5.9 KiB
| name | description |
|---|---|
| repo-hygiene-audit | 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
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-auditbranches, 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 |