docs: reshape runbooks into .agents/skills/<name>/SKILL.md (phase 4)

Problem: runbooks are agent-executable procedures but lived at the repo root,
separate from the other agent instruction now under .agents/.

Change:
- Move runbooks/<name>.md -> .agents/skills/<name>/SKILL.md (folder per skill,
  matching the wiki-hq skills layout). Frontmatter (name, risk_class, inputs,
  verification, docs_update_checklist, transition) preserved.
- Rewrite links (inbound from plans; between-skill siblings) via the move map.
- Update prose references in AGENTS.md, HERMES.md, .agents/OIKOS.md, and the
  operations schema; fix a pre-existing stale link to operations/commands.md.

No code consumed runbooks/ by path, so nothing else changes.

Verification: all SKILL.md frontmatter parses with valid risk_class; every
lifecycle transition resolves to an oikos/ontology.yaml state; broken-link
count 127 -> 126 (fixed one, introduced none).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-06 14:39:31 +02:00
parent 8a6422bd7d
commit 5c5016b3c7
16 changed files with 23 additions and 22 deletions

View File

@@ -40,7 +40,7 @@ one pass through **Observe → Orient → Decide → Act**:
|---|---|---| |---|---|---|
| Host / Service | topology entities | `inventory.yaml` (+ generated `hosts/*.yaml`) | | Host / Service | topology entities | `inventory.yaml` (+ generated `hosts/*.yaml`) |
| Secret | SOPS+age encrypted value, per-client recipients | `secrets/` + `.sops.yaml` | | Secret | SOPS+age encrypted value, per-client recipients | `secrets/` + `.sops.yaml` |
| Runbook | executable workflow with risk class + verification | `runbooks/` (Week 2) | | Runbook | executable workflow with risk class + verification | `.agents/skills/<name>/SKILL.md` |
| Signal | something needing attention, with lifecycle | `signals/` ledger (Week 3) | | Signal | something needing attention, with lifecycle | `signals/` ledger (Week 3) |
| Change | one mutation: who, what, risk, approval, verification | `ledger/` (Week 2) | | Change | one mutation: who, what, risk, approval, verification | `ledger/` (Week 2) |
| Approval | short-TTL signed grant for a gated action | approval engine (Week 3) | | Approval | short-TTL signed grant for a gated action | approval engine (Week 3) |

View File

@@ -20,8 +20,8 @@ versioned, reviewable, and reachable by MCP `get_page`/`search_docs` like any ot
## Runbooks ## Runbooks
Repeatable procedures live in `runbooks/<name>.md` with YAML front-matter that the Oikos policy and Repeatable procedures are skills — one folder per skill at `.agents/skills/<name>/SKILL.md`, with
lifecycle machinery reads: YAML front-matter that the Oikos policy and lifecycle machinery reads:
```yaml ```yaml
--- ---

View File

@@ -11,12 +11,12 @@ docs_update_checklist: [hosts_narrative_page_if_lxc_or_vm]
Goal: bring a new host (workstation, LXC, VM) into inventory and the Goal: bring a new host (workstation, LXC, VM) into inventory and the
secrets model, with mesh membership only where it's actually needed. secrets model, with mesh membership only where it's actually needed.
This wraps the existing `homelab client add` flow — see This wraps the existing `homelab client add` flow — see
[operations/agent-enrollment.md](../operations/agent-enrollment.md) for [operations/agent-enrollment.md](../../../operations/agent-enrollment.md) for
the full walkthrough; this runbook is the risk/lifecycle framing. the full walkthrough; this runbook is the risk/lifecycle framing.
1. On any enrolled client: `homelab client add <hostname>` — appends a 1. On any enrolled client: `homelab client add <hostname>` — appends a
`hosts.<name>:` block to `inventory.yaml` (lifecycle `state: planned` `hosts.<name>:` block to `inventory.yaml` (lifecycle `state: planned`
`provisioning`, per [oikos/ontology.yaml](../oikos/ontology.yaml)), `provisioning`, per [oikos/ontology.yaml](../../../oikos/ontology.yaml)),
commits + pushes. commits + pushes.
2. Netbird join is **optional, not a required step** — only needed for 2. Netbird join is **optional, not a required step** — only needed for
hosts that must be reachable off-LAN (workstations that roam, e.g. hosts that must be reachable off-LAN (workstations that roam, e.g.

View File

@@ -18,10 +18,10 @@ own repo) and get it live, safely.
`oikos/policy.yaml`. `oikos/policy.yaml`.
2. Clone/pull the `config_repo` (never edit the backend's working tree 2. Clone/pull the `config_repo` (never edit the backend's working tree
directly — tracked configs change by commit + push, per directly — tracked configs change by commit + push, per
[OIKOS.md](../OIKOS.md) conventions). [OIKOS.md](../../../OIKOS.md) conventions).
3. Make the change, commit, push to `main`. 3. Make the change, commit, push to `main`.
4. The Gitea webhook fires the deploy pipeline for that repo (see 4. The Gitea webhook fires the deploy pipeline for that repo (see
[infrastructure/auto-deploy.md](../knowledge/wiki/infrastructure/auto-deploy.md) for [infrastructure/auto-deploy.md](../../../knowledge/wiki/infrastructure/auto-deploy.md) for
the exact receiver/reload for this service). the exact receiver/reload for this service).
5. Run the preflight's verification command. If it fails, check 5. Run the preflight's verification command. If it fails, check
`homelab service <name> log` for the reload/restart error. `homelab service <name> log` for the reload/restart error.

View File

@@ -9,13 +9,13 @@ transition: "provisioning -> active"
# Lifecycle: activate a node # Lifecycle: activate a node
Per [oikos/ontology.yaml](../oikos/ontology.yaml). Requires: age key Per [oikos/ontology.yaml](../../../oikos/ontology.yaml). Requires: age key
enrolled if it needs secrets, mesh joined if it needs off-LAN reach, enrolled if it needs secrets, mesh joined if it needs off-LAN reach,
ingress live if public, health check answering, doc page complete, ingress live if public, health check answering, doc page complete,
ledger entry. ledger entry.
1. If the node is a `homelab` client: finish enrollment per 1. If the node is a `homelab` client: finish enrollment per
[client-enrollment.md](client-enrollment.md) (`--finalize-pubkey`, [client-enrollment.md](../client-enrollment/SKILL.md) (`--finalize-pubkey`,
mesh join, `homelab doctor` green). mesh join, `homelab doctor` green).
2. If it hosts a public service: add the `services:` entry in 2. If it hosts a public service: add the `services:` entry in
`inventory.yaml` (backend, url, doc_page, config_repo, risk_notes — `inventory.yaml` (backend, url, doc_page, config_repo, risk_notes —

View File

@@ -9,7 +9,7 @@ transition: "active -> deprecated"
# Lifecycle: deprecate a node # Lifecycle: deprecate a node
Per [oikos/ontology.yaml](../oikos/ontology.yaml): a node keeps running Per [oikos/ontology.yaml](../../../oikos/ontology.yaml): a node keeps running
but takes no new dependents. **Completion condition: zero remaining but takes no new dependents. **Completion condition: zero remaining
inbound `depends-on`/`routes-to` edges** — this is a hard gate, not a inbound `depends-on`/`routes-to` edges** — this is a hard gate, not a
suggestion; `oikos/policy.yaml` `lifecycle_overrides.deprecated.refuse` suggestion; `oikos/policy.yaml` `lifecycle_overrides.deprecated.refuse`
@@ -32,4 +32,4 @@ before proceeding — that's exactly the kind of drift the Week-3 detector
will catch automatically, but until then this manual check is the gate. will catch automatically, but until then this manual check is the gate.
Next (once `affected_by` is empty): Next (once `affected_by` is empty):
[lifecycle-destroy-node.md](lifecycle-destroy-node.md). [lifecycle-destroy-node.md](../lifecycle-destroy-node/SKILL.md).

View File

@@ -10,7 +10,7 @@ transition: "active -> migrating -> active"
# Lifecycle: migrate a node # Lifecycle: migrate a node
Modeled on the strong Phase 1+2 migration Modeled on the strong Phase 1+2 migration
([plans/2026-06-03_110000-library-ssd-migration-to-ludo-mini.md](../plans/2026-06-03_110000-library-ssd-migration-to-ludo-mini.md)). ([plans/2026-06-03_110000-library-ssd-migration-to-ludo-mini.md](../../../plans/2026-06-03_110000-library-ssd-migration-to-ludo-mini.md)).
Requires (ontology): preflight + backup-verified before migrating; Requires (ontology): preflight + backup-verified before migrating;
post-verify + Caddy backends checked + mounts checked + docs updated post-verify + Caddy backends checked + mounts checked + docs updated
before returning to `active`. before returning to `active`.

View File

@@ -9,7 +9,7 @@ transition: "planned -> provisioning"
# Lifecycle: provision a node # Lifecycle: provision a node
Per [oikos/ontology.yaml](../oikos/ontology.yaml) `lifecycle.transitions`. Per [oikos/ontology.yaml](../../../oikos/ontology.yaml) `lifecycle.transitions`.
Policy note: `provisioning` nodes get a lifecycle override — Policy note: `provisioning` nodes get a lifecycle override —
`config_mutation` actions downgrade to `reversible_low` because nothing `config_mutation` actions downgrade to `reversible_low` because nothing
depends on the node yet (see `oikos/policy.yaml` `lifecycle_overrides`). depends on the node yet (see `oikos/policy.yaml` `lifecycle_overrides`).
@@ -30,4 +30,4 @@ chosen, doc page stub.
is enough to satisfy the transition requirement. is enough to satisfy the transition requirement.
4. Reserve the IP in DNS/DHCP notes if it's a fixed LAN address. 4. Reserve the IP in DNS/DHCP notes if it's a fixed LAN address.
Next: [lifecycle-activate-node.md](lifecycle-activate-node.md). Next: [lifecycle-activate-node.md](../lifecycle-activate-node/SKILL.md).

View File

@@ -102,9 +102,9 @@ Then `systemctl status apt-recovery` from a fresh ssh to check progress.
## Related ## Related
- [Operations cheatsheet](commands.md) - [Operations cheatsheet](../../../operations/commands.md)
- [Auto-deploy pipelines](../knowledge/wiki/infrastructure/auto-deploy.md) - [Auto-deploy pipelines](../../../knowledge/wiki/infrastructure/auto-deploy.md)
- [Hubris host page](../knowledge/wiki/hosts/hubris.md) - [Hubris host page](../../../knowledge/wiki/hosts/hubris.md)
## Changelog ## Changelog

View File

@@ -76,7 +76,8 @@ Grep is fine for browsing or when MCP is unreachable.
## 4. Wiki conventions ## 4. Wiki conventions
- Narrative pages live under `knowledge/wiki/{containers,hosts,vms,infrastructure}/`; - Narrative pages live under `knowledge/wiki/{containers,hosts,vms,infrastructure}/`;
procedural pages under `investigations/`, `operations/`, `runbooks/`, `plans/`. runbook procedures under `.agents/skills/<name>/SKILL.md`; other procedural
pages under `investigations/`, `operations/`, `plans/`.
Cross-link liberally; orphans are bugs. Cross-link liberally; orphans are bugs.
- Every page ends with a `## Changelog` section, entries in reverse-chrono - Every page ends with a `## Changelog` section, entries in reverse-chrono
order: order:

View File

@@ -19,8 +19,8 @@ When in doubt, check `/opt/homelab-context/` first.
For the canonical workflows (service health check, config change + For the canonical workflows (service health check, config change +
deploy, client enrollment, incident investigation, and each node deploy, client enrollment, incident investigation, and each node
lifecycle transition), read the matching file in `runbooks/*.md` before lifecycle transition), read the matching `.agents/skills/<name>/SKILL.md` before
acting. Each runbook carries its risk class, required inputs, the acting. Each skill carries its risk class, required inputs, the
verification command, and a docs-update checklist in its frontmatter — verification command, and a docs-update checklist in its frontmatter —
classify against `oikos/policy.yaml` using that risk class before any classify against `oikos/policy.yaml` using that risk class before any
mutation. Don't re-derive topology or the mutation path by grepping the mutation. Don't re-derive topology or the mutation path by grepping the

View File

@@ -42,7 +42,7 @@ is actually unused end-to-end.
## Provisioning steps (once pve_id is assigned) ## Provisioning steps (once pve_id is assigned)
Follow [runbooks/lifecycle-provision-node.md](../runbooks/lifecycle-provision-node.md): Follow [lifecycle-provision-node](../.agents/skills/lifecycle-provision-node/SKILL.md):
1. `pct create <new-id> ...` on hubris — confirm the assigned ID doesn't 1. `pct create <new-id> ...` on hubris — confirm the assigned ID doesn't
collide with 131 or anything else live. collide with 131 or anything else live.
@@ -53,7 +53,7 @@ Follow [runbooks/lifecycle-provision-node.md](../runbooks/lifecycle-provision-no
4. Point node_exporter at hubris + strong (either install locally on each, 4. Point node_exporter at hubris + strong (either install locally on each,
or scrape via SSH-tunneled metrics — install locally is simpler and is or scrape via SSH-tunneled metrics — install locally is simpler and is
the standard approach). the standard approach).
5. Follow [runbooks/lifecycle-activate-node.md](../runbooks/lifecycle-activate-node.md) 5. Follow [lifecycle-activate-node](../.agents/skills/lifecycle-activate-node/SKILL.md)
to flip to `active`, complete the doc page, regenerate to flip to `active`, complete the doc page, regenerate
`hosts/*.yaml` + `infrastructure/topology.md`. `hosts/*.yaml` + `infrastructure/topology.md`.
6. Extend `oikos/scheduler.py`'s disk/temp probes to query Prometheus 6. Extend `oikos/scheduler.py`'s disk/temp probes to query Prometheus