New shared scripts/instructions_gate.py, registered in controller_gates.py and agent_gates.py, never copied into a sibling repo (the reuse_refs_check.py precedent). 20 fixture tests, all asserting the effect: exit code AND that the message names the file and the reason. It is a consistency gate, not a budget gate, and the failure message says so. A /context reading measured the instruction files at 15k tokens against 869k free in a 1M window -- space is not the constraint, and a future reader must not re-derive the wrong reason. The 200-line ceiling is adherence guidance; a file nobody can hold in their head is where contradictions hide, and five were found here. Checks run against effective text (HTML comments stripped, because they are stripped before injection): the line ceiling; every .claude/rules/*.md declares paths: or an explicit unconditional: true; no component version literal; no TEMPORARY block carrying a past date; and the workspace-root CLAUDE.md is byte-identical to its versioned copy -- the live file sits outside any git repo, so that copy is its only version-controlled record. Two traps recorded so they are not reintroduced: a bare \d+\.\d+\.\d+ matches the first three octets of every IPv4 (the gate excludes dotted quads, or it fails on 192.168.0.180 in the agent's own file); and unconditional: true is NOT a Claude Code feature but this project's own marker. Workspace-root CLAUDE.md 208 -> 182 lines (142 effective), copy kept identical. The nine-instance invariant table moved into the felhom-testing skill, which triggers when writing or reviewing a test; all three directive bullets stayed in the core. felhom.eu/CLAUDE.md got surgical corrections only and is knowingly still over the ceiling at 227 effective lines -- closing it needs the restructure R-229 defers, said plainly rather than quietly absorbed. CONTEXT.md gains standing ruling S-35. OPEN-ITEMS.md gains R-229. Docs only -- no Go, no version bump, nothing built or deployed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JJc8sAGRWmavP3rMtdpkr2
10 KiB
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 newinstructions.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
GATESlist 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
target-selection.md's known t740 off-site-tier error is still there (out of scope).- The root
CLAUDE.mdcould 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. - 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 on192.168.0.180in the agent's own file. - 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.
demo-hpalso hosts VM321 c11-appliance, running — seen in the sameqm list, not mentioned innodes.md. Not investigated.- 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.