docs: ledger, registers and standing ruling S-36 for the part-2 instruction/memory work (R-229)
gates / gates (push) Successful in 8s
gates / gates (push) Successful in 8s
R-229 legs (a) and (c) CLOSED. R-230 opened for what this deliberately left -- the ruling owed on auto-written staleness, the symlink decision, the spec-as-failing-test pilot. R-231 opened for /opt/backup/scripts/ being unversioned host state, found while adding the memory store to the backup set: the same class this task was closing, inside the fix for it. S-36: the auto-memory store is backed up, never committed; no memory file is ever deleted; a rule file created mid-session is NOT live in that session. Second session in this repo, so REPORT-<topic>.md per CLAUDE.md; shared REPORT.md untouched.
This commit is contained in:
+30
@@ -17,6 +17,36 @@
|
|||||||
|
|
||||||
## Standing rulings
|
## Standing rulings
|
||||||
|
|
||||||
|
**S-36 — THE AUTO-MEMORY STORE IS BACKED UP, NOT COMMITTED; AND THE WORKSPACE IS INSTALLABLE
|
||||||
|
(2026-08-06, R-229 part 2).**
|
||||||
|
|
||||||
|
1. **`.claude-memory/` is never committed.** It is **auto-written** — Claude writes it, so nobody
|
||||||
|
reviews it before it lands — and may name hosts, paths and out-of-band secret locations that the
|
||||||
|
secrets rule keeps out of committed files. A scan on 2026-08-06 found no credential *values* (535
|
||||||
|
keyword mentions across 104 files, one `key: value`-shaped hit that was prose, zero private-key
|
||||||
|
blocks), but "no secrets today" is not a property a directory keeps on its own when a machine
|
||||||
|
writes to it unattended. It is protected by `dooplex-backup.service` instead
|
||||||
|
(`CLAUDE_MEMORY_DIR`). **Reversing this needs a passing secrets scan first.**
|
||||||
|
2. **No memory file is ever deleted.** Set-aside means **moved** to `.claude-memory/archive/`. The
|
||||||
|
part-2 reconciliation moved 40 and indexed 4, with the file count identical before and after.
|
||||||
|
3. **`MEMORY.md` is the index, and the index is what loads.** Topic files are read on demand, so
|
||||||
|
detail belongs in them, not in the index. `instructions_gate` check 6 now enforces the limits —
|
||||||
|
over-limit FAILS (auto-memory drops content past the limit with no error), an orphan WARNS (the
|
||||||
|
store is outside git), an absent store PASSES **while printing its reason**.
|
||||||
|
4. **Correcting auto-written content is NOT yet ruled on.** The part-2 session diagnosed 21 lines
|
||||||
|
carrying version literals, 5 carrying host addresses, and an entry still asserting a fact whose
|
||||||
|
hand-written twin was deleted as expired — and **changed none of them**. R-230(a).
|
||||||
|
5. **The workspace is installable.** `scripts/install_workspace.py` lays down the workspace-root
|
||||||
|
`CLAUDE.md` and the `InstructionsLoaded` hook from versioned copies. Its targets are **live
|
||||||
|
configuration**, not disposable like the skills: it backs up before every write, **merges**
|
||||||
|
`settings.json` owning exactly one key, and **refuses** an unparseable settings file rather than
|
||||||
|
overwriting it. `documentation/runbooks/workspace-CLAUDE.md` remains the canonical source — a copy
|
||||||
|
under `workspace/` would be a third copy of a file whose problem is that copies drift.
|
||||||
|
6. **A rule file created mid-session is NOT live in that session.** A directory whose instructions
|
||||||
|
were already seeded is not re-scanned. Proven the hard way: after writing four rule files, reads
|
||||||
|
that should have matched them produced no hook line at all. Verify new rules from a fresh session
|
||||||
|
(`claude -p`), never from the frontmatter.
|
||||||
|
|
||||||
**S-35 — INSTRUCTION FILES ARE A SHORT CORE PLUS PATH-SCOPED RULES (2026-08-06, R-229).**
|
**S-35 — INSTRUCTION FILES ARE A SHORT CORE PLUS PATH-SCOPED RULES (2026-08-06, R-229).**
|
||||||
|
|
||||||
Decided while rightsizing the four `CLAUDE.md` files. The mechanisms were verified before being
|
Decided while rightsizing the four `CLAUDE.md` files. The mechanisms were verified before being
|
||||||
|
|||||||
@@ -0,0 +1,154 @@
|
|||||||
|
# REPORT — instruction trim part 2: `felhom.eu`, the memory index, the versioned workspace
|
||||||
|
|
||||||
|
**2026-08-06.** Closes R-229 legs (a) and (c). Opens R-230 and R-231.
|
||||||
|
Full per-block accounting: `documentation/audits/LEDGER-instruction-trim-part2-2026-08-06.md`.
|
||||||
|
|
||||||
|
> **Parallel session.** A second Claude Code session was live in these repos throughout. Per
|
||||||
|
> `CLAUDE.md`'s rule this report is a `REPORT-<topic>.md` sibling; the shared `REPORT.md` was not
|
||||||
|
> touched.
|
||||||
|
|
||||||
|
## 1. Baselines
|
||||||
|
|
||||||
|
`felhom.eu` `c21bcf84f709` · `felhom-controller` `7db42c5fec3b` · `felhom-agent` `062a7027abff` —
|
||||||
|
all clean, all `HEAD == origin/main` at start. Only `felhom.eu` was written to.
|
||||||
|
|
||||||
|
## 2. `felhom.eu/CLAUDE.md`
|
||||||
|
|
||||||
|
| | before | after |
|
||||||
|
|---|---|---|
|
||||||
|
| raw lines | 252 | **130** |
|
||||||
|
| **effective lines** | **227** | **115** |
|
||||||
|
| bytes | 16,698 | 7,824 |
|
||||||
|
|
||||||
|
Split into a core plus `.claude/rules/{hub,website,manifests,docs}.md` — 4 files, all
|
||||||
|
`paths:`-scoped, all ≤60 effective lines. **`instructions_gate` is registered** in
|
||||||
|
`scripts/repo_gates.py`: six gates, all OK. Order was load-bearing — trim first, register second,
|
||||||
|
because a registered-but-failing gate refuses every push through `.githooks/pre-push`.
|
||||||
|
|
||||||
|
Two blocks were kept in the core against §6's sketch, both to avoid rebuilding the failure class
|
||||||
|
they prevent: **register discipline** (applies to every session, not only `documentation/**` ones)
|
||||||
|
and the **R-110 installer fence** (triggered by `scripts/felhom-host-install.sh`, which no fixed glob
|
||||||
|
matches). Ledger §C.
|
||||||
|
|
||||||
|
## 3. Hook evidence for Scenario B — both directions
|
||||||
|
|
||||||
|
Two **fresh** sessions, so the negative control cannot be explained by prior loading:
|
||||||
|
|
||||||
|
| Run | File read | `hub.md` | `website.md` |
|
||||||
|
|---|---|---|---|
|
||||||
|
| A | `website/index.html` | — | `path_glob_match` |
|
||||||
|
| B | `hub/internal/api/handler.go` | `path_glob_match` | — |
|
||||||
|
|
||||||
|
**Why fresh sessions were required — the session's most useful negative result:** after creating
|
||||||
|
`.claude/rules/`, in-session reads that should have matched produced **no hook line at all**. A
|
||||||
|
directory whose instructions were already seeded is not re-scanned. A rule file can be correct, pass
|
||||||
|
every gate, and reach the model never, purely because of when it was created.
|
||||||
|
|
||||||
|
## 4. Always-loaded total
|
||||||
|
|
||||||
|
Measured `/context` (operator-supplied, this session): Memory files held at **11.5k tokens** across
|
||||||
|
three rule loads while Messages grew 8 → 105.7k. **The 11.5k baseline is unchanged by this work**,
|
||||||
|
because `MEMORY.md` moved 17,688 → 17,977 bytes (+1.6%) — the reconciliation was net-neutral by
|
||||||
|
design, trading 40 archived entries for 4 indexed ones plus trimmed detail. A fresh session is needed
|
||||||
|
for a post-change token reading; the byte figures above are exact.
|
||||||
|
|
||||||
|
## 5. Memory reconciliation
|
||||||
|
|
||||||
|
Backup: `/mnt/5_hdd/felhom.eu/backups/claude-memory-20260806-103418` (158 files, taken first).
|
||||||
|
|
||||||
|
| | before | after |
|
||||||
|
|---|---|---|
|
||||||
|
| `.md` files | 158 | **158 — zero deletions** |
|
||||||
|
| orphaned | **44** | **0** |
|
||||||
|
| indexed / archived | 113 / — | 117 / **40** |
|
||||||
|
| `MEMORY.md` | 145 ln / 17,688 B | **150 ln / 17,977 B** |
|
||||||
|
|
||||||
|
**Headroom: 50 lines and 7,622 bytes (29%)** against the 200-line / 25 KB limits. The
|
||||||
|
index-vs-archive discriminator was the store's own `type:` field: all 4 `reference` orphans indexed,
|
||||||
|
all 39 `project` + 1 untyped archived.
|
||||||
|
|
||||||
|
## 6. Staleness diagnosis — **diagnosed, not fixed** (R-230(a))
|
||||||
|
|
||||||
|
- **21 lines** carry component version literals
|
||||||
|
- **5 lines** carry bare host addresses `nodes.md` owns
|
||||||
|
- **3** expired temporal statements; **2** undated open items
|
||||||
|
|
||||||
|
**The structural finding, not the counts:** `felhom-agent/CLAUDE.md` had its expired
|
||||||
|
`TEMPORARY … until ~2026-08-02` block deleted in part 1 and the gate now fails any such block — while
|
||||||
|
`MEMORY.md` still asserts *"demo boxes REMOTE till ~08-02"*. The contradiction was **moved, not
|
||||||
|
resolved**: the hand-written half is clean, the auto-written half — which is larger and loads every
|
||||||
|
session — still states the retired fact.
|
||||||
|
|
||||||
|
## 7. Secrets scan
|
||||||
|
|
||||||
|
535 keyword-matching lines across 104 files; **1** `key: value`-shaped hit, which is prose; **0**
|
||||||
|
private-key blocks. **No credential values.** Nothing from the store was committed regardless.
|
||||||
|
|
||||||
|
## 8. Gates
|
||||||
|
|
||||||
|
`repo_gates.py --fast` → **6 gates, all OK, rc=0**. Test suite **20 → 39 assertions, 0 failures**.
|
||||||
|
|
||||||
|
Red-proof against the **real** store, not a fixture — ceiling 200 → 100:
|
||||||
|
|
||||||
|
```
|
||||||
|
memory index : 150 lines (ceiling 100), 17977 bytes (ceiling 25600)
|
||||||
|
instructions_gate: 1 FAILURE(S)
|
||||||
|
- /mnt/5_hdd/felhom.eu/git/.claude-memory/MEMORY.md: 150 lines, ceiling 100. Content past the
|
||||||
|
auto-memory limit is DROPPED WITH NO ERROR — a truncated index is a silent failure with no
|
||||||
|
observable. …
|
||||||
|
```
|
||||||
|
|
||||||
|
Ceiling restored; gate `rc=0` and suite 39/0 again.
|
||||||
|
|
||||||
|
## 9. Installer
|
||||||
|
|
||||||
|
Idempotency: run 2 reported *"nothing to do"* and `settings.json` sha was **identical** before and
|
||||||
|
after — the claim is "changed nothing the second time", not "ran twice without erroring".
|
||||||
|
|
||||||
|
Merge safety: `sha256` of `settings.json` **minus `.hooks`** was `f1775e94aa9d1ca9` before and after
|
||||||
|
the write; all 7 top-level keys, 30 permission entries, 3 plugins, `effortLevel`, `tui` and
|
||||||
|
`additionalDirectories` survived byte-identically.
|
||||||
|
|
||||||
|
## 10. Backup coverage — positive observable
|
||||||
|
|
||||||
|
From the journal of the real unit:
|
||||||
|
|
||||||
|
```
|
||||||
|
Including Claude auto-memory store: /mnt/5_hdd/felhom.eu/git/.claude-memory
|
||||||
|
start backup on [/mnt/4_hdd/data /mnt/5_hdd/felhom.eu/git/.claude-memory]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Three caveats that make this weaker than "backed up" sounds:** the destination is on the **same
|
||||||
|
physical disk** as the store; the DooPlex backup set has **no off-site leg**
|
||||||
|
(`sync-hetzner-backups.sh` is jarrs.eu and pulls the other way); and `/opt/backup/scripts/` is itself
|
||||||
|
**unversioned host state** (R-231). One-time cost: adding a path invalidated restic's parent match,
|
||||||
|
so the first run re-reads 405 GiB — dedup keeps storage flat and later runs are incremental again.
|
||||||
|
|
||||||
|
## 11. Rules report — first run
|
||||||
|
|
||||||
|
8 events; `session_start` 4, `path_glob_match` 3, `nested_traversal` 1. **6 of 9 rule files had never
|
||||||
|
fired.** Not a defect list: the log only covers since the hook was armed, and
|
||||||
|
`felhom-controller/gates.md` proves the distortion — it fired at 08:11 the same day, before
|
||||||
|
installation, and reads as silent.
|
||||||
|
|
||||||
|
The hook now **self-rotates at 5 MB**, one generation.
|
||||||
|
|
||||||
|
## 12. context7 — nothing written
|
||||||
|
|
||||||
|
`plugin:context7:context7` is **`✘ failed`** (ENOENT on `npx -y @upstash/context7-mcp`); `/mcp` lists
|
||||||
|
only the six Google auth stubs. Per §5 that means **write nothing**. The Go code is not the use case
|
||||||
|
either: one direct dependency in `felhom-agent`, and the hub and controller are stdlib-only by
|
||||||
|
standing rule.
|
||||||
|
|
||||||
|
## 13. Registers
|
||||||
|
|
||||||
|
- **R-229** re-scoped — legs (a) and (c) **CLOSED**; (b) remains; (d) moved to R-230.
|
||||||
|
- **R-230** opened — the auto-written-staleness ruling, the symlink decision, the spec-as-failing-test pilot.
|
||||||
|
- **R-231** opened — `/opt/backup/scripts/` is unversioned host state.
|
||||||
|
- **S-36** added to `CONTEXT.md` standing rulings.
|
||||||
|
|
||||||
|
## 14. Observations — not acted on
|
||||||
|
|
||||||
|
`target-selection.md`'s t740 error; `felhom-agent/CLAUDE.md` at 173 effective lines; the
|
||||||
|
`/context` Messages-accounting inference (§A of the ledger — reasoned, not measured); `MEMORY.md`'s
|
||||||
|
header still reading "felhom-controller Project Memory" though it indexes all four repos.
|
||||||
@@ -0,0 +1,240 @@
|
|||||||
|
# LEDGER — instruction trim, part 2 (`felhom.eu`, the memory index, the versioned workspace)
|
||||||
|
|
||||||
|
Companion to `LEDGER-instruction-trim-2026-08-06.md`, which this closes legs (a) and (c) of. Same
|
||||||
|
per-block format; destinations are `rule-file`, `html-comment`, `already-in:<path>`,
|
||||||
|
`deleted-derivable`, `deleted-stale`, `kept-in-core`.
|
||||||
|
|
||||||
|
**Baselines (verified against live Gitea, all clean, all `HEAD == origin/main`):** `felhom.eu`
|
||||||
|
`c21bcf84f709`, `felhom-controller` `7db42c5fec3b`, `felhom-agent` `062a7027abff`.
|
||||||
|
|
||||||
|
**Concurrency:** a second Claude Code session (`f192bf03`, started 10:05) was live in these repos
|
||||||
|
throughout, doing `qm`/`ssh demo-hp` console work. Managed rather than waited out: clean-tree and
|
||||||
|
`origin/main` re-checked immediately before every commit, explicit-path staging only, `pull --rebase`
|
||||||
|
before every push, and the memory store snapshotted before edit and re-diffed after. Per this repo's
|
||||||
|
own rule for parallel sessions, this run wrote `REPORT-instruction-trim-part2-2026-08-06.md` and did
|
||||||
|
**not** touch the shared `REPORT.md`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## A. Mechanism verification — done before relying on any of it
|
||||||
|
|
||||||
|
| Claim | Verdict | Evidence |
|
||||||
|
|---|---|---|
|
||||||
|
| `.claude/rules/*.md` + `paths:` loads **only** on a matching file read | **confirmed, both directions** | hook log, two fresh sessions: reading `website/index.html` → `website.md path_glob_match`, **no** `hub.md`; reading `hub/internal/api/handler.go` → `hub.md path_glob_match`, **no** `website.md` |
|
||||||
|
| Creating `.claude/rules/` **mid-session** arms it | **DISPROVEN** | after writing the four rule files, in-session reads of `website/index.html` produced **no hook line at all**. A directory whose instructions were already seeded is not re-scanned — same class as the settings-watcher caveat. This is why the proof above needed `claude -p` |
|
||||||
|
| On-demand rules are accounted under `Messages` in `/context` | **still inference, not measured** | two `/context all` readings: Memory-files stayed at exactly 11.5k across three rule loads while Messages grew 8 → 105.7k. That shows rules are **not** counted as memory files; it does not show where they *are*. Recorded as reasoned, per spec §3 |
|
||||||
|
|
||||||
|
The second row is the load-bearing one and it is a **negative result about the instrument**: a rule
|
||||||
|
file can be correct, pass every gate, and reach the model never, purely because of when it was
|
||||||
|
created. Nothing in the gate or the frontmatter would have shown that.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## B. `felhom.eu/CLAUDE.md` — 227 → 115 effective lines
|
||||||
|
|
||||||
|
| Heading / first words | Class | Destination | Reason |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `## The Felhom system` three-component model (7 ln) | duplicated | `already-in:CLAUDE.md` (workspace root) | the root always loads and survives `/compact`; restating it here is a second place to edit |
|
||||||
|
| `## Hub — architecture` ingest paragraph (11 ln) | path-bound | `rule-file:hub.md` | only matters when `hub/` is being edited; the frozen-contract warning went with it |
|
||||||
|
| `## Hub stack — the two constraints` (7 ln) | path-bound | `rule-file:hub.md` | same trigger |
|
||||||
|
| `## Build & deploy — Hub (GitOps)` (25 ln) | path-bound | `rule-file:hub.md` | the clean-tree gate inside it was `already-in:CLAUDE.md` (workspace root) and dropped here |
|
||||||
|
| `## Key patterns` (8 ln) | path-bound | `rule-file:hub.md` | status logic, `parseSQLiteTime`, auto-refresh — all hub internals |
|
||||||
|
| seam-wiring / template-gates rule (4 ln) | path-bound | `rule-file:hub.md` | it is about `html/template` gates, which exist only in `hub/` |
|
||||||
|
| health-check block-I/O rule (5 ln) | path-bound | `rule-file:hub.md` | deliberately **duplicated** into three repos' rule files — a pointer to a sibling repo's rule does not load |
|
||||||
|
| logging levels/phrasing (3 ln) | path-bound | `rule-file:hub.md` | applies when writing Go |
|
||||||
|
| website deploy + BOM + emoji + cache-busting (10 ln) | path-bound | `rule-file:website.md` | |
|
||||||
|
| ArgoCD / deliberate sync / `secretKeyRef` (12 ln) | path-bound | `rule-file:manifests.md` | |
|
||||||
|
| doc routing, one-home-per-fact, N.5 coupling (part of the checklist) | path-bound | `rule-file:docs.md` | |
|
||||||
|
| `## Project overview` per-directory list (14 ln) | derivable | compressed to 6 lines + retrieval map | the tree says what the directories are; what it cannot say is which one is *authoritative for the whole system* — that was kept |
|
||||||
|
| artifact taxonomy, `TASK.md`/`RUNBOOK-*.md` (10 ln) | duplicated | `already-in:CLAUDE.md` (workspace root) | the root defines the taxonomy for all four repos |
|
||||||
|
| browser-automation / `claude-in-chrome` block (6 ln) | duplicated | `already-in:CLAUDE.md` (workspace root) | root's "Live validation — no browser here" |
|
||||||
|
| `## Environment & access` DooPlex + host pointers (22 ln) | duplicated | `already-in:CLAUDE.md` (workspace root) + `nodes.md` | replaced by two rows in the retrieval map |
|
||||||
|
| Legacy-Windows HTML comment (5 ln) | archival | `deleted-derivable` | the root carries the fuller version in its own comment |
|
||||||
|
| `## Build & deploy — Website/Manifests` R-110 installer block (17 ln) | **fence** | **`kept-in-core`, compressed to 6** | see §C |
|
||||||
|
| `## Gates — ONE entry point` (25 ln) | entry point | `kept-in-core`, compressed | §6 names it as core; the R-29 narrative stayed an HTML comment (free) |
|
||||||
|
| end-of-session checklist (18 ln) | **register discipline** | **`kept-in-core`** | see §C |
|
||||||
|
|
||||||
|
**Kept deliberately:** the gate entry point, the end-of-session checklist, register discipline, the
|
||||||
|
R-110 fence, the `git add -A` prohibition, the parallel-session `REPORT.md` rule, the `go test -run`
|
||||||
|
false-green rule, and "ask first — don't guess".
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## C. Two placements that deviate from the spec's §6 sketch — and why
|
||||||
|
|
||||||
|
The spec fixed the rule-file names and globs and left line-by-line placement to the implementer.
|
||||||
|
Two blocks were **kept in the core** that §6's sketch would have relocated. Both would otherwise have
|
||||||
|
rebuilt the exact failure class they exist to prevent.
|
||||||
|
|
||||||
|
| Block | §6 would put it in | Kept in core because |
|
||||||
|
|---|---|---|
|
||||||
|
| **Register discipline** — `OPEN-ITEMS.md` first, `STATUS.md` is a view, nothing exists only in a report | `docs.md` (`paths: documentation/**`) | it applies at the end of **every** session that ships, breaks or decides something. Behind that glob it is invisible in precisely the code-shipping sessions where findings get lost — which is how R-153/154/155 and R-156/157 were minted and lost. A rule that loads only when you are already writing documentation cannot remind you to write documentation |
|
||||||
|
| **The R-110 installer fence** — pushing `felhom-host-install.sh` publishes nothing; publishing is a tag move | `website.md` (`paths: website/**`) | its trigger is editing `scripts/felhom-host-install.sh`, which **none of the four fixed globs matches**. Behind `website/**` it would never load for the one artifact that runs **as root on a virgin box**. Compressed to its actionable clauses rather than relocated |
|
||||||
|
|
||||||
|
`docs.md` states the first of these explicitly, so a reader who expects the rule there is told where
|
||||||
|
it went and why.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## D. The memory store — reconciled, nothing deleted
|
||||||
|
|
||||||
|
**Backup first (ruling 2):** `/mnt/5_hdd/felhom.eu/backups/claude-memory-20260806-103418`, 158 files,
|
||||||
|
taken before any edit.
|
||||||
|
|
||||||
|
| Measure | Before | After |
|
||||||
|
|---|---|---|
|
||||||
|
| `.md` files total | 158 | **158 — equal, zero deletions** |
|
||||||
|
| top-level topic files | 157 | 117 |
|
||||||
|
| `archive/` | — | 40 |
|
||||||
|
| indexed | 113 | **117 (all of them)** |
|
||||||
|
| orphaned | **44** | **0** |
|
||||||
|
| dangling references | 0 | 0 |
|
||||||
|
| `MEMORY.md` | 145 ln / 17,688 B | **150 ln / 17,977 B** |
|
||||||
|
|
||||||
|
**The discriminator was the store's own schema, not taste.** All 4 `reference`-type orphans are
|
||||||
|
durable pointers and were **indexed**; all 39 `project`-type orphans plus 1 untyped are dated episode
|
||||||
|
records — "SHIPPED", "spike GO", "FIXED in vX.Y.Z" — whose conclusions already live in CHANGELOGs and
|
||||||
|
`documentation/audits/`, and were **archived**. Each of the four also justifies indexing on content,
|
||||||
|
not merely type; `hetzner-storagebox-api.md` carries an **open security constraint** (the
|
||||||
|
project-scoped Hetzner token can delete ep0 — move customer boxes to a dedicated project before the
|
||||||
|
hub holds a write token).
|
||||||
|
|
||||||
|
**Detail moved, not dropped.** Seven index lines were shortened; for each, the removed detail was
|
||||||
|
grep-verified present in the topic file first. One check came back **negative**
|
||||||
|
(`controller-internals.md` did not contain the module path) — the line was trimmed anyway, caught on
|
||||||
|
review, and the detail written into `controller-internals.md`. Recorded because the verification step
|
||||||
|
is what caught it.
|
||||||
|
|
||||||
|
**Headroom:** 50 lines and 7,622 bytes (29%) against the 200-line / 25 KB limits.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## E. Staleness diagnosis (Part 2.4) — **diagnosed, deliberately not fixed**
|
||||||
|
|
||||||
|
A ruling is owed before anyone edits Claude's own notes at scale. Filed as **R-230(a)**.
|
||||||
|
|
||||||
|
| Class | Count | Sharpest instance |
|
||||||
|
|---|---|---|
|
||||||
|
| component version literals | **21 lines** | `ctrl 0.162.0`, `hub 0.64/0.65`, `installer 1.15.0` — the exact literal class the gate now bans in every hand-written instruction file |
|
||||||
|
| bare host addresses | **5 lines** | `100.70.170.35`, `167.233.158.164`, `10.77.0.1` — `nodes.md` is supposed to be the single home |
|
||||||
|
| expired temporal statements | 3 | **`"demo boxes REMOTE till ~08-02"`** |
|
||||||
|
| undated open items | 2 | `RENOVATE_GITHUB_COM_TOKEN ROTATION DUE` |
|
||||||
|
|
||||||
|
**The finding that matters is structural, not the counts.** `felhom-agent/CLAUDE.md` had its expired
|
||||||
|
`TEMPORARY — felhom-pve is at a remote site (until ~2026-08-02)` block deleted in part 1, and the gate
|
||||||
|
now fails any such block. The *same claim* is still live in `MEMORY.md` — which loads in every
|
||||||
|
session, is larger than the file the rule was written for, and is the one file whose content the gate
|
||||||
|
does not police. The contradiction was moved, not resolved: the hand-written half is clean and the
|
||||||
|
auto-written half still asserts the retired fact.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## F. The gate — check 6, and why its three outcomes differ
|
||||||
|
|
||||||
|
| Condition | Outcome | Reason |
|
||||||
|
|---|---|---|
|
||||||
|
| index over 200 lines or 25 KB | **FAIL** | auto-memory drops content past the limit with **no error** — silent truncation, no observable |
|
||||||
|
| top-level topic file unreferenced | **WARN** | the store is outside git and changes between sessions; a failure blocks pushes for something no commit can fix |
|
||||||
|
| store absent | **PASS, printing the reason** | machine-local by design; a clone elsewhere legitimately has none |
|
||||||
|
|
||||||
|
The third is a deliberate exception to check 1's *"a missing input is a FAILURE, never a skip"*. The
|
||||||
|
printed reason is the only thing that made it safe to grant, and the test asserts on **that text**,
|
||||||
|
not on `rc == 0` — a pass with no reason is indistinguishable from a gate that stopped running.
|
||||||
|
|
||||||
|
Two load-bearing negatives: a file reachable only via `[[wikilink]]` is **indexed, not orphaned**
|
||||||
|
(flagging it would push someone to add a duplicate row), and `archive/` contents are never orphans.
|
||||||
|
|
||||||
|
**Suite 20 → 39 assertions, 0 failures.** Red-proof against the **real** store, not a fixture:
|
||||||
|
ceiling 200 → 100, gate went red naming the real file and count
|
||||||
|
(`.claude-memory/MEMORY.md: 150 lines, ceiling 100`), restored, green again.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## G. The versioned workspace
|
||||||
|
|
||||||
|
**Canonical path decision (§4.3):** `documentation/runbooks/workspace-CLAUDE.md` **stays** the
|
||||||
|
source. `workspace/` carries only the hook fragment. A `workspace/CLAUDE.md` would be a *third* copy
|
||||||
|
of a file whose whole problem is that copies drift, and check 5 already enforces byte-identity.
|
||||||
|
|
||||||
|
**Symlink recommendation (§4.3): recommended, not done.** Making the live root file a symlink to the
|
||||||
|
versioned copy removes the divergence class outright, and Claude Code follows symlinks (the four
|
||||||
|
skills already are). Not done here because it changes what check 5 asserts — identity of two files
|
||||||
|
becomes correctness of one link — which deserves its own reviewable change. **R-230(b)**.
|
||||||
|
|
||||||
|
**Proofs.** Merge preserved all 7 top-level `settings.json` keys byte-identically (`sha256` of the
|
||||||
|
file minus `.hooks` unchanged across the write). Run 2 wrote nothing — *idempotent* means "changed
|
||||||
|
nothing the second time", not "ran twice without erroring", and only the second is evidence.
|
||||||
|
|
||||||
|
`install_workspace.py` **refuses** an unparseable `settings.json` rather than overwriting it: a
|
||||||
|
malformed settings file silently disables every setting in it, and overwriting would destroy whatever
|
||||||
|
was mid-fix.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## H. Backup coverage (§4.4) — and what it does *not* buy
|
||||||
|
|
||||||
|
Added to `dooplex-backup.service`'s User Data component via `CLAUDE_MEMORY_DIR`. **Positive
|
||||||
|
observable**, from the journal of the real unit:
|
||||||
|
|
||||||
|
```
|
||||||
|
Including Claude auto-memory store: /mnt/5_hdd/felhom.eu/git/.claude-memory
|
||||||
|
start backup on [/mnt/4_hdd/data /mnt/5_hdd/felhom.eu/git/.claude-memory]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Three caveats, each of which makes this weaker than "backed up" sounds:**
|
||||||
|
|
||||||
|
1. The destination `/mnt/5_hdd/backup` is on the **same physical disk** (`sda1`) as the store. This
|
||||||
|
protects against deletion, **not** against disk loss.
|
||||||
|
2. The DooPlex backup set has **no off-site leg**. `sync-hetzner-backups.sh` is a *jarrs.eu* job that
|
||||||
|
pulls **from** Hetzner **to** DooPlex — the opposite direction.
|
||||||
|
3. `/opt/backup/scripts/` is itself **unversioned host state** (**R-231**) — the change exists only on
|
||||||
|
the box, transcribed into `workspace/README.md` so it is at least recorded.
|
||||||
|
|
||||||
|
One-time cost, so it is not later read as a fault: adding a path invalidates restic's parent-snapshot
|
||||||
|
match, so the first run logs `no parent snapshot found` and re-reads all 405 GiB. Dedup keeps storage
|
||||||
|
flat; later runs are incremental again.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## I. Secrets scan of the memory store (§9.3)
|
||||||
|
|
||||||
|
Pattern set: `token|password|passwd|secret|api[_-]?key|private[_-]?key|BEGIN .*PRIVATE|bearer|credential`,
|
||||||
|
case-insensitive, over all 158 files.
|
||||||
|
|
||||||
|
| Measure | Count |
|
||||||
|
|---|---|
|
||||||
|
| lines matching the keyword set | 535 across 104 files |
|
||||||
|
| matching a `key: value` shape with a ≥16-char value | **1** — and it is prose (`scoped token: provision-into-pool, …`) |
|
||||||
|
| private-key blocks | **0** |
|
||||||
|
|
||||||
|
**No credential values.** Nothing from the store was committed regardless (ruling 2); the scan is the
|
||||||
|
evidence the later decision needs, not a licence.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## J. context7 (Part 5) — nothing written, by the rule
|
||||||
|
|
||||||
|
The operator's `/mcp` and `/plugin` output: `plugin:context7:context7` is **`✘ failed`** (ENOENT on
|
||||||
|
`npx -y @upstash/context7-mcp`), and `/mcp` lists only the six Google auth stubs as available. Per
|
||||||
|
§5, **write nothing** — instructions telling Claude to use a tool that does not exist are worse than
|
||||||
|
none. Not written: the `felhom-app-catalog` SKILL.md line and the infrastructure rule-file line.
|
||||||
|
|
||||||
|
**Why the Go code is not the use case either, recorded as §5 asks:** one direct dependency in
|
||||||
|
`felhom-agent` (`golang.org/x/crypto`), and the hub and controller are stdlib-only by standing rule.
|
||||||
|
There is no third-party API surface here that a docs-retrieval tool would help with; the catalog's
|
||||||
|
upstream images are the only plausible consumer, and there `docker inspect` still decides.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## K. Observations — noticed, not acted on
|
||||||
|
|
||||||
|
1. **`target-selection.md` t740 error** — still present, carried from part 1 §L.1.
|
||||||
|
2. **`felhom-agent/CLAUDE.md` at 173 effective lines** — passing with the least headroom; R-229(b).
|
||||||
|
3. **The `/context` Messages-accounting inference** — see §A row 3. Not built on.
|
||||||
|
4. **`rules_report.py` first run: 6 of 9 rule files had never fired.** Not a defect list — the log
|
||||||
|
only covers since the hook was armed, and `felhom-controller/gates.md` is proof of the
|
||||||
|
distortion: it fired at 08:11 the same day, before installation, and reads as silent.
|
||||||
|
5. **The other session's read of `OPEN-ITEMS.md` fired `docs.md`** — independent third-party
|
||||||
|
confirmation the new rules are live, from a session that knew nothing about them.
|
||||||
|
6. **`MEMORY.md`'s header still says "felhom-controller Project Memory"** though it indexes all four
|
||||||
|
repos plus the homelab. Cosmetic; left for the R-230(a) ruling rather than edited.
|
||||||
@@ -113,7 +113,9 @@ the fault was real. Full observables: `tests/campaign11-evidence-2026-08-05/jour
|
|||||||
|
|
||||||
| ID | What | State |
|
| 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. **Deferred, and why:** (a) **`felhom.eu/CLAUDE.md` is at 227 effective lines, over the 200 ceiling** — reducing it needs the core+rules restructure that the task spec explicitly forbade for reviewability, so it is not gated today; when it lands, also register `instructions_gate` in `scripts/repo_gates.py`. (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) **The auto-memory decision** — `MEMORY.md` was measured only (146 lines / 17.3 KB, **within** both the 200-line and 25 KB limits, nothing truncated), but the index references 113 memory files while **157 exist on disk**, so 44 are unindexed. (d) **The spec-as-failing-test pilot**, approved in principle and not started. Full accounting: `audits/LEDGER-instruction-trim-2026-08-06.md` | **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) **`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-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:**
|
**Recorded against existing rows by Phase 2:**
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user