Documentation audit — 2026-10-24¶
The repository had complete inventory page presence but incomplete navigation, stale operational instructions, and a failed publication pipeline. This audit repairs verified documentation defects and adds repeatable checks. It does not certify every operational claim or replace application recovery drills.
Scope and evidence¶
Baseline: 468ed484c5539925efb2720ea0956f3f7ae0abbb from main, audited in the
isolated codex/documentation-repair worktree. The baseline contained 1,284
tracked files, 204 site pages, 46 inventory entities, 26 service/tool
and template directories, 20 Ansible roles, 20 workflows, and 12
inventory generators.
Evidence includes the baseline strict-build log, inventory pointers, service
Compose/README/backup manifests, backup-fetch scripts, current workflow YAML,
public sitemap and search index, and read-only SSH observations from
infra-services and Proxmox. Local JSON reports live under
artifacts/docs-audit/ and are not published.
The following result distinguishes file presence, operational accuracy, completeness and public representation. A green build proves neither that a service is deployed nor that a disaster recovery procedure has been exercised.
Results by requirement¶
| Requirement | Baseline finding | Repair / remaining limit |
|---|---|---|
| Presence | All 46 entity documentation pointers resolved; all 26 service/tool directories had READMEs | New checker prevents missing pointers and missing service manuals |
| Accuracy | Broken links, stale versions, incorrect restore paths/commands and stale deployment prose | Corrected verified errors; live/inventory discrepancies listed below |
| Completeness | Service directory omitted broker, evidence bridge, canary and local evaluation tool; no complete role/workflow/generator map | Complete service directory and project map; source manuals remain private |
| Cloudflare Pages | 54 of 204 pages absent from navigation; 3 routes missing from production; 27 existing searchable pages stale | Navigation repaired and build checks strengthened; publication still requires merge and a successful deployment |
Publishing findings¶
The latest three Deploy Docs runs were failures when inspected. The latest
failed run stopped
at mkdocs build --strict: phase-7-owner-actions.md linked outside docs/
to ../../infra/tailscale/README.md. The same failure reproduced from the baseline.
It was a build failure before Wrangler, not evidence of a Cloudflare token failure.
The live sitemap had 201 routes, compared with 204 in the baseline. These baseline pages were absent from both sitemap and search:
Search-content comparison also found 27 stale baseline pages, including restore, secrets, Wazuh retirement, host facts and Reddit Explorer. This is measured against the pre-repair revision, not inflated by newly authored audit pages.
Repairs include all 54 navigation omissions, eight broken heading links, one
nonexistent directory landing page, and three dead private-repository source
links. Heading text was adjusted where GitHub and MkDocs generated different
fragment IDs. PR validation now builds the site and checks coverage before a
merge can reach the publishing workflow. Local/CI builds use uv.lock instead
of different Material versions.
Operational corrections¶
| Area | Verified defect | Correction and evidence |
|---|---|---|
| Monitoring | README lagged Compose for Prometheus, Alertmanager, Loki and Alloy | Matched tracked image tags; running containers confirmed the same tags |
| Komodo restore | Claimed Core imports the latest dump on startup | Replaced with explicit CLI restore and isolated-target validation; upstream procedure |
| Komodo setup | Directed operators to edit stacks in a disabled UI | Points to ResourceSync; Compose enables KOMODO_UI_WRITE_DISABLED |
| Traefik restore | Restored acme.json beside Compose |
Uses traefik_traefik-certs, matching Compose and backup manifest |
| Authentik restore | Used remote temporary path, compressed input to psql, and wrong database role |
Uses captured staging path, decompression and the fetch script's someone role; removes generic destructive drop/create advice |
| Saltbox recovery | Referred to nonexistent encrypted-source path and /opt/saltbox destination |
Uses actual encrypted source and /srv/git/saltbox/ mapping |
| Rexit restore | Used removed Compose service gallery |
Uses studio, matching Compose |
| Secret rendering | Multiple current guides converted YAML with sed |
Uses SOPS dotenv output; monitoring uses the existing multi-file renderer |
| Full disaster recovery | Old minimum VM sizing and omitted current Tier-1 applications/dependencies | Updated staged recovery map, linked application procedures and explicit acceptance gates |
| Broker status | README said the service was not deployed | Records observed healthy broker/database containers; does not claim mutation acceptance |
| Generated docs | Indexes incorrectly claimed automatic refreshing | States that stub generation creates missing files only |
| Retired workloads | Unmanic prose described a live mount and planned migration; capacity guide still prescribed Wazuh repair | Marked historical and removed current-action wording |
Unresolved accuracy and completeness findings¶
These are findings, not claims that production was changed during the audit.
| Priority | Finding | Evidence | Next action |
|---|---|---|---|
| High | Prox inventory OS is stale | Inventory Debian 12; live /etc/os-release Debian 13, PVE 9.2.21 |
Reconcile inventory and run all generators in a focused infrastructure change |
| High | infra-services allocation differs from inventory | qm config 123: 8 cores, 28 GiB, 200G disk; inventory: 12 cores |
Reconcile desired/current CPU allocation; docs now identify both |
| High | PostgreSQL allocation differs from inventory | pct config 102: 6 cores, 4 GiB, 20G rootfs; inventory: 4 cores, 8G rootfs |
Reconcile inventory; host page records the dated observation |
| Medium | Phoenix rootfs metadata is stale | pct config 124: 60G; inventory and older table: 40 GB |
Reconcile inventory; host page explicitly flags the difference |
| Medium | Rexit inventory port is stale | Inventory 8080; Compose listener and Traefik label 3210 |
Correct inventory and generated consumers in a focused follow-up |
| Medium | Stirling running image differs from the pinned reference | Live container reports latest-fat; Compose pins 2.14.0-fat |
Compare image digests before deciding whether runtime code differs; no redeploy performed |
| High | Complete restore acceptance is not established | Source review corrected real recovery errors but performed no live data restore | Schedule isolated Komodo/Authentik and full dependency-order drills; record outcomes |
| Medium | Private manuals are linked, not full-text published | Private GitHub repository; public Pages site | Review exact manual contents before any public mirroring; no bulk publishing hook was added |
| Medium | Standalone helper coverage is less complete than service coverage | scripts/workstation/ and historical one-off utilities rely on inline/source documentation |
Add task-oriented operator guides as those utilities are maintained; do not infer support from file presence |
The eight registered service inventory entries represent only a subset of the service directories. Existing onboarding documentation explicitly allows legacy stacks to migrate incrementally. This audit documents all directories without inventing missing live service inventory or altering generator outputs.
Verification boundary¶
Automated checks cover Markdown site-page representation, inventory documentation
pointers, service/tool README presence and index links, and role/workflow/generator
mappings. The private repository-link pass is limited to the exact
https://github.com/spadoople/homelab/ namespace and main-ref blob/tree URLs
rendered from site Markdown. It checks path existence in the local checked-out
tree, not the remote main revision or HTTP access. Old-owner URLs, other or
pinned refs, GitHub fragments, and links inside private README bodies are not
validated by that pass. See the
explicit private repository-link scope.
The built output must include each site page in HTML, sitemap and search. The live check compares expected routes and searchable page/section content with production. This scope clarification was added on 2026-10-25; it does not expand the checker, rewrite historical links, or claim a new execution of this audit's dated checks.
Semantic review focused on current entry points, source-of-truth statements, service versions/configuration, deployment and recovery commands, lifecycle claims, and publication procedures. Read-only live checks covered infra-services container names/images/health, the Proxmox OS, its 11 current guests, and selected guest resource configurations. They did not inspect every device, authenticate every UI, validate every external link, execute runbooks, or independently re-certify historical Owner TODO checkmarks and security closures.
Local checks¶
| Check | Result |
|---|---|
| Markdown lint and strict MkDocs build | Passed for 207 pages |
| Source and built-output coverage | Passed for all indexed components |
| Documentation regression tests | 8 passed |
| Ruff, changed YAML, inventory refs and stub presence | Passed |
| Commit hooks | Passed |
| Full Python suite on Windows | 317 cases: 2 failures, 1 import error, 7 skips; failures require POSIX fcntl, executable bits, or Linux shell paths |
| Linux CI | Check the PR's current run; do not infer a pass from local Windows results |
| Live Pages | Baseline drift confirmed; final publication check remains merge/deploy-gated |
Reproduce and close¶
Follow documentation validation. Merge only after the local/PR checks pass. After Deploy Docs succeeds, rebuild the exact merged revision and run the live comparison. Until that succeeds, the publication finding remains open even when the branch's local build is green.