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

204 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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/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 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.