Files
felhom.eu/REPORT-record-correction-2026-07-29.md
T
admin 91a1dad0f3 docs: record commit 3 and the rejected Part 3 ruling in the chain report
Appended section only; the existing content is unchanged. Separate from de5a3e5
because a commit cannot contain its own hash and the spec asked the report to
name it.

Records the four XS edits, and why documentation/backlog/README.md was NOT
touched: the proposed ROADMAP/OPEN-ITEMS state convention rests on a claim that
P3's Status column never takes READY/BLOCKED/WAITING-ON-OPERATOR. It does —
R-90 (BLOCKED), R-91 (WATCHING), R-110 (WAITING-ON-OPERATOR), R-86 (NEXT), plus
nine bare READY rows in P2's recovery-gaps sub-table. Writing it down would have
added a new false invariant to the chain that exists to remove them.
2026-07-29 11:22:40 +02:00

13 KiB
Raw Blame History

REPORT — installer-channel record correction + R-29 filing (2026-07-29)

Two commits, documentation only. No code, no version bump, no CHANGELOG entry, no build, no deploy, no box touched. Written as REPORT-<topic>.md per CLAUDE.md:82-87 so root REPORT.md (the E-2 increment-1 report) is preserved.

# Commit Baseline Scope
1 d4c07873ca0c3d3e547373a9fafc0e472a6535e8 36d635a4cdc1, unmoved Retract a false R-94/E-2d finding; open R-110
2 (this commit) d4c07873, unmoved File R-29 to the register; three record defects; this report

Commit 1 — d4c07873

What was false

36d635a4 recorded that felhom-bootstrap.sh fetches the installer from the hub, that the hub therefore serves 1.19.0, and that a fresh ISO install runs the pre-E-2 installer. All three wrong. The claim had propagated into two OPEN-ITEMS.md rows, the ranking rationale, and ROADMAP.md:149.

Confirmation table — all PASS

# Claim Read at Result
F1 bootstrap fetches from the website scripts/iso/felhom-bootstrap.sh:96 PASS — INSTALL_URL="${FELHOM_INSTALL_URL:-https://felhom.eu/scripts/felhom-host-install.sh}"
F2 hub-rendered command points at the same URL customer_unified.html:563, :1262 PASS — and three emission sites, not two: :563 static, :1262 JS error branch, :1267 JS success branch
F3 website serves /scripts/ from a git-sync tree tracking main manifests/webpage.yaml — nginx :74-77, sparse-checkout CM :211-218, git-sync :272-281, init :299-307 PASS — --branch=main --period=30s --link=current; sparse-checkout /website/ + /scripts/; location /scripts/ { root …/current; }. No image build, no ArgoCD step
F4 hostInstallVersion selects nothing configs.go:28, :487; render_test.go:219; customer_unified.html:494 PASS — repo-wide grep returns exactly those 4 code sites; all other hits prose. Rendered as a text label
F5 every generated flag is parsed by 1.22.0 generator customer_unified.html:1206-1239 vs parser felhom-host-install.sh:1175-1212 PASS — --mode --cores --memory --vmid --node --acl-storages --operator-pubkey-file --preserve-state-from --skip-provision --dry-run --preflight-only --allow-new-leaf (+ --customer-id); every one a parser case. No functional gap
F6 installer is 1.22.0 scripts/felhom-host-install.sh:187 PASS
F7 the drift test is hollow render_test.go:219-221 PASS — strings.Contains(html, hostInstallVersion) compares the constant to itself; passes at any value

Live command 1 — what the URL actually serves

$ curl -fsS https://felhom.eu/scripts/felhom-host-install.sh | grep -m1 '^SCRIPT_VERSION='
SCRIPT_VERSION="1.22.0"   # the SINGLE version source (F-1): -h, the run banners, and the hub

Live command 2 — the drift gate's real state

$ python3 scripts/hostinstall_gates.py; echo "exit=$?"
  ok: SCRIPT_VERSION=1.22.0
  ok: header has no version literal
FAIL: hub Setup-tab hostInstallVersion=1.19.0 != SCRIPT_VERSION=1.22.0 (F-1: bump both together)
  ok: age is in the installed package set
  … (six further ok lines) …
hostinstall gates: 1 FAILURE(S)
exit=1

Phase 0 source read — PAIRING reaches the same installer invocation

Mode selection felhom-bootstrap.sh:537-541: a fresh VM with no baked customer-id calls run_pairing. On HTTP 200 from /api/v1/appliance/poll the loop writes the hub-delivered FELHOM_CUSTOMER_ID + FELHOM_RETRIEVAL_PASSPHRASE into the 0600 env, re-sources it, and calls run_direct in the same invocation (:495-499). run_direct is the single site that fetches $INSTALL_URL (:322-330), builds the args (:334) and invokes bash "$SCRIPT_TMP" "${args[@]}" (:343). The customer it yields is the one the operator bound — claimable. So the ISO leg is the spine for E-2d, not an obstacle to it.

Rows changed

ID Before After
R-94 (line 14) READY — deferred until E-2d, blocked on E-2d, false ISO/hub framing READY (XS), blocked on nothing, retracted + re-scoped to three legs
R-94 (line 16) duplicate row, READY #2, 1.19.0 vs 1.20.0 deleted — merged
R-110 did not exist opened, WAITING-ON-OPERATOR (S)
E-2d ISO implied as obstacle Next-action appended; ISO is the spine
ranked list 1 R-95 · 2 R-94 (high-consequence) · 3 R-86 · 4 R-87 1 R-95 · 2 R-94 de-ranked · 3 R-86 · 4 R-87 · 5 R-110

Files: documentation/backlog/OPEN-ITEMS.md, documentation/backlog/ROADMAP.md, documentation/runbooks/day0-install.md.


Commit 2 — R-29 filing + record hygiene

The §1.1 ruling — is R-29 the right home for a non-design-v2 gate? Yes. Proceeded.

R-29's title says "the design-v2 green gates", and scripts/hostinstall_gates.py is not one — it comes from drill F-1 (2026-07-12) and postdates the item. Four things decide it anyway:

  1. R-29's own audit list already spans well beyond design-v2 subject matter. It names docker_run_volume_path_gate (docker mount safety), offbox_rename_gate, app_row_dedup_gate and manifest_bearer_gate (secrets — runbooks/secrets.md:76). The title is a misnomer relative to the item's own body.
  2. Part (b) — "the systemic half is the real item" in R-29's words — is stated with no design-v2 restriction. It is about the enforcement mechanism: "the gates run only when a human remembers to run them… decide where they run (pre-push hook, build.sh step, or a CI job) and make a red gate block the train." That is gate-agnostic and repo-wide.
  3. hub_confirm_gate.py is already on R-29's list and lives in the same scripts/ directory as hostinstall_gates.py. Wiring one and not the other would be arbitrary.
  4. Identical failure shape, identical genre. Both self-describe as mechanical grep-assertions (hostinstall_gates.py:2, hub_confirm_gate.py:1-8); both exist, assert something true, and are invoked by nothing.

R-29 has already absorbed one independent re-raise without minting an ID (2026-07-18 rehearsal note) and says so explicitly. This is the third. No new ID minted.

Orphan-search evidence — hostinstall_gates.py and hub_confirm_gate.py

Re-established at d4c07873. Pattern hostinstall_gates\|hub_confirm_gate.

# Scope Command Result
S1 felhom.eu, all file types grep -rn "$PAT" . --exclude-dir=.git 19 hits, zero invocations. All are docstrings (scripts/hostinstall_gates.py:5, scripts/hub_confirm_gate.py:7), code comments (hub/internal/web/configs.go:27, scripts/felhom-host-install.sh:189) or prose (REUSE.md:62, CONTEXT.md:540,564, hub/CHANGELOG.md:371,1292,1351,1385, scripts/CHANGELOG.md:483,524, 3 files under documentation/audits/, ROADMAP.md:149,158, OPEN-ITEMS.md:14)
S2 sibling repos grep -rln "$PAT" /mnt/5_hdd/felhom.eu/git --exclude-dir=.git minus this repo 3 files, all in .claude-memory/ (MEMORY.md, drtier-by-default-2026-07-12.md, polish-batch-2026-07-13.md). Notes, not invokers
S3 ~/.claude grep -rln "$PAT" /home/kisfenyo/.claude, and targeted on settings*.json, skills/, hooks/ Zero hits in settings, skills or hooks — where an invoker would live. Remaining hits are file-history/ (Claude Code's own backups of files edited in past sessions) and paste-cache/ (pasted task specs). Neither is an invocation site
S4 git hooks ls -1 .git/hooks/ | grep -v '\.sample$' Empty — every hook is a .sample stub
S5 build files find . -type f \( -iname Makefile -o -iname '*.mk' -o -iname justfile -o -iname 'Taskfile*' \) Only hub/Makefile; grep -n gate hub/Makefilezero occurrences
S6 CI find . -type d \( -name .github -o -name .gitea -o -name .woodpecker* -o -name .drone* -o -name .circleci \) Empty — felhom.eu has no CI configuration at all

Of the four gates in scripts/, only site_gates.py is mandated (CLAUDE.md:153); manifest_bearer_gate.py is named in runbooks/secrets.md:76.

Rows changed

ID Before After
R-29 absent from OPEN-ITEMS.md (grep -c → 0) while present at ROADMAP.md:158 since before the 2026-07-27 rebuild opened, READY (S for (a) / M for (b)), owner CC
R-94 leg (b) stood alone leg (b) carries → R-29 as its class. Row otherwise untouched
E-2d cited :322-341 for an invocation at :343 cited :322-343, with the fetch / args / call each pinned separately
R-95, R-86, R-87 READY #1, READY #3, READY #4 READY — markers dropped (see below)

Not added to the ranked list under "Why the READY rows rank this way": that list is a top-N rationale, not a complete ordering (R-99, R-102, R-103, R-104, R-108, R-109, R-89, R-92, R-93 and E-2d are all READY and absent from it). Ranking R-29 was not asked for and would be a priority claim this task has no basis to make.

§2.2 — markers dropped, not renumbered

The #1/#3/#4 markers duplicated ranked-list positions 1/3/4 for exactly those three rows; two orderings of one set is what produced the hole when the #2 row was merged in d4c07873. Removing the duplicate leaves the ranked list as the single maintained ordering.

The other two record defects

  • ROADMAP.md:147 — cited a non-existent R-164; it means controller v0.164.0's deliberate-stop filter. Corrected. (It had already cost one max-ID scan a false positive.)
  • CONTEXT.md:564 — asserted in the present tense that the single VERSION source is "gated by scripts/hostinstall_gates.py". The gate exists, asserts exactly that, is red, and runs nowhere. Corrected to say the cross-check exists but is not enforced, tracked as R-94 leg (b) / R-29. hub/internal/web/configs.go:27 carries the same false claim in a code comment and was left alone — R-94 leg (b) territory, needs a hub build.

Files: documentation/backlog/OPEN-ITEMS.md, documentation/backlog/ROADMAP.md, documentation/backlog/README.md, CONTEXT.md, this file.


Commit 3 — de5a3e5 — the record-hygiene rider

Baseline 7383400a, unmoved. Four XS items from 7383400a §9; the three deferred observations stay deferred.

File Change
CONTEXT.md:540 "scripts/hub_confirm_gate.py enforces" → asserts, but is not enforced (R-29). Third instance of the class after :564 and configs.go:27
REUSE.md:62 Same claim, "enforces zero". The rule stays — never native confirm()/prompt() is correct guidance and this is a reuse-reference row — only the enforcement claim changes
OPEN-ITEMS.md:4 Root REPORT.md = overwritten per-session; REPORT-<topic>.md = non-clobbering sibling (CLAUDE.md:82-87), 14 of them. Prohibition unchanged
OPEN-ITEMS.md:55 Heading scoped to "the TOP READY rows" + a half-sentence that it is deliberately not a full ordering. No row added to the list

hub/internal/web/configs.go:27 — the fourth instance — left alone (R-94 leg (b), needs a hub build).

Part 3 NOT done — its stated evidence is false

The spec proposed recording a convention: "ROADMAP's P3 Status column only ever takes idea or a SHIPPED/PROVEN-LIVE variant, and never takes READY, BLOCKED or WAITING-ON-OPERATOR", and concluded from that vocabulary split that ROADMAP Status is disposition while OPEN-ITEMS State is live actionability.

Refuted inside P3 itself (ROADMAP.md, lines 113172):

Line ID P3 Status cell
151 R-90 BLOCKED on Hetzner CX33 availability (operator, 2026-07-27)
152 R-91 WATCHING — gated on demo-felhom's first post-migration PBS backup
150 R-110 idea — found 2026-07-29, **WAITING-ON-OPERATOR (a ruling, not a defect)**
137 R-86 **NEXT — operator ruling 2026-07-27**

And P2's recovery-gaps sub-table (lines 99107) uses a bare READY for nine rows, including E-2.

Both halves fail: the column does take the OPEN-ITEMS vocabulary, and BLOCKED/WATCHING/ NEXT are live-actionability words, not dispositions. The real shape is a free-text status field that has absorbed both vocabularies over time — usually disposition, sometimes not. Writing the proposed convention down would install a new false invariant in the commit chain whose whole purpose was removing them, so per the spec's own instruction ("If you think this ruling is wrong, say so and make no edit") documentation/backlog/README.md was not touched.

The narrow question that prompted it still resolves benignly: R-29 and R-94 reading idea in ROADMAP and READY in OPEN-ITEMS is not a defect and needs no sync. What is unsupported is generalising that into a rule.


Standing note

No gate was wired, fixed, run as a hook, or deleted by any of the three commits. Filing R-29 is not doing R-29; the enforcement decision (pre-push hook / build.sh step / CI) and the audit of the remaining eight gates are R-29 part (b), M-sized, and its own task.