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