docs: close out the instruction arc — t740 corrected on evidence, registers, ledger, S-37
gates / gates (push) Successful in 8s

target-selection.md said demo-hp has no off-site tier. Measured first: pvesm list felhom-pbs on the
box returns two snapshots in demo-hp's OWN namespace (2026-07-28, 2026-08-04) against ep0's
felhom-offsite. The claim was TRUE WHEN WRITTEN and went stale when F10 resolved 2026-07-23. The
measurement is kept in an HTML comment beside the corrected sentence. This file decides which
machine may be destroyed, so the sentence was load-bearing, not cosmetic.

R-229(b) CLOSED (agent 175 -> 99 eff). R-230(b) CLOSED (symlink, proven from fresh sessions).
R-230(a) part-actioned -- three false statements fixed, WARN loop added, bulk ruling still owed.

S-37: a claim in an instruction file is checked, not trusted.
This commit is contained in:
2026-08-06 11:49:22 +02:00
parent f49b1f390b
commit 5ca5082e7c
4 changed files with 275 additions and 4 deletions
+27
View File
@@ -17,6 +17,33 @@
## Standing rulings
**S-37 — A CLAIM IN AN INSTRUCTION FILE IS CHECKED, NOT TRUSTED (2026-08-06, R-229/R-230 close-out).**
1. **The workspace-root `CLAUDE.md` is a SYMLINK** to `documentation/runbooks/workspace-CLAUDE.md`.
One file reachable by two paths cannot diverge; two files kept identical by hand must be policed
forever. Check 5 accepts either shape — a clone elsewhere may still have two files — and for the
link it asserts the target **resolves**, because a dangling link means the workspace instructions
load nothing at all and there is no content left to notice is wrong.
2. **A citation that calls a register item OPEN is now gated** (check 7). The trigger is an
**openness claim**, not any citation: policing every mention would fire on ~30 legitimate
provenance citations and the gate would be switched off, which is R-29's own lesson.
3. **`MEMORY.md` is WARNed, never FAILed, on content.** Claude writes it between sessions; a hard
failure would refuse a human's push over a line no human typed. The warning is aimed at the model
that will next edit the file, which is what makes the loop self-correcting.
4. **A scan is not evidence until its false-positive class is known.** Three "expired statements"
reported on 2026-08-06 were all filenames matched inside markdown link targets, while the one real
expired claim carried no ISO date and was missed. Link targets are stripped before any content
judgement now.
5. **A red-proof that does not go red is a finding about the instrument.** Two shipping bugs in check
7 were found exactly that way — a state marker that is not self-closing (which made the check read
its own founding case as open), and a CLOSED exemption applied line-wide instead of to the
citation's clause.
6. **Correct a runbook only after measuring.** `target-selection.md` said demo-hp had no off-site
tier; `pvesm list felhom-pbs` on the box returns two snapshots in its own namespace, newest
2026-08-04. The claim was true when written and went stale when F10 resolved. This file decides
which machine may be destroyed, so a wrong statement about what a machine holds is how a drill
lands somewhere it should not.
**S-36 — THE AUTO-MEMORY STORE IS BACKED UP, NOT COMMITTED; AND THE WORKSPACE IS INSTALLABLE
(2026-08-06, R-229 part 2).**
@@ -0,0 +1,218 @@
# LEDGER — instruction arc close-out (agent split, memory truth, checks 6-content and 7, symlink, t740, context7)
Third and final part. Companions: `LEDGER-instruction-trim-2026-08-06.md`,
`LEDGER-instruction-trim-part2-2026-08-06.md`. Same per-block format.
**Baselines, reconfirmed live before editing, all clean and `HEAD == origin/main`:** `felhom.eu`
`92a076c239b9`, `felhom-controller` `66d80efb9f20`, `felhom-agent` `5b2666e3a2ae`,
`app-catalog-felhom.eu` `459766cb1639`.
---
## A. `felhom-agent/CLAUDE.md` — 175 → 99 effective lines
Measured 175, not the spec's 173: the CI correction pushed in part 2 added two lines.
| Heading / first words | Class | Destination | Reason |
|---|---|---|---|
| `## Layout (verified against the tree)` (27 ln) | derivable | `rule-file:*` + `deleted-derivable` | the tree and `REUSE.md` are its home — but the per-package annotations that were doing real work moved into the rule file for the area they describe, rather than being dropped as the controller's were |
| `## Build / run` release table + R-115/R-186/R-188 narrative (34 ln) | duplicated | `already-in:` the `felhom-build-deploy` skill | the file already said "never hand-roll it" and then restated the table, which drifts from the script it describes |
| UPID/`WaitTask`, privsep intersection, TLS pinning (6 ln) | path-bound | `rule-file:proxmox.md` | only bite when `internal/proxmox/**` is open |
| destructive-op trap rows (4 ln, from `REUSE.md` §3) | path-bound | `rule-file:proxmox.md` | |
| local-API scoping, nonce store, bind traps | path-bound | `rule-file:localapi.md` | |
| PBS three laws, verify default, prune grant | path-bound | `rule-file:backup.md` | |
| format-safety, durable-ID schemes, escrow errors | path-bound | `rule-file:storage.md` | |
| `## Demo host` addresses (8 ln) | duplicated | `already-in:operations/nodes.md` | replaced by two rows in the retrieval map |
| CHANGELOG/REPORT/secrets blockquote, code-quality line | duplicated | `already-in:CLAUDE.md` (workspace root) | |
| artifact taxonomy, `TASK.md`/`RUNBOOK-*.md` (12 ln) | duplicated | `already-in:CLAUDE.md` (workspace root) | |
**Kept in the core** — it is the only part re-injected after `/compact`: the **root-CLI fence and its
three named exceptions**, the destructive-op gate, prove-ownership (audit A1), the gate entry point,
the F9 live-validation fence, trunk-based with its revert-and-report escape hatch, and the checklist.
**New:** `.claude/rules/{proxmox,localapi,backup,storage}.md`, all ≤46 effective lines.
**Glob overlap, stated rather than silently resolved.** `health-checks.md` already matched
`internal/{capability,storage,localapi,hub,guesthook}/**`, which `localapi.md` and `storage.md` now
also match. Both rules load in those directories and neither supersedes the other; each new file says
so in its own text, so a reader who sees two rules fire is not left guessing which wins.
---
## B. The memory index — three false statements, corrected
**Backup first:** `/mnt/5_hdd/felhom.eu/backups/claude-memory-20260806-112853` (158 files).
**The premise needed correcting before the work could be done, and the error was mine.** Part 2's
report said "3 expired temporal statements", and this task inherited that number. Re-run with the
cause printed, **all three matched the ISO date inside a markdown link TARGET** — a filename, not a
claim — while the one real expired claim in the same file was written `~08-02`, carried no ISO date,
and was never matched. A scan that reports the wrong three and misses the right one is worse than no
scan.
The three genuinely false statements, each established by evidence rather than by the scan:
| Line | Claim | How it was falsified |
|---|---|---|
| 17 | `R-193 decision open` | its register row reads `**CLOSED 2026-08-05 — controller v0.200.0**` |
| 34 | `demo boxes REMOTE till ~08-02` | live: `ssh felhom-pve` answers and holds `192.168.0.162/24` on `vmbr0` — the home LAN, same /24 as DooPlex |
| 68 | `OPEN R-25b` | ROADMAP: `**SHIPPED hub v0.69.0 (2026-07-21)**`; capability map: `R-25b CLOSED` |
Only the temporal claim was removed from line 34; its durable half — that `ssh felhom-pve` still
resolves to the **tailnet** address while `felhom-pve-lan` is the LAN alias — was kept, because it is
still true and still surprising.
**Nothing else in the file was edited. No memory file was deleted. 158 files before and after.**
The remaining 32 version literals and 4 host addresses stay, for the warning loop to work on — their
survival is deliberate, not an oversight.
---
## C. Check 6 gains content WARNings; check 7 is new
### Why WARN and not FAIL for the index
Claude writes `MEMORY.md` between sessions, so a hard failure would refuse a human's push over a line
no human typed. The warning is read by the same model that will next edit the file, which makes the
loop self-correcting rather than a chore for a person.
### Check 7 — a citation that calls a register item OPEN must be right
**Deviation from the task's literal wording, stated because it is a real one.** The task says every
citation of a non-open item must itself contain `CLOSED`. Applied literally that fires on ~30
legitimate provenance citations — `(R-161)`, `R-117 spike §6.3` — which cite an item as the **source
of a fact**, not as outstanding work. A gate that noisy is switched off, which is precisely the fate
R-29 documented. **The trigger is therefore an OPENNESS CLAIM**, which still convicts both real
defects and keeps the spec's required pass case (`R-168, CLOSED 2026-08-02`) green.
**Two bugs found by this check's own red-proofs, both of which would have shipped:**
1. **The state marker is not self-closing.** Real rows read `**SHIPPED — and the alarm is
DEMONSTRATED**`. The first parser required `**SHIPPED**` and therefore read **R-168 — the row the
whole check exists for — as OPEN**. A gate that cannot convict its own founding case is decoration.
2. **The CLOSED exemption was line-wide.** `- [Customer RESET shipped](customer-reset-…-2026-07-17.md)
— …; OPEN R-25b` contains "shipped" twice, in a title and a filename, and the whole line was
pardoned. Found **only because the red-proof failed to go red**, and now scoped to the citation's
clause with a regression test.
**Suite 39 → 68 assertions, 0 failures.** Red-proofs against real files, both directions, quoted in
the report.
**What check 7 finds across the four repos right now: nothing** — 19 citations, 0 failures. That is
because part 2 already corrected the four stale sentences; the red-proof, not the zero, is what shows
the check works.
---
## D. The workspace symlink (R-230(b)) — done, and PROVEN
`/mnt/5_hdd/felhom.eu/git/CLAUDE.md` is now a **relative symlink** to
`felhom.eu/documentation/runbooks/workspace-CLAUDE.md`. Backup of the pre-link file:
`/mnt/5_hdd/felhom.eu/backups/workspace-CLAUDE.md.bak-20260806-113952`.
**Check 5 now has two legal shapes and asserts a different thing in each:**
| Shape | Asserted | Rationale |
|---|---|---|
| LINK | it points at the versioned copy **and resolves to a real file** | divergence is impossible; what can still break is the link. **A dangling link is worse than a diverged copy** — the instructions load NOTHING and there is no content left to notice is wrong |
| COPY | byte-identical, as before | a clone elsewhere may legitimately have two files; the link is not forced on anyone |
**Proven, not assumed** — three fresh sessions each logged
`session_start … /mnt/5_hdd/felhom.eu/git/CLAUDE.md` (Claude Code reports the LINK path, not the
resolved one). And because a logged path proves discovery rather than delivery, a fourth fresh
session **with no tools at all** was asked to quote standing rule 1 and returned it verbatim. The
content reaches the model.
---
## E. The t740 off-site claim — measured, then corrected
`target-selection.md` said the PBS-DR / off-site tier is carried by the N100 and **"(demo-hp has
none)"**, and used that as the reason to steer backup-disturbing tests at demo-felhom.
**Measured on the box, from the tier's own evidence:**
```
demo-hp:/etc/pve/storage.cfg pbs: felhom-pbs -> datastore felhom-offsite, server 10.77.0.1,
namespace demo-hp, username felhom@pbs!demo-hp
demo-hp# LC_ALL=C pvesm list felhom-pbs
felhom-pbs:backup/ct/9201/2026-07-28T19:19:45Z pbs-ct 6264034048 9201
felhom-pbs:backup/ct/9201/2026-08-04T19:24:16Z pbs-ct 4637840512 9201
ep0:/mnt/pbs-datastore/ns/ c11 demo-felhom demo-hp rewalk
```
Two snapshots, newest two days old, in demo-hp's **own namespace** on the shared off-site datastore.
**The claim is false.** It was **true when written** and went stale when F10 resolved on 2026-07-23 —
the first snapshot is 2026-07-28. Corrected, with the measurement kept in an HTML comment beside it.
**Why this outranked a typo:** the sentence was load-bearing for "which machine may be destroyed".
"Its backup chain is not disturbable" is exactly the belief under which a drill lands somewhere it
should not.
---
## F. context7 — diagnosed, nothing written
**There is no Node.js on DooPlex at all.** `npx`, `node` and `npm` are all absent from `PATH`, no
`nodejs`/`npm` package is installed, and no runtime directory exists. The plugin manifest
(`~/.claude/plugins/.../context7/.mcp.json`) is:
```json
{ "context7": { "command": "npx", "args": ["-y", "@upstash/context7-mcp"] } }
```
So the `ENOENT` is literal: the `npx` binary does not exist. The fix is one package install away —
**and that decision is not this task's to make.** DooPlex is Tier 2 and *is* the recovery chain
(hub, Gitea, registry, PBS, k3s+Longhorn); adding a language runtime to it for a docs-lookup server
is its own change with its own review, not a rider on a documentation task.
**Whether it is worth pursuing — measured, not repeated from the spec:**
| Module | Direct third-party dependencies |
|---|---|
| `felhom-agent` | **1** (`golang.org/x/crypto`) |
| `felhom.eu/hub` | **3** |
| `felhom-controller` | **6** |
~8 distinct packages, all small, stable and well known. The plausible consumer is the app catalog's
upstream images — and there the skill's existing trap already answers it: **a more
authoritative-sounding source of guesses is still a source of guesses, and `docker inspect` decides.**
Per §6, **nothing was written** to `felhom-app-catalog/SKILL.md` or to any rule file.
---
## G. Before / after
| File | before (eff/B) | after (eff/B) |
|---|---|---|
| workspace root `CLAUDE.md` | 142 / 11,280 | **142 / 11,280 — now a SYMLINK, one file not two** |
| `felhom.eu/CLAUDE.md` | 117 / 8,006 | 117 / 8,006 (untouched) |
| `felhom-controller/CLAUDE.md` | 92 / 6,341 | 92 / 6,341 (untouched) |
| **`felhom-agent/CLAUDE.md`** | **175 / 14,093** | **99 / 6,267** |
| `app-catalog-felhom.eu/CLAUDE.md` | 80 / 6,397 | 80 / 6,397 (untouched) |
| rule files, workspace-wide | 9 | **13**, every one `paths:`-scoped, all ≤60 effective |
| `MEMORY.md` | 150 ln / 17,977 B | 150 ln / 18,078 B — 3 statements corrected |
| gate suite | 39 assertions | **68** |
Every `CLAUDE.md` in the workspace is now under 120 effective lines except the workspace root at 142,
which is deliberate: it is the only file re-injected after `/compact`.
---
## H. Observations — noticed, not acted on
1. **R-129 has fresh evidence.** `target-selection.md` records that demo-hp has no baked SSH key and
needs G1 break-glass, "but a key authenticated on 2026-07-31 — R-129, unresolved". **A key
authenticated again today, 2026-08-06**, non-interactively (`BatchMode=yes`) from DooPlex — that is
how the t740 evidence in §E was gathered. The row is still right to be open, and now has a second
dated observation.
2. **`~/.ssh/config` carries the same expired vacation claim** the memory index did: *"demo-hp … at
the VACATION site until ~2026-08-02, LAN 192.168.0.87 there"*. It is host state, outside every
repo and outside this task's scope, but it is the same statement in a third place.
3. **The hub's operator UI did not answer over its ClusterIP** during this session (request hung,
2 min timeout) — the off-site evidence was gathered from the boxes instead. Not investigated;
the memory note describing that access path may need re-checking.
4. **`MEMORY.md`'s header still reads "felhom-controller Project Memory"** though it indexes all four
repos plus the homelab. Cosmetic, left for the R-230(a) ruling.
5. **`register_state` is now a reusable register parser** living in the gate. If anything else ever
needs "is R-nnn open", it should call this rather than re-deriving it — the two parsing traps in
§C are not obvious and were both found the hard way.
+2 -2
View File
@@ -113,8 +113,8 @@ the fault was real. Full observables: `tests/campaign11-evidence-2026-08-05/jour
| ID | What | State |
|---|---|---|
| **R-229** | **The instruction-file rightsizing landed for `felhom-controller` and the workspace root; three pieces were deliberately deferred.** Done 2026-08-06: controller split into a 92-effective-line core plus four `paths:`-scoped `.claude/rules/*.md`; workspace root 208→142 effective lines with its versioned copy kept byte-identical; surgical corrections to `felhom-agent` and `felhom.eu` (expired TEMPORARY block, every version literal, the Legacy-Windows copies, the duplicated health-check rule); five contradictions resolved — including a drill-VM claim **measured live** (`qm list` on demo-hp shows VM 300 `drill-r50`; `felhom-agent` was right, `felhom-controller` was wrong); new shared `felhom.eu/scripts/instructions_gate.py` registered in `controller_gates.py` and `agent_gates.py`, 20 fixture tests + red-proof. **Leg (a) CLOSED 2026-08-06 (part 2):** `felhom.eu/CLAUDE.md` **227 → 115 effective lines**, split into a core plus `.claude/rules/{hub,website,manifests,docs}.md`; `instructions_gate` **registered in `scripts/repo_gates.py`** (six gates, all OK) in the required order — trim first, register second, because a registered-but-failing gate refuses every push. Scoping proven from the `InstructionsLoaded` hook log in two fresh sessions, not from frontmatter. **Still deferred:** (b) **`felhom-agent/CLAUDE.md` at 173 effective lines** passes with the least headroom; its release section is the next candidate for the `felhom-build-deploy` skill. (c) **CLOSED 2026-08-06 (part 2)** — all 44 orphans resolved with **zero deletions** (file count 158 before and after): 4 durable `reference`-type files indexed, 40 dated episode records moved to `.claude-memory/archive/`. `MEMORY.md` 145 → **150 lines / 17,977 bytes**, and `instructions_gate` check 6 now watches it (over-limit FAILS, orphan WARNS, absent store PASSES *printing its reason*). (d) **The spec-as-failing-test pilot** — moved to R-230. Full accounting: `audits/LEDGER-instruction-trim-2026-08-06.md` + `audits/LEDGER-instruction-trim-part2-2026-08-06.md` | **READY** — owner Viktor |
| **R-230** | **Three instruction/memory follow-ups deliberately left by the part-2 session (2026-08-06), each needing a decision rather than an implementation.** (a) **A ruling is owed on auto-written staleness.** The hand-written `CLAUDE.md` files are now clean of version literals and expired blocks — the gate enforces it — but `MEMORY.md`, which Claude writes and which is the LARGER half of what loads (8.4k tokens vs the root file's 6.6k), carries **21 lines with component version literals**, **5 with bare host addresses**, and an entry still reading *"demo boxes REMOTE till ~08-02"* — the same expired-TEMPORARY class the gate was built to kill, now surviving in the one file the gate's content rules do not cover. Diagnosed and **deliberately not edited**: nobody has ruled on how to correct Claude's own notes at scale. (b) **The symlink decision** for the workspace-root `CLAUDE.md`. Now that `install_workspace.py` exists, making the live file a symlink to `documentation/runbooks/workspace-CLAUDE.md` would remove the divergence class outright. **Recommended, not done** — it changes what check 5 is checking (identity of two files becomes correctness of one link), so it needs its own change. (c) **The spec-as-failing-test pilot**, approved in principle and not started (was R-229(d)). | **READY** — owner Viktor |
| **R-229** | **The instruction-file rightsizing landed for `felhom-controller` and the workspace root; three pieces were deliberately deferred.** Done 2026-08-06: controller split into a 92-effective-line core plus four `paths:`-scoped `.claude/rules/*.md`; workspace root 208→142 effective lines with its versioned copy kept byte-identical; surgical corrections to `felhom-agent` and `felhom.eu` (expired TEMPORARY block, every version literal, the Legacy-Windows copies, the duplicated health-check rule); five contradictions resolved — including a drill-VM claim **measured live** (`qm list` on demo-hp shows VM 300 `drill-r50`; `felhom-agent` was right, `felhom-controller` was wrong); new shared `felhom.eu/scripts/instructions_gate.py` registered in `controller_gates.py` and `agent_gates.py`, 20 fixture tests + red-proof. **Leg (a) CLOSED 2026-08-06 (part 2):** `felhom.eu/CLAUDE.md` **227 → 115 effective lines**, split into a core plus `.claude/rules/{hub,website,manifests,docs}.md`; `instructions_gate` **registered in `scripts/repo_gates.py`** (six gates, all OK) in the required order — trim first, register second, because a registered-but-failing gate refuses every push. Scoping proven from the `InstructionsLoaded` hook log in two fresh sessions, not from frontmatter. **Still deferred:** (b) **CLOSED 2026-08-06 (close-out)**`felhom-agent/CLAUDE.md` **175 → 99 effective lines** (measured 175, not 173: the CI correction added two), split into a core plus `.claude/rules/{proxmox,localapi,backup,storage}.md` beside the existing `health-checks.md`. The release section now points at the `felhom-build-deploy` skill instead of restating a table that drifts from the script. **Every `CLAUDE.md` in the workspace is now ≤120 effective lines except the workspace root at 142, which is deliberate — it is the only file re-injected after `/compact`.** (c) **CLOSED 2026-08-06 (part 2)** — all 44 orphans resolved with **zero deletions** (file count 158 before and after): 4 durable `reference`-type files indexed, 40 dated episode records moved to `.claude-memory/archive/`. `MEMORY.md` 145 → **150 lines / 17,977 bytes**, and `instructions_gate` check 6 now watches it (over-limit FAILS, orphan WARNS, absent store PASSES *printing its reason*). (d) **The spec-as-failing-test pilot** — moved to R-230. Full accounting: `audits/LEDGER-instruction-trim-2026-08-06.md` + `audits/LEDGER-instruction-trim-part2-2026-08-06.md` | **READY** — owner Viktor |
| **R-230** | **Three instruction/memory follow-ups deliberately left by the part-2 session (2026-08-06), each needing a decision rather than an implementation.** (a) **A ruling is owed on auto-written staleness.** The hand-written `CLAUDE.md` files are now clean of version literals and expired blocks — the gate enforces it — but `MEMORY.md`, which Claude writes and which is the LARGER half of what loads (8.4k tokens vs the root file's 6.6k), carries **21 lines with component version literals**, **5 with bare host addresses**, and an entry still reading *"demo boxes REMOTE till ~08-02"* — the same expired-TEMPORARY class the gate was built to kill, now surviving in the one file the gate's content rules do not cover. **Partly actioned 2026-08-06 (close-out), and the ruling is STILL OWED:** the **three statements that were actively false** were corrected — `R-193 decision open` (closed 2026-08-05), `demo boxes REMOTE till ~08-02` (the box answers on the home LAN), `OPEN R-25b` (shipped 2026-07-21) — and gate check 6 now **WARNs** on version literals, host addresses, expired statements and stale-open citations in the index. WARN, never FAIL: Claude writes that file between sessions, so a hard failure would refuse a human's push over a line no human typed, and the warning is read by the model that will next edit it. **The remaining 32 version literals and 4 host addresses were deliberately left** for that loop. What is still owed is the bulk-correction ruling. **Correcting the premise:** the earlier report's "three expired statements" were all FALSE POSITIVES — each matched an ISO date inside a markdown link target, i.e. a filename — while the one real expired claim carried no ISO date at all. (b) **CLOSED 2026-08-06 (close-out)** — the workspace-root `CLAUDE.md` **is now a relative symlink** to the versioned copy, so the divergence class is gone rather than policed. Check 5 learned two shapes: for a link it asserts the target resolves to a real file (**a dangling link is worse than a diverged copy — the instructions load NOTHING and there is no content left to notice is wrong**), for two files byte-identity as before, so a clone elsewhere is unaffected. **Proven, not assumed:** three fresh sessions logged `session_start` for the link path, and a fourth **with no tools at all** quoted standing rule 1 verbatim — the content reaches the model, not just the path. (c) **The spec-as-failing-test pilot**, approved in principle and not started (was R-229(d)). | **READY** — owner Viktor |
| **R-231** | **`/opt/backup/scripts/` on DooPlex is unversioned host state** — found 2026-08-06 while adding the auto-memory store to the backup set. No repository tracks the scripts that protect the recovery chain, so the edit made that day (`CLAUDE_MEMORY_DIR` in `backup-config.sh`, multi-path restic call in `backup-data.sh`) exists only on the box. This is the same class the part-2 session was closing, found inside the fix for it; the change is transcribed in `felhom.eu/workspace/README.md` so it is at least *recorded*. **Two related facts, both understating current safety:** the backup destination (`/mnt/5_hdd/backup`) is on the **same physical disk** as the workspace it protects, and the DooPlex backup set has **no off-site leg** (`sync-hetzner-backups.sh` is jarrs.eu and pulls *from* Hetzner *to* DooPlex). Bringing a root-owned production backup script under version control, and deciding what installs it, is its own scoped change. | **READY** — owner Viktor |
**Recorded against existing rows by Phase 2:**
+28 -2
View File
@@ -68,8 +68,34 @@ felhom-pve, and **moved off DooPlex**. This page exists because that ruling sat
### `demo-felhom` — N100 · **Tier 0**
- **Freely:** create/destroy guests and scratch customers; reinstall the box.
- **Care:** it carries the **PBS-DR / offsite tier** (demo-hp has none), so it is the Tier 0 box whose
backup chain a test can actually disturb. Prefer demo-hp, per the 2026-07-25 ruling.
- **Care:** it carries the **PBS-DR / offsite tier** — but so does demo-hp now, so this is **no longer
a reason to prefer one over the other**. Prefer demo-hp per the 2026-07-25 ruling, on the ruling's
own grounds (it is the designated drill host), not because its backup chain is safe to disturb.
<!--
CORRECTED 2026-08-06. This line used to read "(demo-hp has none)" and was the runbook's stated reason
for steering backup-disturbing tests at demo-felhom. It was TRUE WHEN WRITTEN and went stale when F10
resolved on 2026-07-23; it was flagged as wrong for some time and carried anyway.
Measured before editing, on the box, from the tier's own evidence rather than a config entry:
demo-hp:/etc/pve/storage.cfg pbs: felhom-pbs -> datastore felhom-offsite, server 10.77.0.1,
namespace demo-hp, username felhom@pbs!demo-hp
demo-hp# LC_ALL=C pvesm list felhom-pbs
felhom-pbs:backup/ct/9201/2026-07-28T19:19:45Z pbs-ct 6264034048 9201
felhom-pbs:backup/ct/9201/2026-08-04T19:24:16Z pbs-ct 4637840512 9201
ep0:/mnt/pbs-datastore/ns/ c11 demo-felhom demo-hp rewalk
Two snapshots, the newest two days old, in demo-hp's OWN namespace on the shared off-site datastore.
The PBS-DR leg is what was measured here; the restic/offbox app-data leg is separately evidenced by
R-193, whose whole subject is demo-hp's off-site app-data tier being dropped by the 2026-08-03
rebuild and restaged.
Why this mattered more than a typo: this file's job is to say which machine may be destroyed, and the
sentence was load-bearing for that judgement — "its backup chain is not disturbable" is exactly the
kind of belief under which a drill lands somewhere it should not.
-->
**Both Tier 0 boxes — the shared backup-target fence is DOWNGRADED to a cost, 2026-08-02 (D-d).** It
read *"do not re-point either backup target"*, because these are the only two **correctly configured**