The findings-*.md rule (for generated timestamped reports) also matched the findings-format.md reference doc, so it was never tracked. Scope the rule to digit-prefixed names (findings-[0-9]*) and add the missing reference.
3.1 KiB
3.1 KiB
Findings output format
Three renderers, one finding model. Pick the format for the consumer:
text— terminal triage. Summary line, fullINVENTORY(every repo, clean ones included), then findings grouped by category.md— phase-mapped markdown. Each finding is a- [ ]checkbox under the remediation-plan phase heading it belongs to, so a run pastes straight into the project plan as actionable items.json— machine-readable for week-over-week diffing and automation.
Finding model
Every finding is one record:
| Field | Meaning |
|---|---|
domain |
homelab / pbs / docker (the Obsidian vault is out of scope by default — see check-catalog.md) |
repo |
Directory name within the domain |
path |
Absolute path |
category |
SECURITY / AUDITABILITY / CLEANUP / GIT_STATE / DOCS |
severity |
high / medium / low / info |
check |
Stable check id (e.g. no-git, env-world-readable:.env) |
detail |
Human-readable description of what was found |
hint |
Suggested remediation (informational — the skill does not apply it) |
key |
domain/repo:check — stable identifier for dedup/diffing (JSON only) |
The key is deliberately stable across runs: a finding that persists keeps the
same key, so diffing two JSON runs cleanly separates new drift (keys that
appeared) from remediated items (keys that disappeared).
Category → phase mapping
This is how findings line up with the repo-hygiene-remediation plan, so the recurring review feeds the same backlog structure the original audit used:
| Category | Plan phase |
|---|---|
SECURITY |
Phase 2 — Security remediation |
AUDITABILITY |
Phase 3 — Unblock un-auditable repos |
CLEANUP |
Phase 4 — Cross-repo cleanups |
GIT_STATE |
Git state (review before commit/merge) |
DOCS |
Docs (audit follow-up) |
JSON shape
{
"generated": "2026-05-22T23:05:00",
"host": "herbys-dev",
"repos_scanned": 39,
"summary": {
"by_severity": {"high": 11, "medium": 19, "low": 23, "info": 9},
"by_category": {"SECURITY": 5, "AUDITABILITY": 9, "CLEANUP": 8,
"GIT_STATE": 27, "DOCS": 13}
},
"inventory": [
{"domain": "docker", "repo": "authelia", "path": "...",
"is_git": false, "owner": "root", "git_owner": "", "group": "root",
"branch": "", "finding_count": 2}
],
"findings": [
{"domain": "docker", "repo": "authelia", "path": "...",
"category": "AUDITABILITY", "severity": "high", "check": "no-git",
"detail": "directory holds real content but has no .git",
"hint": "git init + initial commit ...", "key": "docker/authelia:no-git"}
]
}
Diffing two runs
python scripts/audit.py --format json -o this-week.json
# ... a week later ...
python scripts/audit.py --format json -o next-week.json
python3 - <<'PY'
import json
a = {f["key"] for f in json.load(open("this-week.json"))["findings"]}
b = {f["key"] for f in json.load(open("next-week.json"))["findings"]}
print("NEW drift :", sorted(b - a))
print("REMEDIATED :", sorted(a - b))
PY