d4c07873filed "hostinstall_gates.py is invoked by nothing" as a novel observation. It is not novel — R-29 already names the class (green gates are enforced nowhere; one sat RED for 16 releases while every REPORT said green), and R-29 was missing from OPEN-ITEMS.md entirely, having never been carried across the 2026-07-27 register rebuild. An open item about work not getting done was absent from the page that decides what gets done. Ruling on whether R-29 is the right home for a non-design-v2 gate: YES. Its title says design-v2, but its own audit list already spans mount-safety, secrets and dedup gates across four repos, and its part (b) — "the systemic half is the real item" — is about the enforcement mechanism, which is gate-agnostic. hub_confirm_gate.py is already on its list and sits in the same scripts/ directory. No new ID minted; R-29's own text forbids it, and this is the third re-raise it has absorbed. - OPEN-ITEMS.md: open R-29 (READY, S(a)/M(b)), with the orphan evidence and the two separable parts R-29 already defines. - OPEN-ITEMS.md: R-94 leg (b) now points at R-29 as its class. - ROADMAP.md:158: audit list extended with hostinstall_gates.py (RED today, 1.19.0 != 1.22.0) + hub_confirm_gate.py verified orphan. Entry not rewritten. - ROADMAP.md:147: cited a non-existent R-164 — it means controller v0.164.0. - CONTEXT.md:564: asserted in the present tense that the version cross-check is "gated by scripts/hostinstall_gates.py". It exists, is red, and runs nowhere. - OPEN-ITEMS.md: READY #1/#3/#4 markers dropped — they duplicated ranked-list positions and the gap was left by the row merged ind4c07873. - OPEN-ITEMS.md: E-2d citation :322-341 widened to :322-343; the invocation it describes is at :343, two lines outside the old range. - backlog/README.md: two-line lead naming OPEN-ITEMS.md and ROADMAP.md. - REPORT-record-correction-2026-07-29.md: the report CLAUDE.md:82-87 requires for both commits. Root REPORT.md (E-2 increment 1) untouched. No gate wired, fixed, run or deleted — that is R-29 part (b), its own task. Documentation only. No version bump, no CHANGELOG entry, no box touched.
10 KiB
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:
- 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_gateandmanifest_bearer_gate(secrets —runbooks/secrets.md:76). The title is a misnomer relative to the item's own body. - 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.shstep, or a CI job) and make a red gate block the train." That is gate-agnostic and repo-wide. hub_confirm_gate.pyis already on R-29's list and lives in the samescripts/directory ashostinstall_gates.py. Wiring one and not the other would be arbitrary.- 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/Makefile → zero 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 byscripts/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:27carries 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.
Standing note
No gate was wired, fixed, run as a hook, or deleted by either commit. 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.