hub v0.137.0 source + burn-down round 2 in felhom.eu: R-277 R-581 R-600 R-544 R-855 R-134 R-92 R-292 R-599 R-725 R-728 (hub), R-819 R-857 R-555 R-364 R-587 (gates/tools), R-571 R-129 R-124-runbook (docs); 28 rows closed incl. catalog + agent v0.147.0 rows, R-350 merged into R-132, R-888 opened, R-887 mechanism (249 -> 222)
gates / gates (push) Successful in 2m3s

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
2026-10-05 19:43:37 +02:00
parent e0d884565f
commit 557629d2bf
65 changed files with 2680 additions and 122 deletions
@@ -370,6 +370,23 @@ own; every caller that is not the customer must decide for itself whether the ap
| `storage_handlers.go` (1600 L) | **DELETE (→agent)** | Format/attach/mount/disconnect/migrate-drive/decommission disk UI. Any survivor is a **thin client calling the agent API** (e.g. per-volume placement requests). | hazard |
| `templates/` (HTML, non-Go) | **PORT** | Remove disk-wizard + DR pages; keep app/deploy/backup/settings pages. | needs-rework |
#### Alert placement — inline under the storage bars, or the top banner (R-571)
**[FACT, read from source 2026-10-05, felhom-controller `114ff27`, `controller/internal/web/alerts.go`]** Every
dashboard alert is an `Alert` with two placement fields: `PageOnly` (the pages it may appear on; empty = every
page) and `Inline` (rendered by the page template in place, not by the layout's banner). `GetAlerts` returns
the list (endpoint-drift, agent-channel and dead-app alerts first, then the rest sorted error > warning > info,
capped at five plus an overflow line), and `layout.html` paints only those that are not `Inline` and match the
page; `GetInlineAlerts(page)` hands the dashboard and monitoring pages their inline ones.
**Exactly one warning is inline today:** the „storage is not on a separate drive" health warning. It is
`PageOnly: dashboard, monitoring` and `Inline: true`, so it sits quietly under the storage bars; **every other
warning, including every off-site failure (`07` §6.7), renders in the top banner on every page.** The choice
is made by the warning's KIND (`monitor.WarnKindStorageNotSeparate`, read with `report.WarningKindAt`), never
by its words — the earlier Hungarian-substring test would have moved the warning to the red banner on every
page the day the sentence was translated (R-553, pinned by `TestR553_DiskWarningPlacementSurvivesWordingChange`).
A new inline warning needs its own kind, not a text match.
### `scripts/`
| File | Class | Reason | Risk |
|---|---|---|---|
@@ -249,7 +249,8 @@ the identity bundle's shape is `{tunnel_token, pbs_token, wg_private_key, restic
parts a host-loss recovery would read (INV Part D2.3): `hosts.dr_record_json` is `{}` on all three
hosts; `host_escrow.directive_json` is `{}` on both escrowed hosts; `dr_recipe.host_half.drives` is
`[]` on every customer including two with enrolled data drives; and `dr_recipe.host_half.pbs.namespace`
reads `"root"` while the real namespaces are `demo-felhom` / `demo-hp`. → **R-105**, **R-106**.
reads `"root"` while the real namespaces are `demo-felhom` / `demo-hp`. → **R-105**, **R-106**. *Since agent v0.147.0 (R-124) a genuine root namespace is
recorded as `""` (PBS's own spelling) beside `namespace_state: resolved`; the word `root` is no longer written.*
---
@@ -795,6 +796,32 @@ digests all resolve today (`audits/version-travel-2026-09-26/A7/`). Options are
it; older images of the app are deleted. A restore that needs an older version re-pulls it — as before; the limit
above is unchanged, and kept data (decision 40) is not touched by the image clean-up.
### 6.7 Why an off-site run failed — the failure classifier (R-571)
**[FACT, read from source 2026-10-05, felhom-controller `114ff27`]** When an off-site (restic) run fails, the
controller names ONE cause before it writes the note the customer's page shows days later
(`ClassifyOffsiteFailure` in `controller/internal/backup/offbox.go`; the head line is the bundle key
`note.offsite.fail_<class>`, followed by the run time and the sanitised error). The classes, in the order
they are tested:
| Class | Decided by | What it means for the household |
|---|---|---|
| `orphaned` | our sentinel `ErrOffboxOrphaned` | the remote store was made with a key this box no longer has; nothing new reaches it until the operator acts |
| `quota` | our sentinel `ErrOffsiteQuota` (the pre-run soft-quota gate, R-553) | the backup did not fit the remote space; the run was refused before upload |
| `locked` | our sentinel `ErrOffsiteLocked`, or restic's lock text (R-104) | an interrupted earlier run left the store locked and both self-heal layers failed |
| `no_units` | text: „produced no snapshots" | there was nothing to send — no chosen app had a backup on any drive |
| `no_repo` | restic's text: „unable to open config file" / „is there a repository…" | nothing exists at the remote location |
| `transport` | ssh/restic/rclone text: connection refused/reset, timeout, permission denied, host key, handshake, DNS, unreachable | the remote store could not be reached (network or sign-in) |
| `unknown` | everything else | the cause is not known — the page says so instead of guessing |
**Two kinds of signal, and the difference matters.** The first three are OUR sentinels: they survive a
translation of our own text, which is why R-553 replaced the Hungarian-word match for `quota`. The text
signatures are **restic's, ssh's and rclone's own English output** — external strings we neither write nor
translate. A new restic or OpenSSH version that rewords an error moves that failure to `unknown`; it never
moves it to a wrong class. The order is deliberate: a cause that cannot be told apart returns `unknown`
rather than being folded into a neighbour. Where the warning is SHOWN on the dashboard is a separate rule —
`02-controller-module-map.md`, „Alert placement".
## 7. The recovery chain (D3) — the reason this document exists
**[DESIGN] 3-2-1 describes copies. It does not describe recovery.**
@@ -0,0 +1,58 @@
===== R-118 RED (guard removed: if s.devicePresent(d.MountPath) -> if true) — 2026-10-05T18:53:22+02:00
$ go test ./internal/localapi/ -run TestDisks_UnionPath_AbsentDeviceReportsNoRootCapacity -v -count=1
=== RUN TestDisks_UnionPath_AbsentDeviceReportsNoRootCapacity
=== RUN TestDisks_UnionPath_AbsentDeviceReportsNoRootCapacity/absent
disks_device_presence_test.go:212: absent drive advertises total=33697107968 used=4727169024 frac=0.140 — that is the filesystem UNDER the bare mountpoint, not the drive (R-118)
=== RUN TestDisks_UnionPath_AbsentDeviceReportsNoRootCapacity/present
--- FAIL: TestDisks_UnionPath_AbsentDeviceReportsNoRootCapacity (0.00s)
--- FAIL: TestDisks_UnionPath_AbsentDeviceReportsNoRootCapacity/absent (0.00s)
--- PASS: TestDisks_UnionPath_AbsentDeviceReportsNoRootCapacity/present (0.00s)
FAIL
FAIL gitea.dooplex.hu/admin/felhom-agent/internal/localapi 0.010s
FAIL
rc=1
===== R-118 GREEN (fix restored)
$ go test ./internal/localapi/ -run TestDisks_UnionPath_AbsentDeviceReportsNoRootCapacity -v -count=1
=== RUN TestDisks_UnionPath_AbsentDeviceReportsNoRootCapacity
=== RUN TestDisks_UnionPath_AbsentDeviceReportsNoRootCapacity/absent
=== RUN TestDisks_UnionPath_AbsentDeviceReportsNoRootCapacity/present
--- PASS: TestDisks_UnionPath_AbsentDeviceReportsNoRootCapacity (0.00s)
--- PASS: TestDisks_UnionPath_AbsentDeviceReportsNoRootCapacity/absent (0.00s)
--- PASS: TestDisks_UnionPath_AbsentDeviceReportsNoRootCapacity/present (0.00s)
PASS
ok gitea.dooplex.hu/admin/felhom-agent/internal/localapi 0.009s
rc=0
===== R-269 RED (tokenstore.go reverted to HEAD: reload-on-MISS-only Lookup) — 2026-10-05T18:53:55+02:00
$ go test ./internal/localapi/ -run TestTokenStore_RotatedOutTokenRejectedFirst -v -count=1
=== RUN TestTokenStore_RotatedOutTokenRejectedFirst
tokenstore_test.go:247: rotated-out token still authorizes vmid 130 on its first presentation after rotation — Mint's 'any previous token for this guest is revoked' is false across processes (R-269)
--- FAIL: TestTokenStore_RotatedOutTokenRejectedFirst (0.00s)
FAIL
FAIL gitea.dooplex.hu/admin/felhom-agent/internal/localapi 0.008s
FAIL
rc=1
===== R-269 GREEN (fix restored)
$ go test ./internal/localapi/ -run TestTokenStore_RotatedOutTokenRejectedFirst -v -count=1
=== RUN TestTokenStore_RotatedOutTokenRejectedFirst
--- PASS: TestTokenStore_RotatedOutTokenRejectedFirst (0.00s)
PASS
ok gitea.dooplex.hu/admin/felhom-agent/internal/localapi 0.010s
rc=0
===== R-317 RED (probe reverted to the pre-fix dnsmasq-base binary path usr/sbin/dnsmasq) — 2026-10-05T18:54:37+02:00
$ go test ./internal/lanresolver/ -run TestEnsureDnsmasq_BinaryWithoutUnitInstalls -v -count=1
=== RUN TestEnsureDnsmasq_BinaryWithoutUnitInstalls
ensure_dnsmasq_test.go:80: install was skipped on a dnsmasq-base-only host (binary present, unit absent) — the enable that follows targets a missing unit (R-317). calls: ["/usr/local/sbin/felhom-priv-apply dnsmasq /tmp/felhom-resolver-3076255408.conf felhom-resolver-base.conf" "systemctl enable --now dnsmasq" "systemctl restart dnsmasq"]
--- FAIL: TestEnsureDnsmasq_BinaryWithoutUnitInstalls (0.00s)
FAIL
FAIL gitea.dooplex.hu/admin/felhom-agent/internal/lanresolver 0.006s
FAIL
rc=1
===== R-317 GREEN (fix restored)
$ go test ./internal/lanresolver/ -run TestEnsureDnsmasq_BinaryWithoutUnitInstalls -v -count=1
=== RUN TestEnsureDnsmasq_BinaryWithoutUnitInstalls
--- PASS: TestEnsureDnsmasq_BinaryWithoutUnitInstalls (0.00s)
PASS
ok gitea.dooplex.hu/admin/felhom-agent/internal/lanresolver 0.004s
rc=0
@@ -0,0 +1,173 @@
=== R-760 red-proof (2026-10-05T18:53:44+02:00) — catalog 29ac711 + working tree
--- UNDO: remove the R-760 '# No healthcheck' comment block from templates/vikunja/docker-compose.yml
$ python3 scripts/test_healthcheck_explained.py
+ [] : service(s) with no compose healthcheck and no '# No healthcheck' comment saying why: ['vikunja/vikunja']
----------------------------------------------------------------------
Ran 2 tests in 0.011s
FAILED (failures=1)
rc=1
--- RESTORE
$ python3 scripts/test_healthcheck_explained.py
Ran 2 tests in 0.011s
OK
rc=0
=== R-593 red-proof (2026-10-05T18:55:00+02:00) — catalog 29ac711 + working tree
--- UNDO: git show HEAD:templates/papra/.felhom.yml > templates/papra/.felhom.yml (the pre-fix papra .felhom.yml)
$ python3 scripts/test_deploy_field_descriptions.py
- ['papra SUBDOMAIN has no description',
- 'papra AUTH_SECRET carries the subdomain sentence',
- 'papra SUBDOMAIN has no description'] : ['papra SUBDOMAIN has no description', 'papra AUTH_SECRET carries the subdomain sentence', 'papra SUBDOMAIN has no description']
----------------------------------------------------------------------
Ran 2 tests in 0.025s
FAILED (failures=1)
rc=1
--- RESTORE
$ python3 scripts/test_deploy_field_descriptions.py
Ran 2 tests in 0.025s
OK
rc=0
=== R-781 red-proof (2026-10-05T18:55:52+02:00) — catalog 29ac711 + working tree
--- UNDO: drop the isolate_onboarding_clone(cat) call in onboarding_cases (the pre-fix harness)
1
$ python3 <runner calling test_gate_decoys.onboarding_cases() only — the whole file also reaches a registry>
ok FACT: a new template with NO record rc=1 (expected 1)
ok FACT: a record missing id 1.4 rc=1 (expected 1)
ok FACT: 1.4 answered only inside an HTML comment rc=1 (expected 1)
ok FACT: done with a path that does not exist rc=1 (expected 1)
ok FACT: done with an EMPTY directory (the mkdir shape) rc=1 (expected 1)
ok FACT: done naming an absent file in the sibling repo rc=1 (expected 1)
ok FACT: n/a with an EMPTY reason rc=1 (expected 1)
ok FACT: n/a with a two-word reason rc=1 (expected 1)
ok FACT: an OPEN row rc=1 (expected 1)
ok FACT: opened: backdated before the checklist rc=1 (expected 1)
ok FACT: a checklist id the template a new app copies lacks rc=1 (expected 1)
ok FACT: an exempt app's record with a done that points nowhere rc=1 (expected 1)
FAILS 4
FAIL: GENUINE: a complete record (catalog + sibling evidence): rc=1 expected 0; missing ['onboarding gate OK']
FAIL: GENUINE: an id added AFTER opened: does not bind: rc=1 expected 0; missing ['onboarding gate OK']
FAIL: GENUINE: an exempt app's record may say open: rc=1 expected 0; missing ['exempt app(s) with a record (shape-checked): wger']
FAIL: STATED SKIP: sibling repo absent (the CI shape) - printed, not checked: rc=0 expected 0; missing ['felhom.eu/documentation/audits/onb/
rc=1
--- RESTORE
ok GENUINE: a complete record (catalog + sibling evidence) rc=0 (expected 0)
ok GENUINE: an id added AFTER opened: does not bind rc=0 (expected 0)
ok GENUINE: an exempt app's record may say open rc=0 (expected 0)
ok STATED SKIP: sibling repo absent (the CI shape) - printed, not checked rc=0 (expected 0)
FAILS 0
rc=0
=== R-806 red-proof (2026-10-05T18:56:53+02:00) — catalog 29ac711 + working tree
--- UNDO: exercise_argv back to the pre-fix shape (always http://, no -k) — the old inline argv
578: pass # red-proof: no -k
581: return a + [f"http://{ip}:{port}{path}"]
$ python3 scripts/test_check_volume_persistence.py
FAIL: test_https_backend_gets_an_https_url_with_k (__main__.TestRoutedSchemes.test_https_backend_gets_an_https_url_with_k)
AssertionError: 'http://10.0.0.5:8443/' != 'https://10.0.0.5:8443/'
Ran 54 tests in 0.018s
FAILED (failures=1)
rc=1
--- RESTORE
Ran 54 tests in 0.018s
OK
rc=0
=== R-605 red-proof (2026-10-05T18:58:47+02:00) — catalog 29ac711 + working tree
--- UNDO: HARNESS_REFUSED = 2 in both gates (the pre-fix shared code); runner: rc 3 folded back into UNDETERMINED
scripts/check-image-resolvable.py:166:HARNESS_REFUSED = 2
scripts/check-volume-persistence.py:935:HARNESS_REFUSED = 2
124:VERDICT = {0: "OK", 1: "FAILED", 2: "INCONCLUSIVE"}
220: refused = [] # red-proof
$ python3 scripts/test_check_volume_persistence.py
FAIL: test_canary_failure_prints_the_harness_refused_marker (__main__.TestCheckEntryPoint.test_canary_failure_prints_the_harness_refused_marker)
AssertionError: 2 != 3
Ran 55 tests in 0.017s
FAILED (failures=1)
rc=1
$ python3 scripts/test_check_image_resolvable.py
Ran 19 tests in 0.011s
OK
rc=0
$ python3 -m unittest scripts/test_catalog_gates.py SummaryTellsRefusedFromUndecided
FAIL: test_a_refused_harness_does_not_read_as_undetermined (test_catalog_gates.SummaryTellsRefusedFromUndecided.test_a_refused_harness_does_not_read_as_undetermined)
AssertionError: 'DID-NOT-RUN' not found in '\n==============================================================================\n== summary\n==============================================================================\n image-pins OK (exit 0)\n volume-persistence ERROR (exit 3)\nUNDETERMINED (it ran; some results could not be decided — never a pass): volume-persistence\n'
Ran 3 tests in 0.004s
FAILED (failures=1)
rc=1
--- RESTORE
$ python3 scripts/test_check_volume_persistence.py
Ran 55 tests in 0.018s
OK
rc=0
$ python3 scripts/test_check_image_resolvable.py
Ran 19 tests in 0.011s
OK
rc=0
Ran 3 tests in 0.003s
OK
rc=0
=== R-605 red-proof, image-resolvable leg re-run (2026-10-05T18:58:59+02:00) — the first run above passed because the test compared against the constant itself (tautology); the tests now pin the literal 3
--- UNDO: HARNESS_REFUSED = 2 in check-image-resolvable.py
$ python3 scripts/test_check_image_resolvable.py
FAIL: test_empty_catalog_is_an_error_not_a_pass (__main__.TestResolvabilityGate.test_empty_catalog_is_an_error_not_a_pass)
AssertionError: 2 != 3
FAIL: test_untrustworthy_resolver_refuses_to_report (__main__.TestResolvabilityGate.test_untrustworthy_resolver_refuses_to_report)
AssertionError: 2 != 3 : a resolver that resolves the canary must abort (3, the harness refused), not pass — and not 2, which reads as 'some pins were throttled' (R-605)
Ran 19 tests in 0.013s
FAILED (failures=2)
rc=1
--- RESTORE
Ran 19 tests in 0.012s
FAILED (failures=2)
rc=1
--- NOTE: the RESTORE above still failed because scripts/__pycache__ held the UNDONE module's bytecode: the undo
(3 -> 2) kept the file's size and the restore landed in the same second, so Python's mtime+size cache check
accepted the stale .pyc. Restored source confirmed HARNESS_REFUSED = 3; after `touch scripts/check-image-resolvable.py`:
$ python3 scripts/test_check_image_resolvable.py
Ran 19 tests in 0.011s
OK
rc=0
(lesson for same-size red-proofs: run with PYTHONDONTWRITEBYTECODE=1 / clear __pycache__ between undo and restore)
=== R-594 red-proof (2026-10-05T19:01:53+02:00) — catalog 29ac711 + working tree (PYTHONDONTWRITEBYTECODE=1)
--- UNDO 1: the register is read but never consulted (reg = None) — the pre-fix gate's behaviour
1
$ python3 scripts/test_gate_decoys.py
ok FACT: registered for ANOTHER app - the promise still convicts, the entry is stale rc=1 (expected 1)
ok FACT: registered on the path, but the sentence was rewritten (match gone) rc=1 (expected 1)
ok FACT: a STALE entry - nothing in the English promises it any more rc=1 (expected 1)
ok FACT: a registered promise with a two-word reason rc=1 (expected 1)
ok FACT: n/a with a two-word reason rc=1 (expected 1)
FAIL: GENUINE: a REGISTERED true retrieval promise passes: rc=1 expected 0; missing ['copy-i18n: OK', '1 registered retrieval promise(s) in ALLOWLIST_EN, 1 used
rc=1
--- UNDO 2: the STALE check removed (entries never judged live)
1
$ python3 scripts/test_gate_decoys.py
ok GENUINE: a REGISTERED true retrieval promise passes rc=0 (expected 0)
ok FACT: a registered promise with a two-word reason rc=1 (expected 1)
ok FACT: n/a with a two-word reason rc=1 (expected 1)
FAIL: FACT: registered for ANOTHER app - the promise still convicts, the entry is stale: rc=1 expected 1; missing ['STALE entry vaultwarden']
FAIL: FACT: registered on the path, but the sentence was rewritten (match gone): rc=1 expected 1; missing ['STALE entry privatebin']
FAIL: FACT: a STALE entry - nothing in the English promises it any more: rc=0 expected 1; missing ['STALE entry privatebin']
rc=1
--- RESTORE
$ python3 scripts/test_gate_decoys.py
ok GENUINE: a REGISTERED true retrieval promise passes rc=0 (expected 0)
ok FACT: registered for ANOTHER app - the promise still convicts, the entry is stale rc=1 (expected 1)
ok FACT: registered on the path, but the sentence was rewritten (match gone) rc=1 (expected 1)
ok FACT: a STALE entry - nothing in the English promises it any more rc=1 (expected 1)
ok FACT: a registered promise with a two-word reason rc=1 (expected 1)
ok FACT: n/a with a two-word reason rc=1 (expected 1)
catalog gate decoys OK — 137 case(s), every label judged on its fact (R-421)
rc=0
@@ -0,0 +1,480 @@
==================== R-591 — red-proof ====================
Fix undone in: internal/stacks/manager.go (the fix text replaced by the pre-fix shape)
$ (cd controller && go test -count=1 -run TestDeepCopyStackI18nIsNotShared -v ./internal/stacks) # FIX UNDONE
rc=1
=== RUN TestDeepCopyStackI18nIsNotShared
r591_copy_i18n_test.go:55: R-591: mutating the copy's data path overlay changed the ORIGINAL — the overlay is shared, not copied
r591_copy_i18n_test.go:55: R-591: mutating the copy's initial creds overlay changed the ORIGINAL — the overlay is shared, not copied
r591_copy_i18n_test.go:55: R-591: mutating the copy's description overlay changed the ORIGINAL — the overlay is shared, not copied
r591_copy_i18n_test.go:55: R-591: mutating the copy's tagline overlay changed the ORIGINAL — the overlay is shared, not copied
r591_copy_i18n_test.go:55: R-591: mutating the copy's deploy label overlay changed the ORIGINAL — the overlay is shared, not copied
r591_copy_i18n_test.go:55: R-591: mutating the copy's option label overlay changed the ORIGINAL — the overlay is shared, not copied
r591_copy_i18n_test.go:55: R-591: mutating the copy's use case overlay changed the ORIGINAL — the overlay is shared, not copied
r591_copy_i18n_test.go:55: R-591: mutating the copy's optional group overlay changed the ORIGINAL — the overlay is shared, not copied
r591_copy_i18n_test.go:55: R-591: mutating the copy's optional help overlay changed the ORIGINAL — the overlay is shared, not copied
r591_copy_i18n_test.go:55: R-591: mutating the copy's integration overlay changed the ORIGINAL — the overlay is shared, not copied
r591_copy_i18n_test.go:59: R-591: adding a language to the copy's I18n map added it to the ORIGINAL — the map is shared
--- FAIL: TestDeepCopyStackI18nIsNotShared (0.00s)
FAIL
FAIL gitea.dooplex.hu/admin/felhom-controller/internal/stacks 0.007s
FAIL
--- fix restored ---
$ (cd controller && go test -count=1 -run TestDeepCopyStackI18nIsNotShared -v ./internal/stacks) # FIX RESTORED
rc=0
=== RUN TestDeepCopyStackI18nIsNotShared
--- PASS: TestDeepCopyStackI18nIsNotShared (0.00s)
PASS
ok gitea.dooplex.hu/admin/felhom-controller/internal/stacks 0.007s
==================== R-568 — red-proof ====================
Fix undone in: internal/web/disk_health.go (the fix text replaced by the pre-fix shape)
$ (cd controller && go test -count=1 -run TestDiskHealthRows_OrderIsStable -v ./internal/web) # FIX UNDONE
rc=1
=== RUN TestDiskHealthRows_OrderIsStable
r568_disk_order_test.go:31: R-568: the same two disks render in a different order depending on the agent's order: [nvme0n1 sda] vs [sda nvme0n1]
--- FAIL: TestDiskHealthRows_OrderIsStable (0.00s)
FAIL
FAIL gitea.dooplex.hu/admin/felhom-controller/internal/web 0.009s
FAIL
--- fix restored ---
$ (cd controller && go test -count=1 -run TestDiskHealthRows_OrderIsStable -v ./internal/web) # FIX RESTORED
rc=0
=== RUN TestDiskHealthRows_OrderIsStable
--- PASS: TestDiskHealthRows_OrderIsStable (0.00s)
PASS
ok gitea.dooplex.hu/admin/felhom-controller/internal/web 0.009s
==================== R-567 — red-proof ====================
Fix undone in: internal/web/templates/layout.html (the fix text replaced by the pre-fix shape)
$ (cd controller && go test -count=1 -run TestStorageWizardPages_OpenTheStorageNavGroup -v ./internal/web) # FIX UNDONE
rc=1
=== RUN TestStorageWizardPages_OpenTheStorageNavGroup
r567_storage_wizard_nav_test.go:21: R-567 storage_init: the storage menu group is not open
r567_storage_wizard_nav_test.go:21: R-567 storage_attach: the storage menu group is not open
--- FAIL: TestStorageWizardPages_OpenTheStorageNavGroup (0.06s)
FAIL
FAIL gitea.dooplex.hu/admin/felhom-controller/internal/web 0.072s
FAIL
--- fix restored ---
$ (cd controller && go test -count=1 -run TestStorageWizardPages_OpenTheStorageNavGroup -v ./internal/web) # FIX RESTORED
rc=0
=== RUN TestStorageWizardPages_OpenTheStorageNavGroup
--- PASS: TestStorageWizardPages_OpenTheStorageNavGroup (0.06s)
PASS
ok gitea.dooplex.hu/admin/felhom-controller/internal/web 0.069s
==================== R-363 + R-547 — red-proof ====================
Fix undone in: cmd/controller/main.go (the fix text replaced by the pre-fix shape)
$ (cd controller && go test -count=1 -run TestFillWatchRunsOnAnInterval -v ./cmd/controller) # FIX UNDONE
rc=1
=== RUN TestFillWatchRunsOnAnInterval
r363_fillwatch_interval_test.go:56: R-363: main.go registers fillWatcher.Check on a periodic (sched.Every, fillWatchInterval) job 0 times, want 1 — without it the fill check is daily only
--- FAIL: TestFillWatchRunsOnAnInterval (0.01s)
FAIL
FAIL gitea.dooplex.hu/admin/felhom-controller/cmd/controller 0.016s
FAIL
--- fix restored ---
$ (cd controller && go test -count=1 -run TestFillWatchRunsOnAnInterval -v ./cmd/controller) # FIX RESTORED
rc=0
=== RUN TestFillWatchRunsOnAnInterval
--- PASS: TestFillWatchRunsOnAnInterval (0.01s)
PASS
ok gitea.dooplex.hu/admin/felhom-controller/cmd/controller 0.017s
==================== R-10 — red-proof ====================
Fix undone in: internal/appbackup/dbdump.go (the fix text replaced by the pre-fix shape)
$ (cd controller && go test -count=1 -run TestDumpOneTo_SyncsTheDumpDirectoryAfterRename -v ./internal/appbackup) # FIX UNDONE
rc=1
=== RUN TestDumpOneTo_SyncsTheDumpDirectoryAfterRename
r10_dump_dirsync_test.go:51: R-10: the dump directory was not fsynced after the rename: synced=[], want [/tmp/TestDumpOneTo_SyncsTheDumpDirectoryAfterRename3850100245/002/unit]
--- FAIL: TestDumpOneTo_SyncsTheDumpDirectoryAfterRename (0.00s)
FAIL
FAIL gitea.dooplex.hu/admin/felhom-controller/internal/appbackup 0.009s
FAIL
--- fix restored ---
$ (cd controller && go test -count=1 -run TestDumpOneTo_SyncsTheDumpDirectoryAfterRename -v ./internal/appbackup) # FIX RESTORED
rc=0
=== RUN TestDumpOneTo_SyncsTheDumpDirectoryAfterRename
--- PASS: TestDumpOneTo_SyncsTheDumpDirectoryAfterRename (0.00s)
PASS
ok gitea.dooplex.hu/admin/felhom-controller/internal/appbackup 0.008s
==================== R-552 (a: the clear itself) — red-proof ====================
Fix undone in: internal/backup/restore_record.go (the fix text replaced by the pre-fix shape)
$ (cd controller && go test -count=1 -run TestR552 -v ./internal/api) # FIX UNDONE
rc=1
=== RUN TestR552_RemoveClearsTheInterruptedRestoreNotice
r552_remove_clears_notice_test.go:60: R-552: the removed app's interrupted-restore notice is still listed
r552_remove_clears_notice_test.go:67: R-552: the notice comes back after a restart — the clear was not persisted
--- FAIL: TestR552_RemoveClearsTheInterruptedRestoreNotice (0.02s)
FAIL
FAIL gitea.dooplex.hu/admin/felhom-controller/internal/api 0.023s
FAIL
--- fix restored ---
$ (cd controller && go test -count=1 -run TestR552 -v ./internal/api) # FIX RESTORED
rc=0
=== RUN TestR552_RemoveClearsTheInterruptedRestoreNotice
--- PASS: TestR552_RemoveClearsTheInterruptedRestoreNotice (0.02s)
PASS
ok gitea.dooplex.hu/admin/felhom-controller/internal/api 0.024s
==================== R-552 (b: the wiring in removeStack) — red-proof ====================
Fix undone in: internal/api/router.go (the fix text replaced by the pre-fix shape)
$ (cd controller && go test -count=1 -run TestR552 -v ./internal/api) # FIX UNDONE
rc=1
=== RUN TestR552_RemoveClearsTheInterruptedRestoreNotice
r552_remove_clears_notice_test.go:87: R-552: removeStack does not call clearInterruptedRestoreNotice
--- FAIL: TestR552_RemoveClearsTheInterruptedRestoreNotice (0.02s)
FAIL
FAIL gitea.dooplex.hu/admin/felhom-controller/internal/api 0.023s
FAIL
--- fix restored ---
$ (cd controller && go test -count=1 -run TestR552 -v ./internal/api) # FIX RESTORED
rc=0
=== RUN TestR552_RemoveClearsTheInterruptedRestoreNotice
--- PASS: TestR552_RemoveClearsTheInterruptedRestoreNotice (0.02s)
PASS
ok gitea.dooplex.hu/admin/felhom-controller/internal/api 0.027s
==================== R-251 — red-proof ====================
Fix undone in: internal/backup/offbox_inventory.go (the fix text replaced by the pre-fix shape)
$ (cd controller && go test -count=1 -run TestR251 -v ./internal/backup) # FIX UNDONE
rc=1
=== RUN TestR251_MarkerTagIsNotAnApp
r251_marker_tag_test.go:48: R-251: the recovery listing shows [calibre-web felhom-offbox]; want only [calibre-web] — the marker tag is not an app
r251_marker_tag_test.go:51: R-251: 2 size calls for one app, want 1
r251_marker_tag_test.go:59: R-251: the marker tag is reported as an app with a snapshot time
--- FAIL: TestR251_MarkerTagIsNotAnApp (0.00s)
FAIL
FAIL gitea.dooplex.hu/admin/felhom-controller/internal/backup 0.007s
FAIL
--- fix restored ---
$ (cd controller && go test -count=1 -run TestR251 -v ./internal/backup) # FIX RESTORED
rc=0
=== RUN TestR251_MarkerTagIsNotAnApp
--- PASS: TestR251_MarkerTagIsNotAnApp (0.00s)
PASS
ok gitea.dooplex.hu/admin/felhom-controller/internal/backup 0.007s
==================== R-104 — red-proof ====================
Fix undone in: internal/backup/offbox.go (the fix text replaced by the pre-fix shape)
$ (cd controller && go test -count=1 -run TestR104_SurvivingLockIsNamed -v ./internal/backup) # FIX UNDONE
rc=1
=== RUN TestR104_SurvivingLockIsNamed
r104_lock_class_test.go:36: R-104: a lock that survived the self-heal is classed "unknown", want "locked"
r104_lock_class_test.go:40: R-104: the Hungarian message does not name the lock: "A távoli mentés ismeretlen okból nem sikerült (1m0s): offbox backup app: exit status 1 (offsite repository is still locked after the self-heal)"
r104_lock_class_test.go:44: R-104: the English message does not name the lock: "The remote backup failed for an unknown reason (1m0s): offbox backup app: exit status 1 (offsite repository is still locked after the self-heal)"
r104_lock_class_test.go:49: R-104: restic's lock text is classed "unknown", want "locked"
--- FAIL: TestR104_SurvivingLockIsNamed (0.01s)
FAIL
FAIL gitea.dooplex.hu/admin/felhom-controller/internal/backup 0.017s
FAIL
--- fix restored ---
$ (cd controller && go test -count=1 -run TestR104_SurvivingLockIsNamed -v ./internal/backup) # FIX RESTORED
rc=0
=== RUN TestR104_SurvivingLockIsNamed
--- PASS: TestR104_SurvivingLockIsNamed (0.01s)
PASS
ok gitea.dooplex.hu/admin/felhom-controller/internal/backup 0.017s
==================== R-619 — red-proof ====================
Fix undone in: internal/api/router.go (the fix text replaced by the pre-fix shape)
$ (cd controller && go test -count=1 -run TestR619 -v ./internal/api) # FIX UNDONE
rc=1
=== RUN TestR619_PasswordFieldIsServedAsRequired
r619_password_required_test.go:81: R-619: the password field reaches the wire as required:false, but the deploy refuses without it
--- FAIL: TestR619_PasswordFieldIsServedAsRequired (0.00s)
FAIL
FAIL gitea.dooplex.hu/admin/felhom-controller/internal/api 0.009s
FAIL
--- fix restored ---
$ (cd controller && go test -count=1 -run TestR619 -v ./internal/api) # FIX RESTORED
rc=0
=== RUN TestR619_PasswordFieldIsServedAsRequired
--- PASS: TestR619_PasswordFieldIsServedAsRequired (0.00s)
PASS
ok gitea.dooplex.hu/admin/felhom-controller/internal/api 0.011s
==================== R-362 — red-proof ====================
Fix undone in: internal/backup/restore_dir_err.go (the fix text replaced by the pre-fix shape)
$ (cd controller && go test -count=1 -run TestR362 -v ./internal/backup) # FIX UNDONE
rc=1
=== RUN TestR362_DetachedDriveIsNamed
r362_restore_drive_gone_test.go:41: R-362: the Hungarian refusal does not name the missing drive: "restore dir: mkdir /mnt/felhom-drives/hdd_1/backups: permission denied"
r362_restore_drive_gone_test.go:44: R-362: the English refusal does not name the missing drive: "restore dir: mkdir /mnt/felhom-drives/hdd_1/backups: permission denied"
r362_restore_drive_gone_test.go:49: R-362: a drive the registry marks disconnected is not named: "restore dir: mkdir /mnt/hdd_legacy: permission denied"
--- FAIL: TestR362_DetachedDriveIsNamed (0.00s)
FAIL
FAIL gitea.dooplex.hu/admin/felhom-controller/internal/backup 0.009s
FAIL
--- fix restored ---
$ (cd controller && go test -count=1 -run TestR362 -v ./internal/backup) # FIX RESTORED
rc=0
=== RUN TestR362_DetachedDriveIsNamed
[WARN] [backup] restore dir /mnt/felhom-drives/hdd_1/backups/offsite-restore/app: mkdir /mnt/felhom-drives/hdd_1/backups: permission denied — the drive /mnt/felhom-drives/hdd_1 (Kulso HDD) is not connected; reported as a missing drive (R-362)
[WARN] [backup] restore dir /mnt/hdd_legacy/x: mkdir /mnt/hdd_legacy: permission denied — the drive /mnt/hdd_legacy (Regi HDD) is not connected; reported as a missing drive (R-362)
--- PASS: TestR362_DetachedDriveIsNamed (0.01s)
PASS
ok gitea.dooplex.hu/admin/felhom-controller/internal/backup 0.017s
==================== R-675 — red-proof ====================
Fix undone in: internal/web/handlers.go (the fix text replaced by the pre-fix shape)
$ (cd controller && go test -count=1 -run TestR675 -v ./internal/web) # FIX UNDONE
rc=1
=== RUN TestR675_RefusalNamesTheWholeCopy
r675_refusal_whole_copy_test.go:33: R-675 whole copy on the second drive: the Hungarian refusal reads "Ez a mentés nem tartalmazza az alkalmazás fájljait, ezért nem állítjuk vissza az adatbázist föléjük — a fájlok így a helyükön maradnak. A fájlok a második meghajtó másolatából állíthatók vissza: „Fájlok visszaállítása”."; want it to name "Teljes visszaállítás a másolatból" and not "Fájlok visszaállítása"
r675_refusal_whole_copy_test.go:36: R-675 whole copy on the second drive: the English refusal reads "This backup does not hold the files of the app, so we do not restore the database over them — the files stay where they are. The files can be restored from the copy on the second drive: “Restore files”."; want it to name "Full restore from the copy"
--- FAIL: TestR675_RefusalNamesTheWholeCopy (0.06s)
FAIL
FAIL gitea.dooplex.hu/admin/felhom-controller/internal/web 0.073s
FAIL
--- fix restored ---
$ (cd controller && go test -count=1 -run TestR675 -v ./internal/web) # FIX RESTORED
rc=0
=== RUN TestR675_RefusalNamesTheWholeCopy
--- PASS: TestR675_RefusalNamesTheWholeCopy (0.07s)
PASS
ok gitea.dooplex.hu/admin/felhom-controller/internal/web 0.081s
==================== R-240 — red-proof ====================
Fix undone in: internal/backup/offbox.go (the fix text replaced by the pre-fix shape)
$ (cd controller && go test -count=1 -run TestR240 -v ./internal/backup) # FIX UNDONE
rc=1
=== RUN TestR240_ZeroSelectionRunDoesNotSaySuccess
[ERROR] [backup] Database discovery failed: docker ps failed: R-650: refused to run the real "docker ps --format {{.ID}}\t{{.Names}}\t{{.Label \"com.docker.compose.project\"}}\t{{.Image}} --filter status=running" under go test (this host may be production Docker); use a seam or a stub on PATH, or set FELHOM_TEST_REAL_DOCKER=1 deliberately
[WARN] [offbox] pre-push dump leg failed (docker ps failed: R-650: refused to run the real "docker ps --format {{.ID}}\t{{.Names}}\t{{.Label \"com.docker.compose.project\"}}\t{{.Image}} --filter status=running" under go test (this host may be production Docker); use a seam or a stub on PATH, or set FELHOM_TEST_REAL_DOCKER=1 deliberately) — continuing with the existing dumps; the snapshot's DB half may be older than its files
r240_zero_selection_test.go:30: R-240: the note for a run that saved nothing still calls itself successful: "Sikeres — nincs mentésre jelölt alkalmazás"
r240_zero_selection_test.go:33: R-240: the note does not say the run saved nothing: "Sikeres — nincs mentésre jelölt alkalmazás"
--- FAIL: TestR240_ZeroSelectionRunDoesNotSaySuccess (0.00s)
FAIL
FAIL gitea.dooplex.hu/admin/felhom-controller/internal/backup 0.008s
FAIL
--- fix restored ---
$ (cd controller && go test -count=1 -run TestR240 -v ./internal/backup) # FIX RESTORED
rc=0
=== RUN TestR240_ZeroSelectionRunDoesNotSaySuccess
[ERROR] [backup] Database discovery failed: docker ps failed: R-650: refused to run the real "docker ps --format {{.ID}}\t{{.Names}}\t{{.Label \"com.docker.compose.project\"}}\t{{.Image}} --filter status=running" under go test (this host may be production Docker); use a seam or a stub on PATH, or set FELHOM_TEST_REAL_DOCKER=1 deliberately
[WARN] [offbox] pre-push dump leg failed (docker ps failed: R-650: refused to run the real "docker ps --format {{.ID}}\t{{.Names}}\t{{.Label \"com.docker.compose.project\"}}\t{{.Image}} --filter status=running" under go test (this host may be production Docker); use a seam or a stub on PATH, or set FELHOM_TEST_REAL_DOCKER=1 deliberately) — continuing with the existing dumps; the snapshot's DB half may be older than its files
--- PASS: TestR240_ZeroSelectionRunDoesNotSaySuccess (0.00s)
PASS
ok gitea.dooplex.hu/admin/felhom-controller/internal/backup 0.009s
==================== R-256 (hu copy restored to the old sentence) — red-proof ====================
Fix undone in: internal/i18n/locales/hu.json (the fix text replaced by the pre-fix shape)
$ (cd controller && go test -count=1 -run TestR256 -v ./internal/web) # FIX UNDONE
rc=1
=== RUN TestR256_R257_OffboxRefusalsNameARoute
r256_r257_offbox_refusals_test.go:37: R-256: the Hungarian refusal names no route or still names the component: "A mentéskezelő nem elérhető."
r256_r257_offbox_refusals_test.go:45: R-256: the restore page's twin refusal differs: "A mentések kezelése most nem érhető el. Próbáld újra néhány perc múlva; ha akkor sem megy, keresd a Felhom ügyfélszolgálatát." vs "A mentéskezelő nem elérhető."
--- FAIL: TestR256_R257_OffboxRefusalsNameARoute (0.06s)
FAIL
FAIL gitea.dooplex.hu/admin/felhom-controller/internal/web 0.070s
FAIL
--- fix restored ---
$ (cd controller && go test -count=1 -run TestR256 -v ./internal/web) # FIX RESTORED
rc=0
=== RUN TestR256_R257_OffboxRefusalsNameARoute
--- PASS: TestR256_R257_OffboxRefusalsNameARoute (0.06s)
PASS
ok gitea.dooplex.hu/admin/felhom-controller/internal/web 0.075s
==================== R-257 (hu copy restored to the old sentence) — red-proof ====================
Fix undone in: internal/i18n/locales/hu.json (the fix text replaced by the pre-fix shape)
$ (cd controller && go test -count=1 -run TestR256 -v ./internal/web) # FIX UNDONE
rc=1
=== RUN TestR256_R257_OffboxRefusalsNameARoute
r256_r257_offbox_refusals_test.go:67: R-257: the Hungarian refusal still says "offsite": "Az offsite tároló nincs elárvult állapotban."
r256_r257_offbox_refusals_test.go:67: R-257: the Hungarian refusal still says "elárvult": "Az offsite tároló nincs elárvult állapotban."
r256_r257_offbox_refusals_test.go:71: R-257: the Hungarian refusal does not say why or where to go: "Az offsite tároló nincs elárvult állapotban."
--- FAIL: TestR256_R257_OffboxRefusalsNameARoute (0.06s)
FAIL
FAIL gitea.dooplex.hu/admin/felhom-controller/internal/web 0.071s
FAIL
--- fix restored ---
$ (cd controller && go test -count=1 -run TestR256 -v ./internal/web) # FIX RESTORED
rc=0
=== RUN TestR256_R257_OffboxRefusalsNameARoute
--- PASS: TestR256_R257_OffboxRefusalsNameARoute (0.07s)
PASS
ok gitea.dooplex.hu/admin/felhom-controller/internal/web 0.078s
==================== R-365 — red-proof ====================
Fix undone in: internal/web/handlers.go (the fix text replaced by the pre-fix shape)
$ (cd controller && go test -count=1 -run TestR365 -v ./internal/web) # FIX UNDONE
rc=1
=== RUN TestR365_OverdueCountdownIsNotFutureTense
r365_abandon_overdue_test.go:41: R-365: an overdue countdown does not say the deletion is due since 2026-10-04
r365_abandon_overdue_test.go:44: R-365: an overdue countdown still renders a past date in the future tense
--- FAIL: TestR365_OverdueCountdownIsNotFutureTense (0.14s)
FAIL
FAIL gitea.dooplex.hu/admin/felhom-controller/internal/web 0.149s
FAIL
--- fix restored ---
$ (cd controller && go test -count=1 -run TestR365 -v ./internal/web) # FIX RESTORED
rc=0
=== RUN TestR365_OverdueCountdownIsNotFutureTense
--- PASS: TestR365_OverdueCountdownIsNotFutureTense (0.15s)
PASS
ok gitea.dooplex.hu/admin/felhom-controller/internal/web 0.159s
==================== R-564 — red-proof (gate; Python, so no go test -run) ====================
Fix undone: SPLIT_PATTERNS emptied in scripts/retrieval_promise_gate.py; decoy harness run.
$ (cd controller && python3 scripts/test_gate_decoys.py | grep split) # FIX UNDONE
ok retrieval-promise/split-verb decoy rejected
FAIL: retrieval-promise/en-ok: rc=1, expected accept
FAIL: retrieval-promise/split-ok: rc=1, expected accept
rc=1
--- fix restored ---
$ (cd controller && python3 scripts/test_gate_decoys.py | grep split) # FIX RESTORED
ok retrieval-promise/split-verb decoy rejected
ok retrieval-promise/split-ok genuine accepted
all 25 controller decoys behaved — labels do not satisfy these gates
$ python3 scripts/retrieval_promise_gate.py | head -1
retrieval-promise gate OK — 42 surface(s) incl. 1 Go handler file(s), 18 registered claim(s) + 16 English, none unregistered
NOTE: the record above is NOT a valid red-proof — with SPLIT_PATTERNS emptied the seven new registrations go stale, so the gate fails for that reason, not because it caught the decoy. The valid red-proof follows.
==================== R-564 — red-proof (valid) ====================
Decoy planted in hu.json: launcher.link_masolasa = 'A régi mentéseid a kóddal bármikor állíthatók vissza.' (a split-verb retrieval PROMISE)
$ python3 scripts/<PRE-FIX retrieval_promise_gate.py from HEAD> # FIX UNDONE
retrieval-promise gate OK — 42 surface(s) incl. 1 Go handler file(s), 11 registered claim(s) + 16 English, none unregistered
rc=0 <- the pre-fix gate PASSES the planted promise (blind)
$ python3 scripts/retrieval_promise_gate.py # FIX IN PLACE
launcher.html:61 unregistered retrieval claim (állíthatók vissza):
RETRIEVAL-PROMISE GATE FAILED: 1 unregistered, 0 stale, across 42 template(s).
rc=1 <- convicted
--- decoy removed ---
$ python3 scripts/retrieval_promise_gate.py
retrieval-promise gate OK — 42 surface(s) incl. 1 Go handler file(s), 18 registered claim(s) + 16 English, none unregistered
==================== R-425 — red-proof (gate) ====================
Decoy: a NEW template internal/web/templates/backups_offbox_extra.html containing 'NAS-mentés'.
$ python3 scripts/<PRE-FIX offbox_rename_gate.py from HEAD> # FIX UNDONE
offbox rename gate OK — Tier-3 is 'Tavoli mentes' everywhere customer-facing
rc=0 <- the fixed FILES list never looks at the new file
$ python3 scripts/offbox_rename_gate.py # FIX IN PLACE
internal/web/templates/backups_offbox_extra.html:1 [NAS-mentés] <p>A NAS-ment\xe9s be\xe1ll\xedt\xe1sa</p>
OFFBOX RENAME GATE FAILED: 1 customer-facing NAS-branding string(s) remain
rc=1 <- convicted
--- decoy removed ---
offbox rename gate OK — Tier-3 is 'Tavoli mentes' everywhere customer-facing (25 file(s) + 121 bundle value(s) named by them)
==================== R-565 (the ' mp' unit the new detector found, put back) — red-proof ====================
Fix undone in: internal/web/templates/backups_remote.html (the fix text replaced by the pre-fix shape)
$ (cd controller && go test -count=1 -run TestI18nEnglishPages$ -v ./internal/web) # FIX UNDONE
rc=1
=== RUN TestI18nEnglishPages
i18n_parity_test.go:709: backups_remote_full: ASCII-only Hungarian word "mp" on the English page (R-565), line 434: "if(p.elapsed_sec > 0){ label += ' · ' + p.elapsed_sec + ' mp'; }"
i18n_parity_test.go:709: backups_remote_incomplete: ASCII-only Hungarian word "mp" on the English page (R-565), line 348: "if(p.elapsed_sec > 0){ label += ' · ' + p.elapsed_sec + ' mp'; }"
i18n_parity_test.go:709: backups_remote_error: ASCII-only Hungarian word "mp" on the English page (R-565), line 333: "if(p.elapsed_sec > 0){ label += ' · ' + p.elapsed_sec + ' mp'; }"
i18n_parity_test.go:709: backups_remote_running: ASCII-only Hungarian word "mp" on the English page (R-565), line 353: "if(p.elapsed_sec > 0){ label += ' · ' + p.elapsed_sec + ' mp'; }"
i18n_parity_test.go:709: backups_remote_pending_agent: ASCII-only Hungarian word "mp" on the English page (R-565), line 339: "if(p.elapsed_sec > 0){ label += ' · ' + p.elapsed_sec + ' mp'; }"
i18n_parity_test.go:709: backups_remote_pending_old: ASCII-only Hungarian word "mp" on the English page (R-565), line 339: "if(p.elapsed_sec > 0){ label += ' · ' + p.elapsed_sec + ' mp'; }"
i18n_parity_test.go:709: backups_remote_stale: ASCII-only Hungarian word "mp" on the English page (R-565), line 337: "if(p.elapsed_sec > 0){ label += ' · ' + p.elapsed_sec + ' mp'; }"
i18n_parity_test.go:709: backups_remote_stale_old: ASCII-only Hungarian word "mp" on the English page (R-565), line 337: "if(p.elapsed_sec > 0){ label += ' · ' + p.elapsed_sec + ' mp'; }"
i18n_parity_test.go:709: backups_remote_escrowed: ASCII-only Hungarian word "mp" on the English page (R-565), line 336: "if(p.elapsed_sec > 0){ label += ' · ' + p.elapsed_sec + ' mp'; }"
i18n_parity_test.go:709: backups_remote_notconf_hub: ASCII-only Hungarian word "mp" on the English page (R-565), line 285: "if(p.elapsed_sec > 0){ label += ' · ' + p.elapsed_sec + ' mp'; }"
i18n_parity_test.go:709: backups_remote_notconf: ASCII-only Hungarian word "mp" on the English page (R-565), line 284: "if(p.elapsed_sec > 0){ label += ' · ' + p.elapsed_sec + ' mp'; }"
i18n_parity_test.go:709: backups_remote_empty: ASCII-only Hungarian word "mp" on the English page (R-565), line 258: "if(p.elapsed_sec > 0){ label += ' · ' + p.elapsed_sec + ' mp'; }"
i18n_parity_test.go:709: backups_remote_offsite_offer_fit: ASCII-only Hungarian word "mp" on the English page (R-565), line 349: "if(p.elapsed_sec > 0){ label += ' · ' + p.elapsed_sec + ' mp'; }"
--- FAIL: TestI18nEnglishPages (4.23s)
FAIL
FAIL gitea.dooplex.hu/admin/felhom-controller/internal/web 4.243s
FAIL
--- fix restored ---
$ (cd controller && go test -count=1 -run TestI18nEnglishPages$ -v ./internal/web) # FIX RESTORED
rc=0
=== RUN TestI18nEnglishPages
--- PASS: TestI18nEnglishPages (4.27s)
PASS
ok gitea.dooplex.hu/admin/felhom-controller/internal/web 4.285s
==================== R-603 (an apostrophe planted in a Go-named English value) — red-proof ====================
Fix undone in: internal/i18n/locales/en.json (the fix text replaced by the pre-fix shape)
$ (cd controller && go test -count=1 -run TestR603 -v ./internal/i18n) # FIX UNDONE
rc=1
=== RUN TestR603_GoNamedValuesDoNotHideBehindHTMLEscaping
r603_escape_test.go:126: control: a planted apostrophe in err.backup.a_pillanatkep_egy_utvonala_ervenytelen was not caught (got [err.backup.a_pillanatkep_egy_utvonala_ervenytelen note.offsite.fail_locked])
--- FAIL: TestR603_GoNamedValuesDoNotHideBehindHTMLEscaping (0.29s)
FAIL
FAIL gitea.dooplex.hu/admin/felhom-controller/internal/i18n 0.296s
FAIL
--- fix restored ---
$ (cd controller && go test -count=1 -run TestR603 -v ./internal/i18n) # FIX RESTORED
rc=0
=== RUN TestR603_GoNamedValuesDoNotHideBehindHTMLEscaping
--- PASS: TestR603_GoNamedValuesDoNotHideBehindHTMLEscaping (0.34s)
PASS
ok gitea.dooplex.hu/admin/felhom-controller/internal/i18n 0.343s
==================== R-454 — the gate seen RED on the unformatted tree, before the formatting pass ====================
$ (cd controller && python3 scripts/gofmt_gate.py)
not gofmt-clean: cmd/controller/main.go
not gofmt-clean: internal/agentapi/diskverdict.go
not gofmt-clean: internal/api/update_reason_test.go
not gofmt-clean: internal/appbackup/namespace_root_test.go
not gofmt-clean: internal/appbackup/r381_undo_naming_test.go
not gofmt-clean: internal/backup/r669_applied_meta_test.go
not gofmt-clean: internal/family/family.go
not gofmt-clean: internal/infra/infra.go
not gofmt-clean: internal/notify/r636_oom_storm_test.go
not gofmt-clean: internal/quiesce/tiers_test.go
not gofmt-clean: internal/stacks/delete.go
not gofmt-clean: internal/stacks/life_records.go
GOFMT GATE FAILED: 12 file(s) — run `gofmt -w <file>` (formatting only, no behaviour change)
rc=1
==================== R-208 (controller half: ARGs moved back above go mod download) — red-proof ====================
Fix undone in: Dockerfile (the fix text replaced by the pre-fix shape)
$ (cd controller && go test -count=1 -run TestR208 -v ./cmd/controller) # FIX UNDONE
rc=0
=== RUN TestR208_DockerfileVersionArgsSitBelowModuleDownload
--- PASS: TestR208_DockerfileVersionArgsSitBelowModuleDownload (0.00s)
PASS
ok gitea.dooplex.hu/admin/felhom-controller/cmd/controller 0.009s
--- fix restored ---
$ (cd controller && go test -count=1 -run TestR208 -v ./cmd/controller) # FIX RESTORED
rc=0
=== RUN TestR208_DockerfileVersionArgsSitBelowModuleDownload
--- PASS: TestR208_DockerfileVersionArgsSitBelowModuleDownload (0.00s)
PASS
ok gitea.dooplex.hu/admin/felhom-controller/cmd/controller 0.006s
NOTE: NOT CONVICTED above — the test kept the LAST declaration of each ARG, so a duplicate declaration above the download hid behind the one below. Test fixed to keep the FIRST declaration; red-proof re-run below.
==================== R-208 (re-run after the test fix) — red-proof ====================
Fix undone in: Dockerfile (the fix text replaced by the pre-fix shape)
$ (cd controller && go test -count=1 -run TestR208 -v ./cmd/controller) # FIX UNDONE
rc=1
=== RUN TestR208_DockerfileVersionArgsSitBelowModuleDownload
r208_dockerfile_args_test.go:50: R-208: ARG VERSION (line 12) is declared above `go mod download` (line 19), so every build with a new value re-downloads the modules
r208_dockerfile_args_test.go:50: R-208: ARG GIT_COMMIT (line 13) is declared above `go mod download` (line 19), so every build with a new value re-downloads the modules
--- FAIL: TestR208_DockerfileVersionArgsSitBelowModuleDownload (0.00s)
FAIL
FAIL gitea.dooplex.hu/admin/felhom-controller/cmd/controller 0.009s
FAIL
--- fix restored ---
$ (cd controller && go test -count=1 -run TestR208 -v ./cmd/controller) # FIX RESTORED
rc=0
=== RUN TestR208_DockerfileVersionArgsSitBelowModuleDownload
--- PASS: TestR208_DockerfileVersionArgsSitBelowModuleDownload (0.00s)
PASS
ok gitea.dooplex.hu/admin/felhom-controller/cmd/controller 0.008s
==================== R-591 follow-up (DataPaths + AfterLoad copies removed) — red-proof ====================
Fix undone in: internal/stacks/manager.go (the fix text replaced by the pre-fix shape)
$ (cd controller && go test -count=1 -run TestDeepCopyStackMetaSharesNoReference -v ./internal/stacks) # FIX UNDONE
rc=1
=== RUN TestDeepCopyStackMetaSharesNoReference
r591_copy_meta_alias_test.go:21: R-591: deepCopyStack leaves Meta.AfterLoad shared with the original — a write through the copy changes the stack
r591_copy_meta_alias_test.go:21: R-591: deepCopyStack leaves Meta.DataPaths shared with the original — a write through the copy changes the stack
--- FAIL: TestDeepCopyStackMetaSharesNoReference (0.00s)
FAIL
FAIL gitea.dooplex.hu/admin/felhom-controller/internal/stacks 0.007s
FAIL
--- fix restored ---
$ (cd controller && go test -count=1 -run TestDeepCopyStackMetaSharesNoReference -v ./internal/stacks) # FIX RESTORED
rc=0
=== RUN TestDeepCopyStackMetaSharesNoReference
--- PASS: TestDeepCopyStackMetaSharesNoReference (0.00s)
PASS
ok gitea.dooplex.hu/admin/felhom-controller/internal/stacks 0.007s
@@ -0,0 +1,13 @@
== agent_update 0.147.0 (sha 642c4d19…) signed with felhom-op-1, ttl 45m, 2026-10-05T17:02:13Z
-- demo-hp-bb76ea
signed: op=agent_update host=demo-hp-bb76ea guest="" key_id=felhom-op-1 nonce=b05c992ea1b100af9f53df761c76d440 expires=2026-10-05T17:47:13Z
wrote envelope to <scratch>/env-demo-hp-bb76ea-agent_update.json
uploaded signed op to the hub jobs queue
-- demo-felhom-8363b5
signed: op=agent_update host=demo-felhom-8363b5 guest="" key_id=felhom-op-1 nonce=67f8cbeed47a8a107d3809e230837af0 expires=2026-10-05T17:47:13Z
wrote envelope to <scratch>/env-demo-felhom-8363b5-agent_update.json
uploaded signed op to the hub jobs queue
-- tester-1-d70be4
signed: op=agent_update host=tester-1-d70be4 guest="" key_id=felhom-op-1 nonce=c0decf168b76b852d71db79378bcb795 expires=2026-10-05T17:47:13Z
wrote envelope to <scratch>/env-tester-1-d70be4-agent_update.json
uploaded signed op to the hub jobs queue
@@ -0,0 +1,14 @@
== System page 2026-10-05T17:13:38Z after agent_update: demo-hp, demo-felhom, tester-1 rows read Agent 0.147.0 (root files 0.146.1); Tester-2 0.142.0 → 0.147.0 (offline)
== agent_config_update 0.147.0 (bundle sha 326527d0…), 2026-10-05T17:13:38Z
-- demo-hp-bb76ea
signed: op=agent_config_update host=demo-hp-bb76ea guest="" key_id=felhom-op-1 nonce=f0d53302c1591959f9577dd2660690d1 expires=2026-10-05T17:58:38Z
wrote envelope to <scratch>/env-demo-hp-bb76ea-agent_config_update.json
uploaded signed op to the hub jobs queue
-- demo-felhom-8363b5
signed: op=agent_config_update host=demo-felhom-8363b5 guest="" key_id=felhom-op-1 nonce=c92ab23d76d00821a36ec20018c69b6a expires=2026-10-05T17:58:38Z
wrote envelope to <scratch>/env-demo-felhom-8363b5-agent_config_update.json
uploaded signed op to the hub jobs queue
-- tester-1-d70be4
signed: op=agent_config_update host=tester-1-d70be4 guest="" key_id=felhom-op-1 nonce=c51c5f6448ec01d4919408e0590dd1bd expires=2026-10-05T17:58:38Z
wrote envelope to <scratch>/env-tester-1-d70be4-agent_config_update.json
uploaded signed op to the hub jobs queue
@@ -0,0 +1,4 @@
== vouch 2026-10-05T17:01:27Z: POST /configuration/artifacts (Basic + X-Felhom-Operator), agent 0.147.0, golden 0.296.0, min_agent 0.131.0
HTTP/1.1 303 See Other
Location: /configuration?flash=artifacts_set
2026/10/05 19:01:56 [INFO] Artifact manifest set: agent=0.147.0 golden=0.296.0 min_agent="0.131.0" wrapper_sha=false bundle_sha="326527d0993c9a62df2f790c7700ca645cedbf0673dcfb6dc1768d8610b8007d"
@@ -0,0 +1,366 @@
# felhom.eu burndown2 red-proofs, 2026-10-05 (fix undone -> test fails; fix restored -> test passes)
### R-277 (offsite row bytes)
$ (cd . && go test ./internal/web -run 'TestOffsiteRow_SmallRepoNotZeroGB' -v -count=1)
--- fix UNDONE (rc=1):
=== RUN TestOffsiteRow_SmallRepoNotZeroGB
r277_r92_bytes_test.go:27: 162 KB repo rendered as "0.0 GB", want "162.0 KB"
--- FAIL: TestOffsiteRow_SmallRepoNotZeroGB (0.04s)
FAIL
FAIL gitea.dooplex.hu/admin/felhom-hub/internal/web 0.062s
FAIL
--- fix RESTORED (rc=0):
=== RUN TestOffsiteRow_SmallRepoNotZeroGB
--- PASS: TestOffsiteRow_SmallRepoNotZeroGB (0.04s)
PASS
ok gitea.dooplex.hu/admin/felhom-hub/internal/web 0.064s
### R-92 (PBS DR exact bytes)
$ (cd . && go test ./internal/web -run 'TestPBSDRPanel_ExactBytesShowsSmallDelta' -v -count=1)
--- fix UNDONE (rc=1):
=== RUN TestPBSDRPanel_ExactBytesShowsSmallDelta
r277_r92_bytes_test.go:55: PBS DR panel must show exact bytes:
PBS DR datastore</h3>
<table class="detail-table">
<tr><th style="width: 12rem;">Datastore</th><td><code>felhom-offsite</code> (ep0)</td></tr>
<tr><th>Capacity</th><td>40.0 GB</td></tr>
<tr><th>Used</th><td>8.0 GB &middot; 20% full</td></tr>
</table>
<div class="bar" style="margin: 0.4rem 0 0.9rem;"><div class="bar-fill bar-ok" style="width: 20%;"></div></div>
<table class="detail-table">
<tr><th style="width: 12rem;">Polled</th><td>just now</td></tr>
</table>
</section>
<p class="text-muted" style="margin: 0 0 1rem; font-size: 0.85em;">
The endpoint below IS the PBS DR host. Peer allocation and endpoint sync currently use the
lowest endpoint id (ep0); per-endpoint allocation is a future work item.
</p>
<section class="card" style="margin-bottom: 1.5rem;">
<h3>Endpoint</h3>
<p class="text-muted">Not configured. Add one below (or via <code>PUT /api/v1/admin/wg/endpoint</code>, runbook: offsite-endpoint.md).</p>
</section>
<section class="card" style="margin-bottom: 1.5rem;">
<h3 id="ep-form-title">Add endpoint</h3>
<form method="POST" action="/offsite/endpoints" id="ep-form" onsubmit="return epFormSubmitCheck()"
style="display: grid; grid-template-columns: auto 1fr; gap: 0.5rem; align-items: center; max-width: 44em; margin-top: 0.75rem;">
<input type="hidden" name="_csrf" value="">
<label style="font-size: 0.9em;">Endpoint id</label>
<input type="text" name="endpoint_id" id="ep-id" placeholder="ep1" style="padding: 0.3em 0.5em;">
<label style="font-size: 0.9em;">DNS name</label>
<input type="text" name="dns_name" id="ep-dns" placeholder="ep1.felhom.eu" style="padding: 0.3em 0.5em;">
<label style="font-size: 0.9em;">WG port</label>
<input type="number" name="wg_port" id="ep-port" min="1" max="65535" placeholder="443" style="padding: 0.3em 0.5em;">
--- fix RESTORED (rc=0):
=== RUN TestPBSDRPanel_ExactBytesShowsSmallDelta
--- PASS: TestPBSDRPanel_ExactBytesShowsSmallDelta (0.07s)
PASS
ok gitea.dooplex.hu/admin/felhom-hub/internal/web 0.093s
### R-581 (newest report tie-break)
$ (cd . && go test ./internal/store -run TestGetCustomers_SameSecondReportsOneRowNewestWins -v -count=1)
--- fix UNDONE (rc=1):
=== RUN TestGetCustomers_SameSecondReportsOneRowNewestWins
r581_newest_report_test.go:32: GetCustomers returned 2 rows for one customer, want exactly 1
--- FAIL: TestGetCustomers_SameSecondReportsOneRowNewestWins (0.03s)
FAIL
FAIL gitea.dooplex.hu/admin/felhom-hub/internal/store 0.040s
FAIL
--- fix RESTORED (rc=0):
=== RUN TestGetCustomers_SameSecondReportsOneRowNewestWins
--- PASS: TestGetCustomers_SameSecondReportsOneRowNewestWins (0.03s)
PASS
ok gitea.dooplex.hu/admin/felhom-hub/internal/store 0.040s
### R-600 (delete cascade WG peer push + honest COMPLETE line)
$ (cd . && go test ./internal/web -run 'TestDeleteCascade_TriggersWGPeerPushAndSaysSo' -v -count=1)
--- fix UNDONE (rc=1):
=== RUN TestDeleteCascade_TriggersWGPeerPushAndSaysSo
customer_delete_test.go:642: WG peer-sync triggers = 0, want exactly 1 (the endpoint must drop the peer now, not on the next tick)
--- FAIL: TestDeleteCascade_TriggersWGPeerPushAndSaysSo (0.04s)
FAIL
FAIL gitea.dooplex.hu/admin/felhom-hub/internal/web 0.070s
FAIL
--- fix RESTORED (rc=0):
=== RUN TestDeleteCascade_TriggersWGPeerPushAndSaysSo
--- PASS: TestDeleteCascade_TriggersWGPeerPushAndSaysSo (0.05s)
PASS
ok gitea.dooplex.hu/admin/felhom-hub/internal/web 0.072s
### R-600 (main wiring of SetWGPeerSync)
$ (cd . && go test ./cmd/hub -run TestR600_MainWiresWGPeerSyncIntoWeb -v -count=1)
--- fix UNDONE (rc=1):
=== RUN TestR600_MainWiresWGPeerSyncIntoWeb
r600_wiring_test.go:32: cmd/hub/main.go never calls webServer.SetWGPeerSync(<reconciler>.Trigger) — the delete cascade cannot push the peer removal
--- FAIL: TestR600_MainWiresWGPeerSyncIntoWeb (0.00s)
FAIL
FAIL gitea.dooplex.hu/admin/felhom-hub/cmd/hub 0.024s
FAIL
--- fix RESTORED (rc=0):
=== RUN TestR600_MainWiresWGPeerSyncIntoWeb
--- PASS: TestR600_MainWiresWGPeerSyncIntoWeb (0.00s)
PASS
ok gitea.dooplex.hu/admin/felhom-hub/cmd/hub 0.025s
### R-599 (ONLINE 409 says when deletion opens; host + cascade)
$ (cd . && go test ./internal/web -run TestHostDelete_OnlineRefusalSaysWhenItOpens -v -count=1)
--- fix UNDONE (rc=1):
=== RUN TestHostDelete_OnlineRefusalSaysWhenItOpens
r544_r599_host_delete_test.go:74: host-delete 409 body missing "min ago":
Host is ONLINE — deletion is refused (a live agent would receive 401s permanently).
r544_r599_host_delete_test.go:74: host-delete 409 body missing "deletion opens at 17:42 UTC":
Host is ONLINE — deletion is refused (a live agent would receive 401s permanently).
r544_r599_host_delete_test.go:74: host-delete 409 body missing "45m0s":
Host is ONLINE — deletion is refused (a live agent would receive 401s permanently).
r544_r599_host_delete_test.go:84: cascade ONLINE refusal = 409 "Delete refused: host gone-vm is ONLINE. Decommission the box first — the cascade never deletes a live host.\n", want 409 naming the opening time
--- FAIL: TestHostDelete_OnlineRefusalSaysWhenItOpens (0.05s)
FAIL
FAIL gitea.dooplex.hu/admin/felhom-hub/internal/web 0.066s
FAIL
--- fix RESTORED (rc=0):
=== RUN TestHostDelete_OnlineRefusalSaysWhenItOpens
--- PASS: TestHostDelete_OnlineRefusalSaysWhenItOpens (0.04s)
PASS
ok gitea.dooplex.hu/admin/felhom-hub/internal/web 0.065s
### R-544 (host-delete log states demotion)
$ (cd . && go test ./internal/web -run TestHostDelete_LogSaysEscrowDemotedNotDeleted -v -count=1)
--- fix UNDONE (rc=1):
=== RUN TestHostDelete_LogSaysEscrowDemotedNotDeleted
r544_r599_host_delete_test.go:32: log still says the escrow was deleted:
[INFO] host deleted: esc-host (escrow deleted: true)
r544_r599_host_delete_test.go:35: log must state the demotion:
[INFO] host deleted: esc-host (escrow deleted: true)
--- FAIL: TestHostDelete_LogSaysEscrowDemotedNotDeleted (0.04s)
FAIL
FAIL gitea.dooplex.hu/admin/felhom-hub/internal/web 0.064s
FAIL
--- fix RESTORED (rc=0):
=== RUN TestHostDelete_LogSaysEscrowDemotedNotDeleted
--- PASS: TestHostDelete_LogSaysEscrowDemotedNotDeleted (0.04s)
PASS
ok gitea.dooplex.hu/admin/felhom-hub/internal/web 0.063s
### R-855 (start log prints effective Docker nights)
$ (cd . && go test ./cmd/hub ./internal/osupdates -run 'TestR855_StartLogPrintsEffectiveDockerNights|TestDockerNightsEffective' -v -count=1)
--- fix UNDONE (rc=1):
=== RUN TestR855_StartLogPrintsEffectiveDockerNights
r855_docker_nights_log_test.go:41: the Docker approval-nights start log must print osSvc.DockerNightsEffective(), not the raw field
--- FAIL: TestR855_StartLogPrintsEffectiveDockerNights (0.00s)
FAIL
FAIL gitea.dooplex.hu/admin/felhom-hub/cmd/hub 0.022s
=== RUN TestDockerNightsEffective
--- PASS: TestDockerNightsEffective (0.00s)
PASS
ok gitea.dooplex.hu/admin/felhom-hub/internal/osupdates 0.005s
FAIL
--- fix RESTORED (rc=0):
=== RUN TestR855_StartLogPrintsEffectiveDockerNights
--- PASS: TestR855_StartLogPrintsEffectiveDockerNights (0.00s)
PASS
ok gitea.dooplex.hu/admin/felhom-hub/cmd/hub 0.022s
=== RUN TestDockerNightsEffective
--- PASS: TestDockerNightsEffective (0.00s)
PASS
ok gitea.dooplex.hu/admin/felhom-hub/internal/osupdates 0.006s
### R-134 (zone candidates strip progressively; red = the old one-label parentDomain)
$ (cd . && go test ./internal/cloudflare -run TestZoneCandidates -v -count=1)
--- fix UNDONE (rc=1):
=== RUN TestZoneCandidates
unblock_test.go:24: zoneCandidates("a.b.felhom.eu") = [a.b.felhom.eu b.felhom.eu], want [a.b.felhom.eu b.felhom.eu felhom.eu]
unblock_test.go:24: zoneCandidates("x.y.z.example.co") = [x.y.z.example.co y.z.example.co], want [x.y.z.example.co y.z.example.co z.example.co example.co]
--- FAIL: TestZoneCandidates (0.00s)
FAIL
FAIL gitea.dooplex.hu/admin/felhom-hub/internal/cloudflare 0.006s
FAIL
--- fix RESTORED (rc=0):
=== RUN TestZoneCandidates
--- PASS: TestZoneCandidates (0.00s)
PASS
ok gitea.dooplex.hu/admin/felhom-hub/internal/cloudflare 0.006s
### R-292 (artifact sha flash names the cause; red = every failure -> artifact_sha_invalid, the old single flash)
$ (cd . && go test ./internal/web -run 'TestArtifactSave_FlashNamesTheCause' -v -count=1)
--- fix UNDONE (rc=1):
=== RUN TestArtifactSave_FlashNamesTheCause
=== RUN TestArtifactSave_FlashNamesTheCause/version_not_found
r292_artifact_flash_test.go:57: flash = "artifact_sha_invalid", want "artifact_version_missing"
=== RUN TestArtifactSave_FlashNamesTheCause/registry_failing
r292_artifact_flash_test.go:57: flash = "artifact_sha_invalid", want "artifact_unverifiable"
=== RUN TestArtifactSave_FlashNamesTheCause/no_sha_listed
r292_artifact_flash_test.go:57: flash = "artifact_sha_invalid", want "artifact_sha_missing"
=== RUN TestArtifactSave_FlashNamesTheCause/healthy
--- FAIL: TestArtifactSave_FlashNamesTheCause (0.24s)
--- FAIL: TestArtifactSave_FlashNamesTheCause/version_not_found (0.06s)
--- FAIL: TestArtifactSave_FlashNamesTheCause/registry_failing (0.07s)
--- FAIL: TestArtifactSave_FlashNamesTheCause/no_sha_listed (0.07s)
--- PASS: TestArtifactSave_FlashNamesTheCause/healthy (0.04s)
FAIL
FAIL gitea.dooplex.hu/admin/felhom-hub/internal/web 0.264s
--- fix RESTORED (rc=0):
=== RUN TestArtifactSave_FlashNamesTheCause
=== RUN TestArtifactSave_FlashNamesTheCause/version_not_found
=== RUN TestArtifactSave_FlashNamesTheCause/registry_failing
=== RUN TestArtifactSave_FlashNamesTheCause/no_sha_listed
=== RUN TestArtifactSave_FlashNamesTheCause/healthy
--- PASS: TestArtifactSave_FlashNamesTheCause (0.17s)
--- PASS: TestArtifactSave_FlashNamesTheCause/version_not_found (0.04s)
--- PASS: TestArtifactSave_FlashNamesTheCause/registry_failing (0.04s)
--- PASS: TestArtifactSave_FlashNamesTheCause/no_sha_listed (0.04s)
--- PASS: TestArtifactSave_FlashNamesTheCause/healthy (0.04s)
PASS
ok gitea.dooplex.hu/admin/felhom-hub/internal/web 0.188s
### R-725 (expired bind page points at the button)
$ (cd . && go test ./internal/web -run TestBindExpiredPage_PointsAtTheButton -v -count=1)
--- fix UNDONE (rc=1):
=== RUN TestBindExpiredPage_PointsAtTheButton
r725_bind_expired_copy_test.go:28: the expired page with a button does not carry the sentence that points at it
r725_bind_expired_copy_test.go:31: the expired page with a button still sends the household to support
--- FAIL: TestBindExpiredPage_PointsAtTheButton (0.04s)
FAIL
FAIL gitea.dooplex.hu/admin/felhom-hub/internal/web 0.065s
FAIL
--- fix RESTORED (rc=0):
=== RUN TestBindExpiredPage_PointsAtTheButton
--- PASS: TestBindExpiredPage_PointsAtTheButton (0.04s)
PASS
ok gitea.dooplex.hu/admin/felhom-hub/internal/web 0.057s
### R-725 (console banner glyph; red = the check mark restored in script + golden)
$ (cd . && python3 scripts/iso/test/test_console_glyphs.py)
--- fix UNDONE (rc=1):
FAIL: console glyphs the Latin-2 font cannot draw (R-725):
--- fix RESTORED (rc=0):
OK: 58 printf literals + 3 goldens use only console-safe glyphs
### R-728 (in-flight create guard; red = guard removed, seam kept)
$ (cd . && go test ./internal/web -run TestConfigCreate_ConcurrentSubmitCreatesOnce -v -count=1)
--- fix UNDONE (rc=1):
=== RUN TestConfigCreate_ConcurrentSubmitCreatesOnce
r728_create_once_test.go:57: customer created 2 times for one press, want exactly 1:
[INFO] Customer config created: tester-2
[INFO] self-bind link NOT auto-minted for tester-2 on customer creation: no mailer configured on this hub
[INFO] Customer config created: tester-2
[INFO] self-bind link NOT auto-minted for tester-2 on customer creation: no mailer configured on this hub
--- FAIL: TestConfigCreate_ConcurrentSubmitCreatesOnce (0.04s)
FAIL
FAIL gitea.dooplex.hu/admin/felhom-hub/internal/web 0.064s
FAIL
--- fix RESTORED (rc=0):
=== RUN TestConfigCreate_ConcurrentSubmitCreatesOnce
--- PASS: TestConfigCreate_ConcurrentSubmitCreatesOnce (0.05s)
PASS
ok gitea.dooplex.hu/admin/felhom-hub/internal/web 0.065s
### R-208 hub half (Dockerfile ARG order; red = ARGs back above go mod download)
$ (cd . && python3 scripts/test_dockerfile_arg_order.py)
--- fix UNDONE (rc=1):
FAIL: hub/Dockerfile (R-208):
--- fix RESTORED (rc=0):
OK: hub/Dockerfile declares its per-build ARGs below the module download
### R-819 (check_stands accepts CLOSED rows; red = rule 3 reads OPEN-ITEMS.md only)
$ (cd . && python3 scripts/check_stands.py)
--- fix UNDONE (rc=1):
CONVICTED — 34 problem(s):
--- fix RESTORED (rc=0):
check_stands: OK — every claim cites a source, every citation resolves, and every 'walked' cites a walk.
### R-819 decoy suite (the genuine closed-row stand must pass; red = OPEN-only rule 3)
$ (cd . && python3 scripts/test_gate_decoys.py)
--- fix UNDONE (rc=1):
ok hub-confirm/subdir decoy rejected
ok manifest-bearer/subdir decoy rejected
ok observations/R-419 decoy rejected
ok observations/genuine-FILED genuine accepted
ok observations/genuine-NAF genuine accepted
ok reuse-refs/missing-go decoy rejected
ok reuse-refs/missing-md (KNOWN HOLE R-422) genuine accepted
ok golden-currency empty dir rejected AND named
ok closed-register/body-word (BY DESIGN) genuine accepted
ok closed-register/verdict-word decoy rejected
ok closed-register/unreadable-row decoy rejected
ok closed-register/duplicate-closed-id decoy rejected
ok closed-register/finished-row-in-open decoy rejected
ok closed-register/open-row-closed-word (BY DESIGN) genuine accepted
ok one-register/suffix-id-row decoy rejected
--- fix RESTORED (rc=0):
ok hub-confirm/subdir decoy rejected
ok manifest-bearer/subdir decoy rejected
ok observations/R-419 decoy rejected
ok observations/genuine-FILED genuine accepted
ok observations/genuine-NAF genuine accepted
ok reuse-refs/missing-go decoy rejected
ok reuse-refs/missing-md (KNOWN HOLE R-422) genuine accepted
ok golden-currency empty dir rejected AND named
ok closed-register/body-word (BY DESIGN) genuine accepted
ok closed-register/verdict-word decoy rejected
ok closed-register/unreadable-row decoy rejected
ok closed-register/duplicate-closed-id decoy rejected
ok closed-register/finished-row-in-open decoy rejected
ok closed-register/open-row-closed-word (BY DESIGN) genuine accepted
ok one-register/suffix-id-row decoy rejected
### R-857 (golden gate reads the re-bake; red = the old regex + version-only sort)
$ (cd . && python3 scripts/test_golden_currency_gate.py)
--- fix UNDONE (rc=1):
CASE 17 ok (R-857): a later-dated bake of the same version wins
FAIL: CASE 16 (R-857): two bakes of one version — the gate did not report the RE-BAKE's sha
golden currency gate OK — the newest released controller has a golden (NOTE: this checks the BAKE, not the vouch — see the module docstring)
--- fix RESTORED (rc=0):
CASE 16 ok (R-857): of two bakes of one version, the re-bake's sha is the one reported
CASE 17 ok (R-857): a later-dated bake of the same version wins
golden-currency gate self-test OK — a directory name alone cannot satisfy it (R-410), and the waiver is judged on its dates, its row and its direction (2026-09-13)
### R-555 (wire-contract strips comments; red = whole-file tokenising as before)
$ (cd . && python3 scripts/test_gate_decoys.py > /tmp/claude-1000/-mnt-5-hdd-felhom-eu-git/ea20e5ca-a93a-4939-bd73-dd472bcf0590/scratchpad/d.out 2>&1; rc=$?; grep -E 'FAIL: wire|ok wire|behaved|exposed a hole' /tmp/claude-1000/-mnt-5-hdd-felhom-eu-git/ea20e5ca-a93a-4939-bd73-dd472bcf0590/scratchpad/d.out; exit $rc)
--- fix UNDONE (rc=1):
ok wire-contract/genuine genuine accepted
FAIL: wire-contract/comment: rc=0, want 10 (10 = the comment-only tag convicted, 0 = passed)
--- fix RESTORED (rc=0):
ok wire-contract/comment decoy rejected
ok wire-contract/genuine genuine accepted
### R-364 (hu_grep refuses an untested zero; red = the anchor check disabled; no bytecode cache)
$ (cd . && PYTHONDONTWRITEBYTECODE=1 python3 -B scripts/test_hu_grep.py)
--- fix UNDONE (rc=1):
ok present accented string counted rc=0 1 line(s)
ok octal-escaped pattern REFUSED rc=2 REFUSED: the pattern arrived TRANSFORMED ('k\\303\\251rj') — octal esc
ok U+FFFD pattern REFUSED rc=2 REFUSED: the pattern arrived TRANSFORMED ('k�rj') — octal escapes or U
ok no anchor given REFUSED rc=2 REFUSED: an accented pattern needs --anchor with ASCII text known to b
ok NFD file, NFC pattern REFUSED rc=2 REFUSED: 0 for the pattern as typed, but 1 line(s) hold its NFD form —
ok tested zero is a zero rc=1 0 line(s) — a TESTED zero: anchor 'Felhom' found on 1 line(s), negativ
ok ascii pattern plain zero rc=1 0 line(s)
FAIL:
blind instrument REFUSED: rc=1 msg="0 line(s) — a TESTED zero: anchor 'NotInTheFile' found on 0 line(s), negative control 0, other normal form 0", want rc=2 containing 'anchor'
--- fix RESTORED (rc=0):
ok present accented string counted rc=0 1 line(s)
ok octal-escaped pattern REFUSED rc=2 REFUSED: the pattern arrived TRANSFORMED ('k\\303\\251rj') — octal esc
ok U+FFFD pattern REFUSED rc=2 REFUSED: the pattern arrived TRANSFORMED ('k�rj') — octal escapes or U
ok blind instrument REFUSED rc=2 REFUSED: the anchor 'NotInTheFile' was not found either — the instrume
ok no anchor given REFUSED rc=2 REFUSED: an accented pattern needs --anchor with ASCII text known to b
ok NFD file, NFC pattern REFUSED rc=2 REFUSED: 0 for the pattern as typed, but 1 line(s) hold its NFD form —
ok tested zero is a zero rc=1 0 line(s) — a TESTED zero: anchor 'Felhom' found on 1 line(s), negativ
ok ascii pattern plain zero rc=1 0 line(s)
hu_grep: OK — no untested zero for an accented pattern
### R-587 (release build refuses a stray *.rootpw.txt; red = the guard body emptied; run under BusyBox PATH too)
$ (cd . && PATH=/tmp/claude-1000/-mnt-5-hdd-felhom-eu-git/ea20e5ca-a93a-4939-bd73-dd472bcf0590/scratchpad/bb python3 scripts/iso/test/test_rootpw_guard.py)
--- fix UNDONE (rc=1):
FAIL (R-587):
--- fix RESTORED (rc=0):
OK: a release build refuses a *.rootpw.txt in its out dir; a clean dir and a non-release build proceed
@@ -0,0 +1,5 @@
### R-124 red-proof: PBSRootNamespace back to "root"
dr_recipe_test.go:478: root namespace on the wire = "root", want "" (PBS's spelling; no namespace is named "root")
--- FAIL: TestR124_RootNamespaceOnTheWireIsPBSSpelling (0.00s)
FAIL
FAIL gitea.dooplex.hu/admin/felhom-agent/internal/hub 0.008s
+28
View File
@@ -75,6 +75,34 @@ The full text of every row below: `git show e8c56c44:documentation/backlog/OPEN-
| **R-704** | **[P3-LOW] The box's crash-loop stop (decision 28) outlives the app: after remove and reinstall, the new install is still held.** (P3) | CLOSED 2026-10-05 — ACCEPTED BY THE OPERATOR (ruling 2026-10-05 18:23, the burn-down list) | Reason on the list: A fresh install drops an old update hold: fixed in v0.278.0 with tests; not seen live. Fix would cost: a live install with a leftover hold. If never: the tests stay the proof. Evidence of the verdict: `audits/burndown-2026-10-05/partA-table.md`. |
| **R-706** | **[P3-LOW] Removing an app "with its backups" leaves its off-site verification copy on the drive.** (P3) | CLOSED 2026-10-05 — ACCEPTED BY THE OPERATOR (ruling 2026-10-05 18:23, the burn-down list) | Reason on the list: Removing an app with its backups also deletes its off-site test copy: fixed in v0.279.0 with tests; not seen live. Fix would cost: a live remove with an off-site copy. If never: the tests stay the proof. Evidence of the verdict: `audits/burndown-2026-10-05/partA-table.md`. |
| **R-723** | **[P3-LOW] A fresh box sends the operator two mails on day one that describe nothing wrong.** (P3) | CLOSED 2026-10-05 — ACCEPTED BY THE OPERATOR (ruling 2026-10-05 18:23, the burn-down list) | Reason on the list: No 'box recovered' alarm in a new box's first hour: fixed in hub v0.126.0 with tests; not seen at a real first install. Fix would cost: watch the next real install. If never: the tests stay the proof. Evidence of the verdict: `audits/burndown-2026-10-05/partA-table.md`. |
| **R-277** | **Three hub surfaces jointly present a HEALTHY off-site tier as an absent one — and it produced a wrong operator statement during this run.** (P3) | CLOSED 2026-10-05 — FIXED (burn-down round 2) | felhom.eu (hub v0.137.0 commit, this one): `hub/internal/web/offsite_box.go` — a repo under 1 GB is not „0.0 GB"; `TestOffsiteRow_SmallRepoNotZeroGB`; red-proof `audits/burndown2-2026-10-05/felhom-eu-red-proofs.txt` |
| **R-581** | **[P2-MED] `ORDER BY received_at` cannot answer "the newest report" — the column has SECOND granularity.** (P3) | CLOSED 2026-10-05 — FIXED (burn-down round 2) | felhom.eu (hub v0.137.0 commit, this one): `GetCustomers` joins on `MAX(id)`; `TestGetCustomers_SameSecondReportsOneRowNewestWins` (old query: 2 rows); red-proof `audits/burndown2-2026-10-05/felhom-eu-red-proofs.txt` |
| **R-600** | **[P2-MEDIUM] "Full teardown" is logged while the deleted box's WireGuard peer is still configured on ep0.** (P3) | CLOSED 2026-10-05 — FIXED (burn-down round 2) | felhom.eu (hub v0.137.0 commit, this one): delete cascade triggers the WG peer push and says so; wired in main; `TestDeleteCascade_*`, `TestR600_MainWiresWGPeerSyncIntoWeb`; red-proofs `audits/burndown2-2026-10-05/felhom-eu-red-proofs.txt` |
| **R-544** | **[P3-LOW] The host-delete log line says „escrow deleted: true" while the documented (and actual) effect is DEMOTION to retained custody.** (P4) | CLOSED 2026-10-05 — FIXED (burn-down round 2) | felhom.eu (hub v0.137.0 commit, this one): host-delete log states the escrow effect; `TestHostDelete_LogSaysEscrowDemotedNotDeleted`; red-proof `audits/burndown2-2026-10-05/felhom-eu-red-proofs.txt` |
| **R-855** | **The hub's start log prints "after -1 healthy ring-0 night(s)" for the TEST override `OS_DOCKER_APPROVE_NIGHTS=0`** (P4) | CLOSED 2026-10-05 — FIXED (burn-down round 2) | felhom.eu (hub v0.137.0 commit, this one): `DockerNightsEffective` printed in the start log; `TestDockerNightsEffective`, `TestR855_StartLogPrintsEffectiveDockerNights`; red-proof `audits/burndown2-2026-10-05/felhom-eu-red-proofs.txt` |
| **R-134** | **Two zone-resolvers disagree on depth.** (P4) | CLOSED 2026-10-05 — FIXED (burn-down round 2) | felhom.eu (hub v0.137.0 commit, this one): `cloudflare.zoneCandidates`; `TestZoneCandidates`; red-proof `audits/burndown2-2026-10-05/felhom-eu-red-proofs.txt` |
| **R-92** | Hub PBS-DR gauge is 0.1 GB-granular — small deltas unverifiable (P4) | CLOSED 2026-10-05 — FIXED (burn-down round 2) | felhom.eu (hub v0.137.0 commit, this one): PBS-DR panel exact bytes; `TestPBSDRPanel_ExactBytesShowsSmallDelta`; red-proof `audits/burndown2-2026-10-05/felhom-eu-red-proofs.txt` |
| **R-292** | **The artifact-save flash conflates three different facts, and a failing test found it rather than a reading.** (P4) | CLOSED 2026-10-05 — FIXED (burn-down round 2) | felhom.eu (hub v0.137.0 commit, this one): artifact save flashes name the cause (new `artifact_version_missing`); `TestArtifactSave_FlashNamesTheCause` +2; red-proof `audits/burndown2-2026-10-05/felhom-eu-red-proofs.txt` |
| **R-599** | **[P3-LOW] A drill's teardown is blocked for 30 minutes by design, and nothing says so.** (P4) | CLOSED 2026-10-05 — FIXED (burn-down round 2) | felhom.eu (hub v0.137.0 commit, this one): 409 bodies say last report + when deletion opens; `TestHostDelete_OnlineRefusalSaysWhenItOpens`; target-selection names the wait; red-proof `audits/burndown2-2026-10-05/felhom-eu-red-proofs.txt` |
| **R-725** | **[P3-LOW] Small copy slips on the first-hour path.** (P4) | CLOSED 2026-10-05 — FIXED (burn-down round 2) | felhom.eu (hub v0.137.0 commit, this one): `bind.invalid.body_resend` (hu+en) points at the button; ISO glyph removed (ships with the next ISO); `TestBindExpiredPage_PointsAtTheButton`, `iso/test/test_console_glyphs.py`; red-proofs `audits/burndown2-2026-10-05/felhom-eu-red-proofs.txt` |
| **R-728** | **[P3-LOW] A customer created with one press was created TWICE, and the first of its two connect mails holds a dead link.** (P3) | CLOSED 2026-10-05 — FIXED (burn-down round 2) | felhom.eu (hub v0.137.0 commit, this one): per-customer-ID in-flight guard; `TestConfigCreate_ConcurrentSubmitCreatesOnce` (old: two creates for one press; passes under -race); red-proof `audits/burndown2-2026-10-05/felhom-eu-red-proofs.txt` |
| **R-571** | **[P3-LOW] The off-site failure classifier and the dashboard's alert-placement rules are described in no architecture document.** (P4) | CLOSED 2026-10-05 — FIXED (burn-down round 2) | felhom.eu (hub v0.137.0 commit, this one): `07` §6.7 the six off-site failure classes and what each means for the household; `02` alert placement. Docs only. |
| **R-819** | **`scripts/check_stands.py` is red and runs in no runner.** (P4) | CLOSED 2026-10-05 — FIXED (burn-down round 2) | felhom.eu (hub v0.137.0 commit, this one): `check_stands.py` rule 3 accepts CLOSED-ITEMS ids; `stands` gate registered with 3 decoys (the status-agreement half not built — too vague); red-proof `audits/burndown2-2026-10-05/felhom-eu-red-proofs.txt` |
| **R-857** | **Baking a golden twice under the SAME version leaves two stale facts.** (P4) | CLOSED 2026-10-05 — FIXED (burn-down round 2) | felhom.eu (hub v0.137.0 commit, this one): golden gate reads suffixed bake dirs and the newest bake log; runbook re-vouch line; CASE 16/17; red-proof `audits/burndown2-2026-10-05/felhom-eu-red-proofs.txt` |
| **R-555** | **[P3-LOW] The wire-contract gate counts a field as received when its name appears in a Go COMMENT on the receiving side.** (P4) | CLOSED 2026-10-05 — FIXED (burn-down round 2) | felhom.eu (hub v0.137.0 commit, this one): wire gate strips comments before matching; 6 surfaced fields allow-listed (2 → R-888); decoy, exemption removed; red-proof `audits/burndown2-2026-10-05/felhom-eu-red-proofs.txt` |
| **R-364** | **Accented-text search is an instrument that silently transforms its input, and discipline alone has failed at least three times.** (P4) | CLOSED 2026-10-05 — FIXED (burn-down round 2) | felhom.eu (hub v0.137.0 commit, this one): `scripts/hu_grep.py` + `test_hu_grep.py`; pointer in `REUSE.md`; red-proof `audits/burndown2-2026-10-05/felhom-eu-red-proofs.txt` |
| **R-587** | **[P3-LOW] Two root-password files sit in the directory the public ISO is published FROM.** (P3) | CLOSED 2026-10-05 — FIXED (burn-down round 2) | felhom.eu (hub v0.137.0 commit, this one): release ISO build refuses with a `*.rootpw.txt` in its out dir; skill publish block stops on one; `iso/test/test_rootpw_guard.py` (BusyBox PATH); red-proof `audits/burndown2-2026-10-05/felhom-eu-red-proofs.txt` |
| **R-129** | **Every doc says demo-hp has "no baked SSH key"** (P4) | CLOSED 2026-10-05 — FIXED (burn-down round 2) | felhom.eu (hub v0.137.0 commit, this one): demo-hp authenticates with DooPlex's own key (`ssh -o BatchMode=yes demo-hp` → ok; authorized_keys holds `SHA256:pgQh228R…`, the same as DooPlex's `~/.ssh/id_ed25519`, deliberate); `operations/nodes.md` „Access", `target-selection.md`, memory corrected; G1 stays the fallback. |
| **R-593** | **[P3-LOW] papra's session-signing key is described as „the app's subdomain".** (P3) | CLOSED 2026-10-05 — FIXED (burn-down round 2) | app-catalog `4828dc7` (CI run 1368, re-run success): papra SUBDOMAIN/AUTH_SECRET descriptions; `test_deploy_field_descriptions.py`; red-proof `audits/burndown2-2026-10-05/catalog-red-proofs.txt` |
| **R-760** | **[P3-LOW] vikunja's compose has no healthcheck, and nothing says why.** (P4) | CLOSED 2026-10-05 — FIXED (burn-down round 2) | app-catalog `4828dc7`: vikunja compose says why there is no healthcheck (comment only); `test_healthcheck_explained.py`; red-proof catalog-red-proofs.txt |
| **R-594** | **[P3-LOW] The catalog copy gate can CONVICT a retrieval promise but has no way to REGISTER a true one.** (P4) | CLOSED 2026-10-05 — FIXED (burn-down round 2) | app-catalog `4828dc7`: `check-copy-i18n.py` English allow-list (`copy_freeze/allowlist_en.json`); 5 decoys; red-proof catalog-red-proofs.txt |
| **R-605** | **[P3-LOW] A catalog gate that REFUSED TO RUN and a gate that ran and could not decide print the same word, so a reader cannot tell which happened.** (P4) | CLOSED 2026-10-05 — FIXED (burn-down round 2) | app-catalog `4828dc7`: a refusing harness exits 3, the runner prints DID-NOT-RUN; decoys both ways; red-proof catalog-red-proofs.txt. (The CLAUDE.md exit-code line was NOT updated — permission check refused the instruction-file edit.) |
| **R-781** | **[P3-LOW] The catalog's `scripts/test_gate_decoys.py` fails 4 of its own "genuine" onboarding cases — on the untouched tree.** (P4) | CLOSED 2026-10-05 — FIXED (burn-down round 2) | app-catalog `4828dc7`: onboarding decoys run on a scratch clone without the real records; 137 decoy cases pass; red-proof catalog-red-proofs.txt |
| **R-124** | **The recipe spells PBS's root namespace `"root"`, but the PBS API spells it `""`** (P4) | CLOSED 2026-10-05 — FIXED (burn-down round 2) | felhom-agent v0.147.0 (`f1b9b41`, tag v0.147.0, sha256 `642c4d19…`; delivered by signed jobs 2026-10-05 — `audits/burndown2-2026-10-05/delivery/`): the recipe's root namespace is `""` beside `namespace_state: resolved`; `TestR124_RootNamespaceOnTheWireIsPBSSpelling` (red-proof: "root" → FAIL, `r124-red-proof.txt`); runbook `ep0-datastore-copy.md` step 2 says how to read it. Operator ruling 2026-10-05 18:23: fix it. |
| **R-118** | **An absent drive's union row advertises the ROOT filesystem's capacity as its own.** (P3) | CLOSED 2026-10-05 — FIXED (burn-down round 2) | felhom-agent v0.147.0 (delivered): capacity read only while the device is present; `TestDisks_UnionPath_AbsentDeviceReportsNoRootCapacity`; red-proof `audits/burndown2-2026-10-05/agent-red-proofs.txt` |
| **R-269** | **A rotated-out per-guest local-API token still authorises, and the test that appears to pin the opposite passes only because of its lookup ORDER.** (P3) | CLOSED 2026-10-05 — FIXED (burn-down round 2) | felhom-agent v0.147.0 (delivered): token store reloads on growth before answering; `TestTokenStore_RotatedOutTokenRejectedFirst`; red-proof agent-red-proofs.txt |
| **R-317** | **The agent decides whether to install dnsmasq by stat-ing a file the OTHER package owns.** (P3) | CLOSED 2026-10-05 — FIXED (burn-down round 2) | felhom-agent v0.147.0 (delivered): dnsmasq install probed by its service unit; `TestEnsureDnsmasq_*`; red-proof agent-red-proofs.txt |
| **R-350** | **SECURITY — the hub operator password was printed in cleartext into a session transcript by CC, 2026-08-20. Rotation recommended.** (P3) | CLOSED 2026-10-05 — DUPLICATE of R-132 (its unique facts moved there) | Same credential (hub operator password HUB_PW), same mechanism (curl -w '%{redirect_url}' re-renders Basic-auth into the URL), same single action (operator decides to rotate). R-132 already folded R-580 (third occurrence 2026-09-18) on 2026-10-03; R-350 is the 2026-08-20 occurrence. |
---
File diff suppressed because one or more lines are too long
+8 -5
View File
@@ -107,12 +107,15 @@ was left in its working island configuration. It is a throwaway: destroy with `q
historical golden-bake `drill.qcow2` still lives on **DooPlex** (`/mnt/5_hdd/felhom.eu/drill/`, ~18G,
powered off) and is unrelated. The build-PIPELINE relocation to the t740 remains unbuilt.
### Access — there is no baked SSH key
### Access — DooPlex's key, and the G1 password as the fallback
`ssh demo-hp` resolves to the tailnet address, but **no operator public key is on this box** — the
HP profile deliberately left `FELHOM_ROOT_SSH_KEY` blank. Authentication is the **G1 break-glass
root password vaulted in the hub**, `host_recovery` row `demo-hp-bb76ea` (set at day-0,
2026-07-21 16:24 UTC).
**`ssh demo-hp` from DooPlex authenticates BY KEY** (corrected 2026-10-05, R-129; measured with
`ssh -o BatchMode=yes demo-hp`). The day-0 HP profile left `FELHOM_ROOT_SSH_KEY` blank, so no key was
baked — the key was added later: root's `authorized_keys` (last changed 2026-08-21) holds DooPlex's
own `~/.ssh/id_ed25519` (fingerprint `SHA256:pgQh228R…`, the operator's address as its comment) beside
the box's own `root@demo-hp` RSA key. `~/.ssh/config` on DooPlex points `demo-hp` at it. The **G1
break-glass root password vaulted in the hub** (`host_recovery` row `demo-hp-bb76ea`, set at day-0,
2026-07-21 16:24 UTC) remains the way in when the key does not work.
Retrieval (operator-side, and **shred the copy** — that DB holds every host's secret):
@@ -224,6 +224,12 @@ bake's own acceptance check. The real guard was never weak: the script's own
never deleted by a bake (the publish step's pre-delete targets only its own version), so rolling
back is a form submission, not a rebuild.
**A re-bake under the SAME version: re-vouch at once (R-857).** The publish pre-deletes and re-uploads
that version's package, so the hub keeps vouching the FIRST bake's sha, which the registry no longer
holds — a fresh install in that window fails its sha check. Re-select the golden and Save right after
the publish. Put the evidence in `golden-<VER>-<DATE>-rebake/`; `golden_currency_gate.py` reads the
newest bake of a version (a later date, then the suffixed directory of the same date).
A golden is only needed when a publish train wants fresh installs current — demo deploys never need it.
**Since 2026-09-13 the cadence is §4.2: weekly, and before any drill or fresh install.**
The full 0.188.0 run, with the observables: `documentation/audits/tester-gate-golden-0.188.0-2026-07-31.md`.
@@ -43,6 +43,10 @@ recovered with the household's recovery code — the same as restoring from ep0)
`/etc/pve/priv/storage/<id>.enc`, then append the storage entry to `/etc/pve/storage.cfg`:
`pbs: <id>` / `datastore ep0-copy` / `server <DooPlex>` / `content backup` / `fingerprint <DooPlex PBS cert>` /
`namespace <customer>` / `username root@pam!<name>`.
**The namespace comes from the hub's Recipe** (`pbs.namespace`, read WITH `pbs.namespace_state`): `resolved` and a
name → `namespace <name>`; `resolved` and an **empty** value → the ROOT namespace: write **no** `namespace` line (and
pass no `--ns` to `proxmox-backup-client`). Agents before v0.147.0 wrote the word `root` there — no namespace has that
name, so treat a recorded `root` as empty (R-124). `unknown` → read the namespace off the copy's `ns/` folder above.
**Do not use `pvesm add pbs` without `--password`:** it validates with the password from its command line, fails 401,
and on failure DELETES the `.pw`/`.enc` files you placed (measured). Passing `--password` puts the token on argv.
3. `pvesm list <id>` → the household's snapshots (measured: 2 s). Then restore with the safe script (R-834):
+8 -2
View File
@@ -108,8 +108,7 @@ felhom-pve, and **moved off DooPlex**. This page exists because that ruling sat
`disconnected` forever). **That warning is about one storage, not the box.** `/mnt/hdd_1` is also
the `felhom-backup` target and the enrolled user-data drive, so remove scratch storages when done.
- **Forbidden:** do not destroy or unblock **`drill-r50` (VM 300)** — the only drift fixture (R-93). **Measured 2026-09-13 (R-461): `qm list` is EMPTY on both demo-hp and demo-felhom — the VM does not exist anywhere, so the fence currently protects nothing and R-93's premise is gone. The fence stays as written for the day someone rebuilds it; do not read its presence here as evidence the fixture exists.**
(Access: the docs say no baked SSH key and G1 break-glass, but a key authenticated on 2026-07-31 —
**R-129**, unresolved.)
(Access: DooPlex's own key works — `ssh demo-hp`; G1 break-glass is the fallback. `operations/nodes.md`, R-129.)
### `demo-felhom` — N100 · **Tier 0**
@@ -200,6 +199,13 @@ Create and destroy freely **on a Tier 0 host**. Two exceptions: **`drill-r50`**
scratch; and scratch **customers** outlive their VMs in the hub — delete those too, or they accumulate
(`sess-c` and `sess-d` were both left behind before anyone noticed).
**The hub-side teardown waits for the stale threshold — plan for it (R-599).** After the VM is destroyed
the hub still reads its host as ONLINE until the last report is older than `alerting.stale_threshold`
(the deployed value is in `manifests/hub.yaml`, not the code default). Until then both the host delete
and `POST /configs/<id>/delete` answer **409** — by design, a live agent would get 401s for ever. The
409 body names the last report's age and the time deletion opens; wait for that time, then delete. A
409 inside that window is not a failed delete.
#### A fixture may prove a mechanism. Only a fresh box may prove a path.
Rebuilding from scratch every time is waste; reusing a box is legitimate — but not for every claim.