Problem: the narrative docs lacked an enforceable style standard, and
agent-facing instruction (OIKOS/CAVEMAN/CONTRIBUTING) was interleaved with
human content at the repo root.
Change:
- Add .agents/shared/{writing-style,llm-wiki}.md — a lint-checkable prose
standard (with an imperative-voice exception for runbooks/recipes) and the
sources/wiki/index/log layer model.
- Move CAVEMAN.md -> .agents/shared/caveman.md,
CONTRIBUTING.md -> .agents/shared/page-templates.md,
OIKOS.md -> .agents/OIKOS.md; leave thin root stubs so old links resolve.
- Add .agents/domains/{knowledge,operations}/schema.md; operations schema
codifies "plans always live in plans/".
- Repoint live references (AGENTS, README, GLOSSARY, OIKOS) and fix OIKOS.md's
internal relative links for its new depth.
Risk: none to the operational substrate — inventory.yaml, hosts/*.yaml,
oikos/, mcp/, secrets/, bin/ untouched (verified via git status).
Verification: relative-link check across .agents/ clean; substrate churn empty.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
165 lines
11 KiB
Markdown
165 lines
11 KiB
Markdown
# 2026-07-06 — Adopt the wiki-hq documentation architecture
|
||
|
||
## Goal
|
||
|
||
Reorganize the Homelab-Docs **narrative layer** into the `wiki-hq`
|
||
(`/Users/dtoro/Downloads/wiki-hq-main`) documentation model — `sources / wiki / index / log`
|
||
plus an `.agents/` separation and a single lint-checkable writing-style standard — **without
|
||
breaking** the Oikos machine-readable substrate that reads fixed paths. Keep the existing Mermaid
|
||
topology generator (no LikeC4).
|
||
|
||
## Context
|
||
|
||
Homelab-Docs is already a mature operational-docs system: `inventory.yaml` as source of truth,
|
||
generated `hosts/*.yaml`, an MCP server, a `homelab` CLI, and the Oikos OODA kernel (scheduler,
|
||
drift, decide, ledger, approvals). Its weakness is on the *narrative* side — the prose layer grew
|
||
organically and lacks the discipline wiki-hq shows:
|
||
|
||
- No enforceable **writing-style** standard (CONTRIBUTING.md has a one-line "voice" note; CAVEMAN.md governs agent *chat*, not docs).
|
||
- **Index/README files are inconsistent** — some pure listings, some prose+tables, some with stale counts.
|
||
- **No append-only operations log** for doc maintenance — doc changes are visible only in git.
|
||
- **Agent instructions and human content are interleaved** at the repo root (AGENTS.md, OIKOS.md, CAVEMAN.md, CONTRIBUTING.md, GLOSSARY.md alongside `containers/`, `infrastructure/`, …).
|
||
|
||
Decision (confirmed with operator): **full structural adoption**, **keep Mermaid**, adopt all four
|
||
borrows — writing-style guide, section-index/README pattern, append-only per-area logs, and
|
||
agent-instruction separation.
|
||
|
||
## Hard constraint: protect the operational substrate
|
||
|
||
These paths are read programmatically and **must not move** (see `mcp/server.py`, `bin/homelab`, `oikos/`):
|
||
|
||
- `inventory.yaml` (root) — MCP (`mcp/server.py:36`), CLI (`bin/homelab:36`), scheduler, drift, relations, gen-topology.
|
||
- `hosts/*.yaml` (root, generated) — `HOSTS_DIR` (`mcp/server.py:37`); written by `mcp/build_host_files.py`; read/written by `bin/homelab`.
|
||
- `oikos/` — kernel code, `oikos/cards/` (`explain` tool, `mcp/server.py:38`), `oikos/state.json`.
|
||
- `secrets/`, `secrets-issuance/`, `ssh/`, `scripts/`, `tools/`, `vps/`, `bin/`, `bootstrap.sh`.
|
||
|
||
Two MCP tools are path-agnostic and survive any narrative reorg: `search_docs` (ripgreps all
|
||
`*.md`) and `get_page(path)` (agent supplies a repo-relative path). The **`## Changelog` convention
|
||
must stay** on node pages — `get_changelog` (`mcp/server.py:214`) and the ledger parse it. New
|
||
`log.md` files are **additive**, not a replacement for per-page changelogs.
|
||
|
||
## Current → target structure
|
||
|
||
Restructure the **narrative layer only**; leave the substrate in place.
|
||
|
||
```
|
||
Homelab-Docs/
|
||
README.md # human landing page (kept; refreshed to new nav)
|
||
AGENTS.md # kept at root (conventional discovery path)
|
||
|
||
.agents/ # NEW — agent-facing instruction, separated from content
|
||
shared/
|
||
writing-style.md # NEW — adapted from wiki-hq (homelab voice)
|
||
llm-wiki.md # NEW — the sources/wiki/index/log contract for THIS repo
|
||
caveman.md # moved from CAVEMAN.md (agent chat mode)
|
||
page-templates.md # moved from CONTRIBUTING.md (page templates)
|
||
domains/
|
||
knowledge/schema.md # contract for the current-state wiki
|
||
operations/schema.md # contract for runbooks / investigations / plans
|
||
skills/ # runbooks reshaped as SKILL.md (Phase 4)
|
||
lifecycle-provision-node/SKILL.md
|
||
service-health-check/SKILL.md
|
||
...
|
||
OIKOS.md # operating-model doc (moved; agent-facing)
|
||
|
||
knowledge/ # durable current-state wiki (Feedback/authoritative layer)
|
||
index.md # pure listing → section indexes only
|
||
log.md # append-only doc-maintenance operations log
|
||
GLOSSARY.md # moved from root
|
||
sources/
|
||
index.md # flat catalog table
|
||
references/ # external docs (from infrastructure/references/)
|
||
wiki/
|
||
hosts/ README.md + hubris.md + strong.md
|
||
containers/ README.md (= today's index.md) + <id>-<name>.md
|
||
vms/ README.md + <id>-<name>.md
|
||
infrastructure/ README.md + grouped: network/ identity/ storage/ ingress/ operations/
|
||
|
||
operations/ # operator runbook narrative index
|
||
index.md commands.md agent-enrollment.md hermes-agent.md
|
||
|
||
investigations/ # incident evidence (Observe sources)
|
||
README.md + log.md + YYYY-MM-DD-*.md + archive/
|
||
plans/ # projects / design docs (Act)
|
||
README.md + YYYY-MM-DD-*.md + done/
|
||
|
||
# substrate — UNCHANGED (see Hard constraint)
|
||
inventory.yaml hosts/*.yaml oikos/ mcp/ secrets/ secrets-issuance/
|
||
ssh/ scripts/ tools/ vps/ bin/ bootstrap.sh
|
||
```
|
||
|
||
**hosts/ split:** generated `hosts/*.yaml` stay at root (substrate); only the two *narrative* pages
|
||
`hosts/hubris.md` and `hosts/strong.md` move to `knowledge/wiki/hosts/`. This is the one directory
|
||
where machine and narrative content currently mix.
|
||
|
||
## Consumers to update when narrative paths move
|
||
|
||
Same phase as the move:
|
||
|
||
- **`inventory.yaml` `doc_page` fields** — per-host pointer; consumed by `bin/homelab` (`die("no doc_page recorded…")` near `bin/homelab:1288`). Repoint to `knowledge/wiki/...`.
|
||
- **`oikos/gen-topology.py` / `oikos/gen_topology_lib.py`** — write `infrastructure/topology.md` and link `oikos/cards/` to doc pages. Move `topology.md` under `knowledge/wiki/infrastructure/` and update the output-path constant (`oikos/gen-topology.py:3`, `:49`, `:195`).
|
||
- **`oikos/cards/` templates** and `oikos/drift.py` docstring at `oikos/drift.py:218` (`containers/121-caddy.md`) — cosmetic, update for accuracy.
|
||
- **Internal cross-links** — repo-wide relative-link rewrite + link-check pass.
|
||
- **Same-session update rule** (`CONTRIBUTING.md:125`) — rewrite its path checklist to the new layout.
|
||
|
||
`search_docs`, `get_page`, `get_changelog` need **no** code change.
|
||
|
||
## Writing-style standard (adapted, not copied)
|
||
|
||
Create `.agents/shared/writing-style.md` from
|
||
`/Users/dtoro/Downloads/wiki-hq-main/.agents/shared/writing-style/writing-style.md`:
|
||
|
||
- **Keep:** banned-vocabulary list (puffers, "leverage/utilize/delve", poetic nouns, promotional adjectives), information-density rule, bold-leading-phrase pattern, tables-over-bullets, `>` blockquotes for caveats, one-sided cross-references, "cross-references are content not catalog".
|
||
- **Adjust voice:** wiki-hq mandates strict third-person "no you". Homelab docs are operator runbooks that already use imperative recipes. Allow **imperative/second-person in runbooks, recipes, and skills**; third-person reference voice for node/infrastructure descriptions. State this exception explicitly.
|
||
- **Clarify vs CAVEMAN.md:** Caveman governs agent *chat responses*; writing-style governs *committed docs*. No conflict.
|
||
- **Make it lint-checkable:** the Oikos lint surface (or a new `.agents/skills/docs-lint`) greps banned words + structural violations.
|
||
|
||
## Reconciling "append-only per-area logs" with the Oikos ledger
|
||
|
||
- The **Oikos change ledger** (`oikos/ledger.py`) stays authoritative for *infrastructure changes* (who/what/risk/approval/verification). Do not duplicate it.
|
||
- **Per-page `## Changelog`** stays (parsed by `get_changelog`).
|
||
- New **per-area `log.md`** (`knowledge/log.md`, `investigations/log.md`, `plans/log.md`) records *doc-maintenance operations only* — restructures, source ingests, lint sweeps — in wiki-hq's single-line format `## [YYYY-MM-DD] <op> | <summary>`.
|
||
|
||
## Convention: plans always live in `plans/`
|
||
|
||
The restructure must codify — in `.agents/domains/operations/schema.md`, `AGENTS.md`, and
|
||
`.agents/shared/page-templates.md` — that **any plan or design doc for the Homelab is always
|
||
written into the repo `plans/` folder** (`plans/YYYY-MM-DD-slug.md`), never into a scratch/agent
|
||
plan path, an ad-hoc location, or a chat message. Agents drafting a plan create the file under
|
||
`plans/`, list it in `plans/index.md`, and move it to `plans/done/` on completion. This rule is
|
||
stated once in the operations schema and cross-referenced from `AGENTS.md` so every agent sees it
|
||
at orientation.
|
||
|
||
## Section-index / README pattern
|
||
|
||
Every folder gets a `README.md`: 1–3 sentence prose intro, then a single two-column nav table
|
||
`| Document | What it covers |`, nothing else — no stale counts, no duplicated prose. Pure-listing
|
||
`index.md` files (e.g. `containers/index.md`) become the folder `README.md`. Root `README.md` stays
|
||
the human landing page (may exceed the strict pattern).
|
||
|
||
## Phased rollout
|
||
|
||
Each phase independently valuable, independently verifiable, committed separately.
|
||
|
||
1. **Conventions first (no moves).** Add `.agents/shared/writing-style.md`, `llm-wiki.md`; move CAVEMAN.md → `.agents/shared/caveman.md`, CONTRIBUTING.md → `.agents/shared/page-templates.md` (leave thin root stubs pointing to the new locations). Verify: links resolve; MCP untouched.
|
||
2. **Agent-instruction separation.** Move OIKOS.md → `.agents/OIKOS.md`; author `.agents/domains/{knowledge,operations}/schema.md`. Keep AGENTS.md at root, pointing at `.agents/`. Verify: AGENTS.md still the discovery entry; `tools/setup-hermes-soul.sh:51` references resolve or are updated.
|
||
3. **Narrative move into `knowledge/wiki/`.** Move `containers/`, `vms/`, `infrastructure/`, host narratives; add `knowledge/index.md`, `knowledge/log.md`, `knowledge/sources/`. Same commit: update `inventory.yaml` `doc_page` fields, `oikos/gen-topology.py` output path, all internal links. Verify: link-check clean; CLI `doc_page` lookups work; `gen-topology.py` writes to the new path; MCP `get_page`/`search_docs` return moved pages.
|
||
4. **Runbooks → skills.** Reshape `runbooks/*.md` into `.agents/skills/<name>/SKILL.md` (they already carry `risk_class`, `inputs`, `verification` frontmatter — near-compatible). Update runbook-path references. Verify: frontmatter parses; lifecycle transitions still map to `oikos/ontology.yaml`.
|
||
5. **Style + README pass.** Apply the writing-style standard and README pattern across narrative pages; add `log.md` entries recording the restructure. Verify: docs-lint clean.
|
||
|
||
## Verification (end to end)
|
||
|
||
- **Link integrity:** markdown link checker (or `rg` over `](` targets) after each move phase; zero broken relative links.
|
||
- **MCP resolves docs:** `HOMELAB_CONTEXT_DIR=<repo>` — `get_page("knowledge/wiki/containers/104-gitea.md")` returns content; `search_docs("gitea")` hits new paths; `get_changelog(<moved page>)` parses.
|
||
- **CLI doc_page lookups:** exercise `bin/homelab`'s `doc_page` read against a moved page — no "no doc_page recorded" error.
|
||
- **Topology generation:** run `oikos/gen-topology.py` — writes the new `topology.md` path; `oikos/cards/` links point at moved pages.
|
||
- **Substrate untouched:** `mcp/build_host_files.py` regenerates `hosts/*.yaml` identically; `git diff` shows no substrate churn.
|
||
- **Style lint:** banned-vocabulary grep over `knowledge/` returns no hits; every folder `README.md` matches the intro-plus-nav-table shape.
|
||
|
||
## Post-migration
|
||
|
||
Update `plans/index.md` to list this plan; on completion move it to `plans/done/`. Record the
|
||
restructure in the new `knowledge/log.md`. Update `README.md` and `AGENTS.md` navigation to the new
|
||
layout.
|
||
```
|