R-265 CI timeout + golden 0.210.0 baked; R-221/R-259/R-258 closed, R-266 minted, G-3 unblocked
gates / gates (push) Successful in 32s

Four defects of one family, all shipped today: something the box already knows, thrown away or drawn
as its opposite. Agent v0.128.0, controller v0.210.0. NO HUB CODE, no hub bump, no ArgoCD sync.

R-265 (this repo). timeout-minutes: 5 on the gates job — every honest run in the observed session
finished in 18-34s, so this is ~9x the slowest and far under whatever reaped run 264 at 834s with no
log. The alarm mail now carries Elapsed (start stamp via $GITHUB_ENV; an absent stamp prints
"unknown (no start stamp)", never a bogus 1.7-billion-second figure) and its "names itself in the run
log" sentence is qualified so it cannot mislead when there is no log.

⚠ THE UNKNOWN IS NOT CLOSED. Whether the if: failure() alarm fires for a REAPED job is still
unverified. The timeout makes the reap unreachable in practice; it does not answer what happens in
one. Demonstrating it means deliberately hanging a run on main, which would leave the branch red for
a parallel session. Said in the workflow comment, the changelog, R-265 and the report — none of them
claiming it is answered.

GOLDEN 0.210.0 baked, published, round-trip verified, NOT VOUCHED. The currency gate went red the
moment the controller was bumped — correct — and is closed by the bake, never --no-verify. No
--no-verify anywhere this session.

⚠ THE AGENT WAS NOT PUBLISHED UNTIL THIS SESSION CHECKED, AND IT MATTERED. R-221's fix is in the
AGENT, and a fresh install takes its agent from the Day-0 manifest. The binary had been hand-deployed
to felhom-pve and never published, so agent_version 0.128.0 was not selectable and a fresh install
would have received 0.127.0 — the golden would have carried the controller fixes and NOT the one the
headline defect needed. Caught by checking each Day-0 value was FETCHABLE rather than assuming.
Published from the live-deployed bytes, sha-verified across the hop first.

Registers. R-221, R-259, R-258, R-265 CLOSED. R-266 MINTED (READY): the failed root statfs still
travels to the hub as a 0-of-0 disk; ranked LOW because it is the quiet direction — it can only miss
a true alarm, never raise a false one — and it is now a two-repo wire change governed by G-1's gate.
Highest ID moved R-265 -> R-266.

CONTEXT S-39 rules the convention this project was missing: "we do not know" is never drawn as
"fine", and the codebase has ONE way of saying it — an explicit ...Known bool companion checked in
the template. ROADMAP G-3 was explicitly blocked on that decision and is unblocked; what remains
there is a survey-and-convert of existing sites, not the gate.

Capability map row 93 CHECKED and it was NOT claiming something untrue — it is about the operator
notification path. But its narrative ("the page you open to ask whether ONE app is backed up")
invites the wrong reading, and the adjacent thing WAS false until v0.210.0, so the row now records
that the two halves disagreed and only the operator half was true.

Six red-proofs across the two code repos, each with the mutation asserted applied. The one that
matters: Part 1 Scenario A FAILED against today's tree, with the intended message.

Part 1's operator-present live validation is OWED and is the session's STOP.

repo_gates --fast: all 8 OK.
This commit is contained in:
2026-08-08 16:52:39 +02:00
parent 4f5784894e
commit 4a4a1e245a
9 changed files with 751 additions and 184 deletions
+130 -135
View File
@@ -1,169 +1,164 @@
# REPORT — instruction-file rightsizing (core + path-scoped rules), 2026-08-06
# REPORT — the seed that never ran twice, and three pictures that were not true (2026-08-08)
**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.
Four defects of one family, each with a source-verified mechanism, tests and red-proofs.
Agent **v0.128.0** · controller **v0.210.0** · `gates.yml` (workflow only). **The hub was not touched,
not bumped and not deployed.**
**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. Part 1's live sequence — NOT YET RUN
---
**The code, tests and deploy are done; the live proof is the operator-present STOP** (§10). Agent
0.128.0 is live on `felhom-pve` (`felhom-agent --version``0.128.0`, service `active`, normal
smartctl/lvs/lxc-info work in the journal). Steps 3 and 4 — the preflight NOT OK, then OK after one
tick with no daemon restart — are **owed and will be quoted verbatim when the operator says go.**
## 1. Baselines
## 2. The writer of `agent.json` — ESTABLISHED
| Repo | `main` @ start | Clean | Note |
`step_agent_config`, **`felhom.eu/scripts/felhom-host-install.sh:2396`**; the Python render at
**`:2449`**; the `O_TRUNC` write at **`:2579`**. `PRESERVE_FROM` defaults empty (**`:256`**) and is set
only by an explicit `--preserve-from` (**`:1246`**). **The render never writes an `escrow` section at
all** — grep over the whole heredoc: zero hits. The pbsdr marker is host-side
(`<agent-state>/pbsdr/marker.json`) and survives. **R-221's attribution was correct.**
A rebuild is only the case that was *measured*; the same hole opens for a hand-edited or restored
config, which is the honest reason the fix is at the seam rather than in the installer.
## 3. Red-proofs — 6 of 6, each with the mutation asserted applied
| # | mutation | assertion it applied | outcome |
|---|---|---|---|
| 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 |
| 1 | **Part 1 / Scenario A: remove the new seed call** | marker `MUTATED: the R-221 re-assert removed` present | **RED** — and **yes, it failed against today's tree**, with the intended message ✔ |
| 2 | Part 1: remove the early return as well | marker `MUTATED: early return deleted` present | **RED** — the zero-Proxmox-calls assertion is load-bearing, not decorative ✔ |
| 3 | Part 3 / F: revert to `status.LastDBDump.Success` | marker present | **RED** — app X's false green returns ✔ |
| 4 | Part 3 / G: map "no result" to `ok` | marker present | **RED** — green-on-presence returns ✔ |
| 5 | Part 2 / D: ignore `DiskKnown` in the template | marker present | **RED** — „0.0 GB / 0.0 GB (0%)" in the nominal colour returns ✔ |
| 6 | Part 2 / E: force the flag false | marker present | **RED** — a healthy box is shown losing its numbers ✔ |
**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.
All six restored and re-verified green. **Answer to the question asked directly: the Part 1 test DID
fail against today's tree.**
## 2. Contradictions: 5 before → 0 after
## 4. The Hungarian strings as shipped
| # | What conflicted | Resolution |
- `„A tárhely mérete most nem olvasható ki."` — the disk caveat line
- `„nem ismert"` — the short label in the value slot
- `„Erről a mentésről nincs eredményünk."` — the `title` on the no-verdict backup mark
## 5. The §7.3 truth table as implemented
| this app's own most recent dump result | restore point | verdict |
|---|---|---|
| 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 |
| any of its databases failed | yes | `error` |
| all clean | yes | `ok` |
| none recorded | yes | **no icon**, time only, with the title above |
| any | no | no tier-1 row, unchanged |
The sweep found **none beyond the five**.
**Recency was left alone**, deliberately: an age threshold means inventing a number, and the time is
already printed beside the icon. Recorded as an observation.
## 3. Before / after (effective = HTML comments stripped, i.e. what the model receives)
## 6. The CI timeout, and what is still unknown
| 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** |
**`timeout-minutes: 5`** — every honest run in the observed session finished in **1834 s**, so 5 min
is ~9× the slowest honest run and far under whatever reaped run 264 at 834 s. The alarm mail now
carries **`Elapsed`** (a start stamp in step 1 via `$GITHUB_ENV`; an absent stamp prints
`unknown (no start stamp)`, never a bogus number), and its "names itself in the run log" sentence is
qualified so it cannot mislead when there is no log.
**A controller session's instruction load: 31,417 → 12,986 effective bytes (59%).**
**THE UNKNOWN IS NOT CLOSED.** Whether the `if: failure()` alarm fires at all for a *reaped* job is
**still unverified**. The timeout makes the reap unreachable in practice; it does not answer what
happens inside one. Demonstrating it would mean deliberately hanging a run on `main`, which would
leave the branch red for a parallel session, so it was not done. Said in the workflow comment, the
changelog, R-265 and here — four places, none of them claiming it is answered.
Figures are bytes, deliberately. The measured `/context` ratio shows a bytes/4 token estimate
understates the true cost by **1.591.90×**, so byte counts are the honest unit here.
## 7. Tests
**`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.
| | before | after |
|---|---|---|
| controller | — | **1355** total (`+7` this session: 4 verdict, 3 disk-meter) |
| agent | — | **947** total (`+5` this session) |
## 4. Files created / modified
`go build ./... && go vet ./... && go test ./...` **green in both repos** (controller 28 packages,
agent 29), run separately from every commit. `controller_gates.py --fast` all OK; `agent_gates.py
--fast` all OK; `repo_gates.py --fast` **all 8 OK**.
**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`
The dashboard test **extracts** the meter block from the shipped template rather than copying it — a
copied block drifts and then passes while the page it covers has changed.
**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)
## 8. Deploy
## 5. Mechanism verification — done before relying on it
| | version | evidence |
|---|---|---|
| agent | **0.128.0** | `felhom-agent --version` on `felhom-pve`; service `active`; prior binary kept as `felhom-agent.bak-0.127.0` |
| controller | **0.210.0** | `docker ps` on guest 9201: `felhom-controller:0.210.0 Up (healthy)` |
The whole design rests on three claims. Two were confirmed, one was **false**:
**Endpoint-level validation of the controller UI was ATTEMPTED AND DID NOT SUCCEED — stated rather
than skipped.** What was tried: login POST against the container IP `172.17.0.2:8080` with the
mandatory `Host:` header, first with curl's cookie jar and then with the `Set-Cookie` handled
explicitly (the known `felhom_session` jar trap). Login returned `302` and `/` returned `302` back to
login both times, so the dashboard was never rendered. **Fallback observable, on the deployed
artifact rather than the source** — `grep -a` inside the running container's binary:
`SystemInfo.DiskKnown` ×1, the disk caveat line ×6, the no-verdict title ×1, `--version`
`0.210.0 (commit c732fe1)`. That proves the shipped bytes carry both fixes; it does **not** prove the
rendered page, and the template-level tests are what stand for that.
| 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 |
## 9. The bake, and the three Day-0 values
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.
`documentation/tests/golden-0.210.0-2026-08-08/` — golden **0.210.0**, **656 787 777 B**, sha256
`b9f701fa…4c0a00`, round-trip verified, `./etc/felhom-controller-image` read **out of the downloaded
archive** → `felhom-controller:0.210.0`. Markers all green, token grep 0 with a control returning 1,
bake VM destroyed, drill disk restored to `virgin`.
## 6. Amnesty list (the list to review)
| field | set to | verified |
|---|---|---|
| `golden_version` | **0.210.0** | package `GET` **200**; hub dropdown offers it, `data-sha` matches the bake |
| `agent_version` | **0.128.0** | package `GET` **200**; hub dropdown offers it, `data-sha` matches the deployed binary |
| `min_agent` | **0.127.0** (unchanged) | read from the CHANGELOG header written this session |
Deliberately short — **one** item qualified:
**⚠ The agent was NOT published until this session checked, and it mattered.** R-221's fix is in the
**agent**; the binary had been hand-deployed and never published, so `agent_version 0.128.0` was not
selectable and a fresh install would have received 0.127.0 — the golden would have carried the
controller fixes and **not** the one the headline defect needed. Caught by checking each value was
*fetchable* instead of assuming. Published from the **live-deployed bytes**, sha-verified across the
hop first (`c6eba73b…` identical on both sides).
- 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.
**`min_agent` stays 0.127.0 deliberately:** `MinAgent` declares what the *controller* requires, and
v0.210.0 requires nothing new from the agent. R-221 is delivered by `agent_version`, not by the floor.
**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.
**Nothing was vouched. No hub setting was touched.** The Save is the operator's.
## 7. Gate results
## 10. Gate failures remaining
- `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.
**None.** `golden_currency_gate.py` went red the moment the controller was bumped — correct, and
closed by the bake, not by `--no-verify`. **No `--no-verify` anywhere in this session.**
## 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.
**Closed:** R-221, R-259, R-258, R-265 (the last with its unknown explicitly still open).
**Minted:** **R-266** — the failed root `statfs` still travels to the hub as a 0-of-0 disk; ranked
low because it is the quiet direction, and now a two-repo wire change governed by G-1's gate.
**Highest ID moved R-265 → R-266.** **G-3 unblocked** in `ROADMAP.md`; **CONTEXT S-39** rules the
convention.
## 12. Observations — not acted on
**Still open, untouched:** R-246, R-255, R-256, R-257, R-261, R-262, R-263, R-264, R-240, R-243,
R-202, R-213, R-244, R-214/R-235, C7's test-comment half, and G-8's other half.
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.
## 12. The capability-map row
`00-capability-map.md:93`*"A failed per-app Tier-1 backup reaches the OPERATOR"*. **Checked, and
it was NOT claiming something untrue:** it is about the operator notification path and claims nothing
about what `/backups/apps` draws. But its narrative — *"the page you open to ask whether ONE app is
backed up"* — invites the wrong reading, and the adjacent thing WAS false: the page's tick was green
on presence until v0.210.0, so the two halves disagreed and only the operator half was true. The row
now records that.
## 13. Observations — noticed, NOT acted on
1. **Other collectors in `info_linux.go` return silently on error**`readLoadAvg`, `readMemInfo`
and the temperature read. Only the disk one was traced to a customer-visible surface, and the
change was deliberately not widened into a refactor of that file.
2. **The tick's recency weakness stands.** A tick over a three-week-old restore point is still a
tick. Adding an age threshold means inventing a number; the time is printed beside it.
3. **The controller UI could not be driven headlessly this session** (§8). Worth one session to
re-establish the documented headless login, because "invoke the endpoint the UI invokes" is this
project's standard validation method and it is currently unavailable for the controller.
4. **`HDDKnown` is wired but has no template consumer yet** — the HDD path renders through
`StorageBars`, which has its own `Disconnected` state. Adding the flag there is the natural next
step of the S-39 conversion and is part of G-3's survey, not this session.