91a1dad0f3
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.
204 lines
13 KiB
Markdown
204 lines
13 KiB
Markdown
# 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 113–172):
|
||
|
||
| 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 99–107) 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.
|