# 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]
```

**Completed and verified in the repository.** Snapshot `b587f775`, 58,158 files / 405.865 GiB in
27:56, `994.132 MiB added (77.440 MiB stored)` — 77 MiB stored for a 405 GiB re-read confirms the
one-time cost was I/O, not storage. Contents: **161** `.claude-memory` entries — **118/118** top-level
`.md` and **40/40** under `archive/`. The 03:07 snapshot holds `/mnt/4_hdd/data` only, so the two
snapshots are a clean before/after in one listing.

**A false alarm I raised against my own instrument.** The first check used
`restic ls <snapshot> <path>` and reported **0 of 40** archive files — which read exactly like 40
memories silently unprotected. It was wrong: restic 0.18.0's path filter lists entries *at* that path
and does not recurse. The unfiltered listing shows all 40. Recorded because it is this project's own
rule — *an instrument that can drop results silently is not a measurement* — biting a measurement made
**to verify a safety property**.

**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).

## 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.

## 12b. A stale claim the checklist caught — corrected in all four repos

Confirming this session's own push by run ID (checklist's last item) surfaced that **four instruction
files claimed CI was still owed** (R-168) — and **R-168 was CLOSED on 2026-08-02**. CI exists, runs
`repo_gates.py --fast` on every push, and emails on failure; this session's commits produced runs
117 (`success`) and 118.

One of the four was `felhom.eu/CLAUDE.md`, where **this session carried the stale sentence forward
through the trim**. `felhom-agent/CLAUDE.md` **contradicted itself** — its release section already
said R-168 mails the failure. Corrected in all four, pushed.

**Two lessons about this task, not about CI:** a trim is a *volume* operation and carries stale
content forward unless each claim is re-checked; and the gate cannot catch this class — "this
register item is closed" is not mechanically checkable from the instruction file. The thing that
caught it was the checklist item demanding a **run ID** rather than a memory.

## 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.
