From 5ca5082e7caa4cbba654a2decec23ba34c866f4e Mon Sep 17 00:00:00 2001 From: kisfenyo Date: Thu, 6 Aug 2026 11:49:22 +0200 Subject: [PATCH] =?UTF-8?q?docs:=20close=20out=20the=20instruction=20arc?= =?UTF-8?q?=20=E2=80=94=20t740=20corrected=20on=20evidence,=20registers,?= =?UTF-8?q?=20ledger,=20S-37?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- CONTEXT.md | 27 +++ .../LEDGER-instruction-closeout-2026-08-06.md | 218 ++++++++++++++++++ documentation/backlog/OPEN-ITEMS.md | 4 +- documentation/runbooks/target-selection.md | 30 ++- 4 files changed, 275 insertions(+), 4 deletions(-) create mode 100644 documentation/audits/LEDGER-instruction-closeout-2026-08-06.md diff --git a/CONTEXT.md b/CONTEXT.md index 7b9c72a..eb6c332 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -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).** diff --git a/documentation/audits/LEDGER-instruction-closeout-2026-08-06.md b/documentation/audits/LEDGER-instruction-closeout-2026-08-06.md new file mode 100644 index 0000000..66a6253 --- /dev/null +++ b/documentation/audits/LEDGER-instruction-closeout-2026-08-06.md @@ -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. diff --git a/documentation/backlog/OPEN-ITEMS.md b/documentation/backlog/OPEN-ITEMS.md index 1d3a4e8..8103b0c 100644 --- a/documentation/backlog/OPEN-ITEMS.md +++ b/documentation/backlog/OPEN-ITEMS.md @@ -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:** diff --git a/documentation/runbooks/target-selection.md b/documentation/runbooks/target-selection.md index 2805bef..50e6135 100644 --- a/documentation/runbooks/target-selection.md +++ b/documentation/runbooks/target-selection.md @@ -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. + + + **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**