92a076c239
gates / gates (push) Successful in 8s
58,158 files / 405.865 GiB in 27:56; 994 MiB added, 77 MiB stored -- the one-time full re-read cost I/O, not storage, as predicted. Also records a false alarm I raised against my own instrument: `restic ls <snapshot> <path>` reported 0 of 40 archived memories because restic 0.18.0's path filter does not recurse into subdirectories. The unfiltered listing shows all 40. An instrument that can drop results silently is not a measurement -- and this one was measuring a safety property.
183 lines
9.0 KiB
Markdown
183 lines
9.0 KiB
Markdown
# 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.
|