# REPORT — instruction-file rightsizing (core + path-scoped rules), 2026-08-06 **Docs and gate only. No Go changed, no version bumped, no image built, nothing deployed, no customer machine touched.** One read-only command ran against live hardware (`ssh demo-hp "qm list"`), permitted by the task for exactly one purpose. **The headline is the contradiction count, not a token saving.** A `/context` reading measured the instruction files at **15k tokens against 869k free** in a 1M window. Space was never the constraint; five stale or conflicting facts were. --- ## 1. Baselines | Repo | `main` @ start | Clean | Note | |---|---|---|---| | felhom-controller | `a62bb3874b25` | yes | matched spec | | felhom-agent | `a2e914f683bd` | yes | matched spec | | felhom.eu | `d30c2a51ed2a` | yes | matched spec | | app-catalog-felhom.eu | `ee2c8102016a` | yes | **untouched** — 79 lines, already the target shape, cited as the model | **One correction:** the workspace-root `CLAUDE.md` measured **16,642 B / 208 lines**, not the spec's 15,431 / 207 — it was edited at 08:32 that morning, after the spec was written. The other five files matched exactly. Also structural: `/mnt/5_hdd/felhom.eu/git` **is not a git repository**, so the live root `CLAUDE.md` is untracked; only its copy under `felhom.eu/` is version-controlled. ## 2. Contradictions: 5 before → 0 after | # | What conflicted | Resolution | |---|---|---| | 1 | agent said demo-hp hosts drill VM `300`; controller said none was provisioned | **Measured live:** `qm list` → `300 drill-r50 stopped`. **felhom-agent was right.** `nodes.md:96` already said so correctly — both `CLAUDE.md` blocks became pointers, no new text needed | | 2 | agent's `TEMPORARY` block (expired 2026-08-02) said felhom-pve was remote; controller said it was back on the LAN | both deleted; the audit holds the record. The gate now fails any past-dated TEMPORARY block | | 3 | controller pinned `agent 0.93.0`, against the root file's own no-versions rule | every version literal removed from effective text in all four files | | 4 | controller gave `demo-felhom` as the LAN *fallback* address as if it were the route | host tables removed from all three `CLAUDE.md` files → `nodes.md` | | 5 | root file said memory held `(119 files)`; it holds 157 + the index | parenthetical deleted, not corrected — derivable, and it would go stale again | The sweep found **none beyond the five**. ## 3. Before / after (effective = HTML comments stripped, i.e. what the model receives) | File | before | after raw | after effective | gate | |---|---|---|---|---| | workspace-root `CLAUDE.md` | 208 ln / 16,642 B | 182 / 11,280 | **142 / 8,105** | pass | | versioned copy | 208 / 16,642 | 182 / 11,280 | **142 / 8,105** | pass — `cmp` identical | | `felhom-controller/CLAUDE.md` | 215 / 14,775 | 110 / 6,341 | **92 / 4,881** | pass | | `felhom-agent/CLAUDE.md` | 216 / 15,554 | 205 / 13,619 | **173 / 11,491** | pass | | `felhom.eu/CLAUDE.md` | 241 / 17,471 | 235 / 17,003 | **227 / 16,286** | **over — deferred** | **A controller session's instruction load: 31,417 → 12,986 effective bytes (−59%).** Figures are bytes, deliberately. The measured `/context` ratio shows a bytes/4 token estimate understates the true cost by **1.59–1.90×**, so byte counts are the honest unit here. **`felhom.eu/CLAUDE.md` is knowingly left over the ceiling.** Getting it under 200 needs the core+rules restructure the spec explicitly forbade for reviewability, and the gate is registered only in the controller and agent runners. Deferred as **R-229**, said plainly rather than quietly absorbed. ## 4. Files created / modified **Created:** `felhom-controller/.claude/rules/{gates,ui-hungarian,backup-paths,agent-coupling}.md` · `felhom-agent/.claude/rules/health-checks.md` · `felhom.eu/scripts/instructions_gate.py` · `felhom.eu/scripts/test_instructions_gate.py` · `felhom.eu/documentation/audits/LEDGER-instruction-trim-2026-08-06.md` **Modified:** the four `CLAUDE.md` files + the versioned copy · `controller_gates.py` · `agent_gates.py` · `skills/felhom-testing/SKILL.md` (gained the nine-instance table) · three `CHANGELOG.md` · `CONTEXT.md` (S-35) · `OPEN-ITEMS.md` (R-229) ## 5. Mechanism verification — done before relying on it The whole design rests on three claims. Two were confirmed, one was **false**: | Claim | Verdict | |---|---| | `.claude/rules/*.md` + `paths:` loads only on a matching file read | confirmed against the installed 2.1.222 build | | HTML comments are stripped before injection | **confirmed empirically** — control (both markers plain → both reported) vs treatment (one commented → not reported, twice) | | `unconditional: true` frontmatter | **NOT a product feature.** It is this project's own marker; the gate's docstring says so, so nobody hunts for it in the docs | The HTML-comment test **failed its first red-proof** — an open-ended prompt returned one marker in both arms, so the instrument was dropping a result silently. Rewritten as forced yes/no questions with a both-plain control, it discriminated. Worth recording: this project's own rule ("an instrument that can drop results silently is not a measurement") caught a measurement made *for* this task. ## 6. Amnesty list (the list to review) Deliberately short — **one** item qualified: - Three generic code-quality bullets in `felhom.eu/CLAUDE.md` ("always double-check generated code", "handle edge cases without overcomplicating", "add debug capabilities"). No recorded production violation, recoverable worst case, and duplicated in the workspace root. **"ask first — don't guess" was kept** — that one is a real behavioural directive. **Nothing on the protected list was touched.** The nine-row invariant table, "presence is not success", the four R-96 rules, the F9 fence, secrets handling, trunk-based, the DooPlex protections, the destructive-target fences with their permitted targets, and the clean-tree gate all survive — moved or compressed, never dropped, each compressed fence keeping its target and its reason. ## 7. Gate results - `controller_gates.py --fast` → **all 9 gates OK**, including the new `instructions`. - `agent_gates.py --fast` → **both gates OK**. - `test_instructions_gate.py` → **20 passed, 0 failed**. - `go build ./... && go vet ./... && go test ./...` → **green in all three modules** (`felhom-controller/controller`, `felhom.eu/hub`, `felhom-agent`) — and unchanged, as required: no Go was touched. - **Registration seam asserted by parsing each runner's `GATES` list from the AST**, not by grepping the source — a commented-out registration would not satisfy it. **Red-proof** (ceiling temporarily 100, real trimmed files): ``` CLAUDE.md effective lines : 92 (ceiling 100) [felhom-controller] CLAUDE.md effective lines : 173 (ceiling 100) [felhom-agent] instructions_gate: 1 FAILURE(S) - .../felhom-agent/CLAUDE.md: 173 effective lines, ceiling 100. This is an ADHERENCE limit, not a space limit — long instruction files get followed less reliably and hide contradictions. ``` It discriminated: agent failed and was named, controller still passed. Threshold restored, suite re-run green. ## 8. `MEMORY.md` — measured only, unchanged 146 lines / 17,688 B (17.3 KB) against the 200-line / 25 KB limits: **within both, nothing truncated.** The expected finding is absent; a different one is present — the index references **113** memory files while **157** exist on disk, and **zero** referenced files are missing. So 44 memory files are unindexed. Recorded, not acted on (the ruling deferred this). ## 9. Delegation **No subagent was used.** All search, inventory and verification was done in-session. Nothing was delegated, so nothing needs the read-only caveat. ## 10. Which files actually load Settled from the supplied `/context`, not assumed: at the workspace root exactly **two** memory files load — the root `CLAUDE.md` (6.6k tokens) and `MEMORY.md` (8.4k). Per-repo `CLAUDE.md` files are **not** loaded at launch; they load on demand when a file in that directory is read. This is why the irreversible fences were kept in the root file. **Still outstanding (HUMAN):** `/context all` from **inside `felhom-controller`** after this trim, to give the measured after-figure and confirm which rule files a repo session actually pulls in. Claude Code cannot invoke a slash command on itself. The after-figures above are byte counts from disk, not measured tokens, and are labelled as such. ## 11. Register `OPEN-ITEMS.md` row taken: **R-229** (READY, owner Viktor) — covering `felhom.eu/CLAUDE.md`'s restructure, `felhom-agent`'s remaining headroom, the auto-memory decision, and the spec-as-failing-test pilot. ## 12. Observations — not acted on 1. `target-selection.md`'s known t740 off-site-tier error is still there (out of scope). 2. **The root `CLAUDE.md` could be a symlink** to its versioned copy, removing the divergence class entirely — same filesystem, and Claude Code reads through symlinks (the four skills already are). Not done, per the spec. If adopted, the gate's copy-identity check should become a symlink-target check. 3. **A blanket version-literal ban has a false-positive class the spec did not anticipate:** a bare `\d+\.\d+\.\d+` matches the first three octets of every IPv4. Without the dotted-quad exclusion the gate fails on `192.168.0.180` in the agent's own file. 4. The spec calls the invariant table "the eight-invariant table"; **it has nine rows** and its own text says "Nine instances". Flagged so the protected list is not later applied to eight of nine. 5. `demo-hp` also hosts VM `321 c11-appliance`, **running** — seen in the same `qm list`, not mentioned in `nodes.md`. Not investigated. 6. Two unused Claude Code plugins (`typescript-lsp`, `context7`) and a broad user-scope allowlist (`Bash(python3:*)`, `Bash(curl:*)`, `Bash(scp:*)` — standing arbitrary execution and network egress in every project) were found by the earlier setup audit. **They live in `~/.claude/settings.json`, not in any repo, and are deliberately out of scope.** Recorded only.