09 §3 decision 250 (operator 2026-10-15 11:26/11:29): a release needs no golden, CC raises the floor after the three test boxes, the golden is weekly; 247 reversed, 7 stands. golden_currency_gate judges the golden's AGE (>7 days); Sunday golden step; rule files, CLAUDE.md, runbooks, build skill follow; R-1045 closed (floor 0.313.0); R-1051 floor route, R-1052 new-box path, R-1053 rc tags never moved
@@ -168,8 +168,9 @@ These are rulings, not proposals. Anything specced against a different assumptio
### 2026-09-13 (afternoon) — the floor carries a release without a golden, and every backup counts
7.~~**A floor carries a release past the vouched golden when the release's MinAgent is declared with
it**~~ **SUPERSEDED by decision 247 (operator, 2026-10-15): bake → vouch → floor; never a floor for a version with no golden.** The hub mechanism below still exists; the PROCESS no longer uses it. (R-472; hub v0.112.0.) Inside the golden the manifest's MinAgent governs, as before. Above it,
7. **A floor carries a release past the vouched golden when the release's MinAgent is declared with
it** (R-472; hub v0.112.0). *Superseded by decision 247 at 07:48 on 2026-10-15 and REINSTATED by decision 250 at
11:26 the same day — it stands.* Inside the golden the manifest's MinAgent governs, as before. Above it,
the MinAgent the operator declares with the floor — read from the release's CHANGELOG header, which
`minagent_header_gate.py` now guarantees — goes into the same per-box agent comparison. An
undeclared floor above the golden is still HELD, and both floor forms refuse to save one. **Why:** a
@@ -915,9 +916,28 @@ its length, and both fixes cost something the household would notice — operato
127.**The agent's three by-design abilities (`03` §3.1) stay for now**; revisited before the first paying customer.
*Operator ruling 2026-10-05.* (R-861)
### 2026-10-15 (11:26/11:29) — a release needs no golden; CC raises the floor; the golden is weekly (recorded before the work)
250.**A controller release needs no golden; CC raises the floor; the golden is baked weekly.** *Operator ruling 2026-10-15
11:26/11:29.* Reverses decision 247 (07:48 the same day); decision 7 stands again.
1.**The image goes to the registry only when it is ready for every box** — every household's manual update button
offers the HIGHEST tag in the registry (`TriggerUpdate("manual")`), while the floor path targets the floor.
2.**CC raises the floor itself**, with the release's MinAgent declared, when both guards hold: (a) the release ran on
the three test boxes (Tester 1, demo-hp, demo-felhom) through the normal update path; (b) the box walk's checks are
green on all three afterwards (`scripts/https_only_check.py` included). Not at night unless the night brief says so.
*Route:* the only way to save the global floor today is the operator page with the operator's own password; CC
does not use it for this until the operator grants a scoped route (R-1051).
3.**Agent releases (and their bundles) still stop for the operator's vouch;** a floor whose MinAgent is above the
vouched agent is never set.
4.**The golden is baked weekly** (Sunday night, `runbooks/nightly-rotation.md`); the operator vouches it. A new box
installs the vouched golden and updates itself to the floor (not yet proven end to end — R-1052).
5. The push gate judges the golden by its AGE (older than 7 days while the floor is above it), not by the newest
10:46:53 gate after 30 s more (Plex fully started): {"state": "closed", "since": "2026-10-15T08:45:53Z", "hosts": ["plex.enkisfelhom.hu"]}
10:46:53 the household's „Kész” on the unclaimed Plex -> 409 {"data": null, "error": "Plex does not report the connection yet. Try again in a minute.", "ok": false}
10:46:54 GET https://plex.enkisfelhom.hu/identity (Accept application/json) -> 401 loc=- body='{"error":"this app is waiting for its first setup"}'
10:46:54 GET https://plex.enkisfelhom.hu/library/sections (Accept application/json) -> 401 loc=- body='{"error":"this app is waiting for its first setup"}'
10:46:54 GET https://plex.enkisfelhom.hu/:/prefs (Accept */*) -> 401 loc=- body='{"error":"this app is waiting for its first setup"}'
10:46:54 == (2) a stranger on the LAN path (traefik :443 on the guest)
## 2026-10-15 (afternoon) — the floor reached 0.313.0
| Row | What | Closed | Evidence |
|---|---|---|---|
| **R-1045** | **An unclaimed Plex answers everything without a token to a private address — and through the tunnel every request comes from one.** (P2) | CLOSED 2026-10-15 — its closing condition met: the global floor is 0.313.0 (operator 11:29), so every box that reports updates to a controller that asks Plex itself whether it is claimed; the setup gate closes the public path on every controller (`9a23ee5`) | `audits/media-build-2026-10-15/A-plex-gate/`, `G7/` |
## 2026-10-15 — the media build (controller v0.313.0)
| ID | Category | Sev | What | State | Blocked on | Next action | Owner |
|---|---|---|---|---|---|---|---|
| **R-1023** | Storage & devices | P2 | **Build: media apps see every drive, and the box says when they must restart (operator rulings 2026-10-14, `09` §3).** Scope from `architecture/15-media-library.md`: one bind per drive of `userdata/media/<library folders>` at the same path; the drive-absent gate per bind (today it keys on the app's one `HDD_PATH`, and the boot gate waits only for THAT drive — measured after the 17:25 power cut); the restart notice after a drive is added, removed or comes back, with „restart now” / „later” and a per-drive „apps restarted since” mark; removal, export and backup classes taught that a shared library is no app's own (R-1027, R-1019). **S5/S6 measured 2026-10-14:** a pulled drive stops none of the media apps (they key on another drive), they keep a dead view; on return nothing restarts them — so the restart notice MUST also cover „a drive came back”, the restart must be stop+start, and Sonarr/Radarr's own health page says nothing about an unreachable root folder, so the box must say it. **Night 2026-10-14 measured every event × app (`15` §10, `audits/media-drive-events-2026-10-14/`):** nothing is lost while a drive is gone (K1), so the build needs NO scan/unmonitor/torrent guard; it DOES need: (1) one bind per PRESENT drive only — a bind to an absent path makes the app unstartable (K3-4: 500 „error while creating mount source path … permission denied”); (2) the media apps recreated without the drive BEFORE a safe removal unmounts it (R-1044); (3) a returned drive restarts every container that binds it, FileBrowser included (K5: 2–10 s per app); (4) „restart now” cuts a playing stream (K5-4: cut, back in 22 s) — check Plex/Jellyfin sessions first or offer „later”; (5) the stale view is found with NO disk I/O: each container's `/proc/<pid>/mountinfo` shows the old device with `shutdown` (K4). Narrowed 2026-10-14 night. **BUILT 2026-10-15 — controller v0.313.0 + catalog `0188430` (`15` §11; `audits/media-build-2026-10-15/`): proven on Tester 1 (G1–G4, G6) and demo-hp 9201 (G7).** What is left is delivery. |**WAITING-ON-OPERATOR** — golden 0.313.0 baked; the vouch and the floor are the operator's (decision 247) | the operator's vouch of golden 0.313.0, then the floor | After the floor: read back that demo-felhom and Tester 1 report 0.313.0 through the normal path; then close | CC |
| **R-1023** | Storage & devices | P2 | **Build: media apps see every drive, and the box says when they must restart (operator rulings 2026-10-14, `09` §3).** Scope from `architecture/15-media-library.md`: one bind per drive of `userdata/media/<library folders>` at the same path; the drive-absent gate per bind (today it keys on the app's one `HDD_PATH`, and the boot gate waits only for THAT drive — measured after the 17:25 power cut); the restart notice after a drive is added, removed or comes back, with „restart now” / „later” and a per-drive „apps restarted since” mark; removal, export and backup classes taught that a shared library is no app's own (R-1027, R-1019). **S5/S6 measured 2026-10-14:** a pulled drive stops none of the media apps (they key on another drive), they keep a dead view; on return nothing restarts them — so the restart notice MUST also cover „a drive came back”, the restart must be stop+start, and Sonarr/Radarr's own health page says nothing about an unreachable root folder, so the box must say it. **Night 2026-10-14 measured every event × app (`15` §10, `audits/media-drive-events-2026-10-14/`):** nothing is lost while a drive is gone (K1), so the build needs NO scan/unmonitor/torrent guard; it DOES need: (1) one bind per PRESENT drive only — a bind to an absent path makes the app unstartable (K3-4: 500 „error while creating mount source path … permission denied”); (2) the media apps recreated without the drive BEFORE a safe removal unmounts it (R-1044); (3) a returned drive restarts every container that binds it, FileBrowser included (K5: 2–10 s per app); (4) „restart now” cuts a playing stream (K5-4: cut, back in 22 s) — check Plex/Jellyfin sessions first or offer „later”; (5) the stale view is found with NO disk I/O: each container's `/proc/<pid>/mountinfo` shows the old device with `shutdown` (K4). Narrowed 2026-10-14 night. **BUILT 2026-10-15 — controller v0.313.0 + catalog `0188430` (`15` §11; `audits/media-build-2026-10-15/`): proven on Tester 1 (G1–G4, G6) and demo-hp 9201 (G7).** What is left is delivery. **2026-10-15 11:29: the operator vouched golden 0.313.0 and raised the global floor to 0.313.0 (read back from the hub DB).** demo-felhom moved to 0.313.0 by itself through the floor; demo-hp was put back on 0.313.0 by hand (it ran the test tag rc.4 and has its own floor override 0.311.0); Tester 1 still runs rc.4, which the floor never moves (R-1053) — fenced in the 2026-10-15 afternoon task. | **NARROWED** — delivered by the floor; Tester 1 waits on R-1053's step | R-1053 (Tester 1 back on a release tag) | Put Tester 1 on the 0.313.0 tag (or let R-1053's fix move it); read it back; close | CC |
| **R-330** | Storage & devices | P3 | **Disk health Phase 2 — the three SMART attributes the wire does not carry.** The failing drive's most telling counter was **187 `Reported_Uncorrect`**, sitting at normalized **1** against threshold **0** with a raw count of **1001** — one point from failing and structurally unable to get there. Also wanted: **199 `UDMA_CRC_Error_Count`** (cabling) and **188 `Command_Timeout`**. None are on the agent→controller wire today, so v0.215.0's ladder could not use them. Phase 2 also persists periodic SMART **samples** (the right home is `metrics.MetricsStore`, NOT the Phase-1 state file, which is one record per disk and must stay that way). **This is a declared WIRE change, so under the G-1 gate the hub must model the new fields in the SAME session** — that is precisely why it was kept out of Phase 1, where it would have turned a one-word severity fix into a three-repo change | **READY** — **2026-10-12: the wire half is CLOSED in this row** — released in agent v0.150.0 (boxes run 0.154.0), controller and hub decode 187/188/199. **Only open work:** the verdict slice (use the three counters in the disk verdict and the household's words) and persisting samples. **READY (M) — NEW 2026-08-14****2026-10-06 night: the wire half BUILT on main (unreleased):** the agent sends SMART 187 `reported_uncorrect`, 188 `command_timeout` (raw, vendor-packed on some drives — carried as reported), 199 `udma_crc_errors` (pointer + omitempty: unknown is absent, never 0); the controller decodes them and the hub's host-report mirror models them (G-1 wire gate green; it convicted the agent half alone). **Carried only** — no verdict, banner, mail or alarm reads them (`TestR330_CountersChangeNoVerdictYet`); using them changes what a household is told and is the next slice, with persisting samples. Red-proofs `audits/night-burndown-2026-10-06/r330/`. Ships with agent v0.150.0 and the next controller and hub releases. | R-328 (closed) | The verdict slice: 187/188/199 into the disk verdict with tests; then persist samples | CC |
| **R-332** | Storage & devices | P3 | **The new Hiba-from-counters path has never fired on real hardware.** v0.215.0's whole point is a verdict the product could not previously reach, and it is proven only against the committed fixture's values in unit tests (12 scenario groups, 11 of 12 red-proofs failing as required). The live validation on demo-hp proved the **negative** — three healthy disks still read Rendben across the deploy, no false alert — and the **severity wire** end to end, but no live disk has actually reached Hiba. **This is the honest gap and it must not be closed by pointing at the fixture tests**: the drive that produced the fixture is in DooPlex, which is Tier 2 and never a drill target, and the demo boxes are all-flash and healthy | **WATCHING — NEW 2026-08-14, NARROWED same day.** One item originally in this gap is now PROVEN LIVE: the **persisted state surviving a controller restart**. The v0.215.0→v0.216.0 redeploy destroyed and rebuilt the container, and the new one read back a `changed_at` written by the PREVIOUS version (`2026-08-14T07:23:14.640216851Z`, still intact at 09:31:35Z) instead of re-baselining — Scenario L on real hardware, not just the production-path unit test. **What remains unproven is the verdict itself, plus the stronger restart half: an already-ALERTED disk not re-alerting** | a real degrading disk, or an injection harness | **Closing condition:** a live disk reaching Hiba from counters, OR a deliberate injection through the REAL pipeline (agent `/disks` → controller check → hub event), not a hand-set verdict | CC |
| **R-331** | Storage & devices | P4 | **Disk health Phase 3 — growth-rate detection, and retiring the static 64.** The v0.215.0 count backstop (64 unreadable sectors → Hiba) is **a judgement from ONE drive**: the observed benign excursion peaked at 16 and cleared inside an hour, and the terminal run passed 64 at 13 Aug 11:28 and never came back. It is deliberately a backstop BEHIND the sustain rule, not the primary signal, but it is still a magic number tuned on a single sample and it will be wrong for some drive. With Phase 2's history the box can ask the question that actually matters — *is this count climbing, and how fast* — which distinguishes a drive with eight stable aging sectors from one adding forty a day, something no static threshold can do. Revisit 64 when that exists | **READY (M) — NEW 2026-08-14** | R-330 | Growth-rate rule over persisted samples; re-derive or delete the static 64 | CC |
| ID | Category | Sev | What | State | Blocked on | Next action | Owner |
|---|---|---|---|---|---|---|---|
@@ -220,10 +220,9 @@ stopping line that lies.
| **R-779** | Security & access | P4 | **[P3-LOW] Part A's "two outside addresses seen as two" is proven through the simulated tunnel only; on the REAL tunnel the second outside address (ep0, one request allowed) was refused by Cloudflare's edge with 403 and never reached the box.** Measured 2026-10-01 19:51 UTC (`audits/visitors-2026-10-01/A/L2-demo-hp-real-tunnel.txt`): no log line on demo-hp; demo-hp's box has no geo restriction in its settings, so a Cloudflare ZONE rule (country or bot, not read) refused a German datacenter address. DooPlex's own address on the real tunnel was seen as itself. **Needs:** one sign-in from a second Hungarian address (the operator's phone off wifi) while DooPlex is locked out — 2 minutes; and say which Cloudflare rule refused ep0. | **WAITING-ON-OPERATOR — rank P3-LOW; owner: operator (a phone), CC reads the logs****Re-ranked 2026-10-03: P3→P4: a proof gap on the real tunnel; operator-only follow-up.** | — | — | CC + operator |
| **R-904** | Security & access | P4 | **Cloudflare can read every household's app traffic; replacing it with our own relay is a later item.** Facts (reviewer discussion 2026-10-08; `01`): app traffic and the dashboard reach the box through the Cloudflare Tunnel (`01` §5 trust table, rows end-user ↔ apps and customer ↔ controller UI; §7), and the tunnel's public end is Cloudflare's edge, where TLS ends — so Cloudflare can technically read that traffic (the FAQ says so since 2026-10-08, R-900; „TLS ends at the edge" is not written in `01` — add it there). What Cloudflare gives today, free: inbound reach with no router setup, the CGNAT answer (`01` §4, §7); certificates (`01` §7, the free tier covers one level below a zone); the geo-WAF the hub enforces (`01` §5 last row, §7); flood protection (not in `01`). The alternative named: our own EU relay over WireGuard with TLS passthrough by SNI, certificates on the box, the geo-block on the relay. Its costs: one more machine the operator keeps up, and a single point of reach for every box; weaker flood protection; about a week of work after a spike. Operator ruling 2026-10-08 09:07 (`09` §3 decision 184): a later item. | **DEFERRED** — **2026-10-10: the asked sentence is in `01` §7** („TLS ends at Cloudflare's edge …”); the spike stays deferred. **DEFERRED — after the first customers (operator ruling 2026-10-08 09:07)** | — | A spike after the first customers (the relay's reach, cost and flood behaviour, measured) | operator |
| **R-1040** | Security & access | P4 | **Until the household makes Sonarr's or Radarr's first admin, any container on the box can read the app's API key.** Measured on the bench 2026-10-14 with the R-1030 seed in place: `AuthenticationRequired=Enabled`, `AuthenticationMethod=None` — `/initialize.json` from another container answers 200 with `apiKey`. Outsiders are blocked by the box's setup gate in that window; a compromised other app is not. | **OPEN** — filed 2026-10-14 | — | Seed the API key at install (`SONARR__AUTH__APIKEY`, measured to work) so the box knows it, and close the window by telling the household in the first steps; or seed a Forms user | CC |
| **R-1045** | Security & access | P2 | **An unclaimed Plex answers everything without a token to a private address — and through the tunnel every request comes from one.** MEASURED 2026-10-14 night on a throwaway `plex-k6` on Tester 1 (`audits/media-drive-events-2026-10-14/``K6-1-unclaimed-plex.txt`): with no claim, `/library/sections`, `/:/prefs` and `/myplex/account` answer 200 with NO token from the Docker network. Through cloudflared, Plex sees cloudflared's private address, so a publicly routed unclaimed Plex would be open to anyone — server settings and every library. R-1028's late code leaves exactly such a server. NOT measured over the public path (the throwaway had no route). Harm if true: a household's media and Plex settings open to the internet. **NARROWED 2026-10-15 (Part A, `audits/media-build-2026-10-15/A-plex-gate/`):** the live catalog's Plex has `setup_gate: true` (app-catalog `9a23ee5`). Proven on demo-hp 9201 with an EXPIRED claim code: the server came up unclaimed and open on the docker network (`/library/sections`, `/:/prefs` 200 with no token), while a stranger over the public path got only the gate (HTML → 302 to the dashboard's `/__gate/start`; `/identity`, `/library/sections`, `/:/prefs` → 401 „waiting for its first setup”), the same on the LAN path; the household passed with its dashboard session and reached Plex. Removing the app removed the gate file 9 s later (the tick). **Left open:** the gate opens on the household's „Kész, beállítottam” even while Plex is still unclaimed (no probe: Plex answers XML to the probe's request) — closed by R-1028's build (the controller claims and opens on a real claim). **2026-10-15 (v0.313.0):** the controller asks Plex itself whether it is claimed — after the claim on the Plex page and before „Kész” may open the gate (`TestPlexDonePress_RefusedWhileUnclaimed`; G5 on 9201: no code, gated, „Kész” 409). Two background security reviews of the catalog pushes found the old-controller half: an optional code (fixed: required again, `after_start` for 0.313.0) and the remaining residual — on a controller before 0.313.0 a code that ran out leaves an unclaimed Plex that „Kész” can open. A status probe meant to close that OPENED an unclaimed Plex while Plex started (G7f; removed within the hour). The residual ends when the floor carries 0.313.0. | **WAITING-ON-OPERATOR** — closed by the floor at 0.313.0 | the floor at 0.313.0 | After the floor: close | CC |
| **R-1050** | Security & access | P4 | **Raise HSTS to one year on or after 2026-11-15, if no https trouble was reported.**`09` §3 decision 249 (D2 = A, operator 2026-10-15): HSTS stays `max-age=86400` now; after a month with no https trouble it becomes one year (`31536000`); never preload, never `includeSubDomains`. The value is `HSTSMaxAgeSeconds` in `felhom-controller/controller/internal/infra/infra.go`, pinned by `TestRenderForwardedHeaders_HTTPSOnly`. „Trouble” = any report of a name that will not open, or a box answering http after the floor. | **WATCHING** — filed 2026-10-15 | the date | On or after 2026-11-15: read the register and the operator's mail for https trouble since 2026-10-15; none → a controller release with one year (test updated), bake → vouch → floor | CC |
| ID | Category | Sev | What | State | Blocked on | Next action | Owner |
|---|---|---|---|---|---|---|---|
@@ -234,10 +233,12 @@ stopping line that lies.
| **R-972** | Box system & updates | P2 | **The shipped controller and hub programs are built with Go 1.24.13 and golang.org/x/crypto v0.31.0, and the agent with Go 1.26.0, so dozens of fixed Go security bugs (including html/template escaping fixes) are still in what runs on boxes and the hub.** Go 1.24 has had no security fixes since Go 1.26 was released (Go supports the two newest major releases: https://go.dev/doc/devel/release#policy); the scanners name fixes in Go 1.26.9 and 1.27.2. Also: the controller image's Debian 12 base has 12 fixable critical packages, and the Samba image has 48 fixable high ones. Fix: build all three on a supported, patched Go; update x/crypto; rebuild the base images. Scanner output: `audits/security-review-2026-10-12/scanners/`. | OPEN — measured on the shipped binaries | — | Bump the toolchain and bases in their own release task | CC |
| **R-862** | Box system & updates | P3 | **Tester 2 cannot take the config bundle until one by-hand bootstrap is done: its `felhom-os-apply` (agent 0.142.0) predates the bundle mode, and no signed job can write a root file on it.** FOUND 2026-10-04 (R-840 build, Part C): the route reaches every box installed from installer 1.31.0 on, and the demo boxes (bootstrapped by CC); Tester 2 has every root file of agent 0.142.0 (installer 1.30.0) and lacks only the R-858 wrapper fix, which matters only for a Docker step it gets solely from a signed job. CC has no route to Tester 2 (its door admits only the operator's WireGuard peer; `felhom-op` cannot become root). The steps: `runbooks/config-bundle.md` "Tester 2". Then CC signs the bundle and reads it back. | **WAITING-ON-OPERATOR** — the operator said (2026-10-04 ~19:05) he will try through his tunnel **2026-10-07 07:58 (`09` §3 decision 170): the operator believes Tester 2 never set up the off-site backup (no escrow); the laptop may stay off for days; the tester plans to add an HDD.****2026-10-07 (Part H, read only):** in Tester 2's last 10 notifications (2026-10-05 09:16 → 2026-10-07 06:58) the household got 0 mails, the operator 10 (host_down ×9, expected_dbdump_missed ×1); control: the household channel shows on the other three customers. So the household is not mailed daily while the box is off; no row (`audits/day-2026-10-07/H/`). | the operator's WireGuard tunnel | the operator runs the three bootstrap commands; CC sends the bundle | operator |
| **R-78** | Box system & updates | P3 | **`local_api` authority ruling — auto-reconcile vs detect-only** **MIGRATED FROM `ROADMAP.md` 2026-08-22 (R-369) — originally filed 2026-07-25, size M, roadmap state `idea (deferred OUT of R-77 on purpose)`.** Moved verbatim; nothing added or reinterpreted. The roadmap keeps its copy as history, marked moved. **An OWED OPERATOR DECISION, not a defect — moved because an owed ruling hidden among feature ideas is the shape this session exists to remove.** | **OPEN — migrated from ROADMAP 2026-08-22, rank unchanged** | — | R-77 ships detection because the fix is genuinely undecided, and **both directions can lose customer-visible function**. **Direction 1 (today):**`controller.yaml` wins and drift is silent → the 2026-07-25 island migration blinded the whole fleet's control plane for 17.5 h (drive gate, guest-reboot recovery, quiesce/backup all degrade). R-77 makes that loud but does not stop it recurring. **Direction 2 (`bootstrap.json` wins, auto-reconcile on boot):** a guest whose `controller.yaml` is CORRECT and whose `bootstrap.json` is stale — a half-completed re-provision, a hand-repaired guest, a setup-wizard box — gets a **working channel clobbered on the next restart**, fleet-wide and silently, during a routine deploy. That is not obviously better than the bug. Needs a spike: which writer is authoritative per field (endpoint vs fingerprint vs token — `mergeLocalAPI` replaces the whole block, so they cannot be reconciled independently today), whether the agent side should stamp a generation/mtime so 'newer wins' is even expressible, and whether reconcile should require an operator ack. Until then the drift alert plus a manual edit is the supported path. | CC |
| **R-468** | Box system & updates | P3 | **[P3-LOW] THE GOLDEN WAIVER — goldens on a cadence, not per release (operator ruling 2026-09-13).** 25 goldens in 26 days in August, almost one per release, because `golden_currency_gate.py` trips on every release by design and the only honest ways past it were a bake or a declared `--no-verify` (thirteen by 2026-09-01, R-404/R-417). **The ruling: bake WEEKLY, and always before any drill or fresh install.** Every release still raises the FLOOR, so both demo boxes keep getting each release in ~20 s; only the golden — which protects a fresh install and nothing else — moves to a cadence. **The mechanism (built 2026-09-13):**`documentation/tests/golden-waiver. **⚠ CORRECTED THE SAME DAY (R-472): between bakes the floor does NOT carry a release — the hub holds any floor above the vouched golden (publish-train rule 1), so releases between bakes reach the demo boxes only by hand-deploy.**yml`, four lines (`issued`, `expires`, `reason`, `register_row: R-468`), read by the gate. While valid, a golden BEHIND the record makes the gate print a loud ADVISORY and exit 0; when it expires the gate is red again until someone bakes or renews. **The 14-day cap is enforced by the gate, not the runbook** — a longer, undated, unparseable, reason-less or row-less waiver is INCONCLUSIVE (exit 2), never 0 and never silently ignored. **It never covers a golden that is UNRECORDED (R-385)** — that is not a cadence choice. **A dated waiver cannot be forgotten; it just expires** — the difference from R-242's original rule, which recurred the day after it was written. Tests: `scripts/test_golden_currency_gate.py` cases 5–15 (E/F/G/H, a 15-day, absent, unparseable, bad-row and empty-reason waiver each 2; the R-421 decoy — a file saying only `expires` — 2). **This is a PRE-CUSTOMER arrangement: the first external install retires it** (delete the file in that commit). Cadence written into `RUNBOOK-manual-build.md` §4.2 and the `felhom.eu` end-of-session checklist. **Does NOT touch R-242's open half (nothing gates the VOUCH).**| **WATCHING — rank P3-LOW; owner: CC (renew ≤ 14 days or bake); retire at the first external install** | — | — | CC |
| **R-468** | Box system & updates | P3 | **[P3-LOW] THE GOLDEN WAIVER — goldens on a cadence, not per release (operator ruling 2026-09-13).** 25 goldens in 26 days in August, almost one per release, because `golden_currency_gate.py` trips on every release by design and the only honest ways past it were a bake or a declared `--no-verify` (thirteen by 2026-09-01, R-404/R-417). **The ruling: bake WEEKLY, and always before any drill or fresh install.** Every release still raises the FLOOR, so both demo boxes keep getting each release in ~20 s; only the golden — which protects a fresh install and nothing else — moves to a cadence. **The mechanism (built 2026-09-13):**`documentation/tests/golden-waiver. **⚠ CORRECTED THE SAME DAY (R-472): between bakes the floor does NOT carry a release — the hub holds any floor above the vouched golden (publish-train rule 1), so releases between bakes reach the demo boxes only by hand-deploy.**yml`, four lines (`issued`, `expires`, `reason`, `register_row: R-468`), read by the gate. While valid, a golden BEHIND the record makes the gate print a loud ADVISORY and exit 0; when it expires the gate is red again until someone bakes or renews. **The 14-day cap is enforced by the gate, not the runbook** — a longer, undated, unparseable, reason-less or row-less waiver is INCONCLUSIVE (exit 2), never 0 and never silently ignored. **It never covers a golden that is UNRECORDED (R-385)** — that is not a cadence choice. **A dated waiver cannot be forgotten; it just expires** — the difference from R-242's original rule, which recurred the day after it was written. Tests: `scripts/test_golden_currency_gate.py` cases 5–15 (E/F/G/H, a 15-day, absent, unparseable, bad-row and empty-reason waiver each 2; the R-421 decoy — a file saying only `expires` — 2). **This is a PRE-CUSTOMER arrangement: the first external install retires it** (delete the file in that commit). Cadence written into `RUNBOOK-manual-build.md` §4.2 and the `felhom.eu` end-of-session checklist. **Does NOT touch R-242's open half (nothing gates the VOUCH).****2026-10-15 (`09` §3 decision 250, operator):** the cadence is now the RULE, not a waiver — a controller release needs no golden (the floor carries it), the golden is baked every Sunday night when the floor is above it (`runbooks/nightly-rotation.md`), and `golden_currency_gate.py` convicts only a golden MORE THAN 7 DAYS old while a release is ahead (CASE 18–21, red-proved). The waiver file stays as a dated exception for a missed Sunday. | **WATCHING** — the weekly bake is a night-routine step; the first external install still retires the waiver arrangement | — | — | CC |
| **R-1004** | Box system & updates | P4 | **After an unclean stop every guest ext4 volume waits ~43 s in multi-mount protection before it mounts, so each restart after a power cut takes ~1.5 min longer.** Kernel: „EXT4-fs warning (device dm-6): ext4_multi_mount_protect: MMP interval 42 higher than expected, please wait" on dm-6/dm-7 and loop0/loop1, demo-hp and Tester 1, every round of `audits/night-power-2026-10-13/` (R1 `repairs.txt`). | **READY** — filed 2026-10-14 | — | CC: find where MMP is switched on for the guest volumes (Proxmox `pct` create, or our own mkfs), whether it protects anything on a one-host disk, and measure the restart without it on a scratch guest | CC |
| **R-1013** | Box system & updates | P4 | **After a power cut the agent's start-up stale-lock pass logs an ERROR „pct unlock failed … no lock found" when Proxmox's own start-all removed the lock in the same second.** The outcome is the goal (no lock), so the line is a false alarm in the log. Seen on demo-hp 2026-10-14 12:07:44–45 UTC (`audits/power-cut-fixes-2026-10-14/E/after-boot.txt`; Proxmox: „removed left over backup lock from '9201'!"). Small, but the agent's release for this session is spent (v0.156.1). | **OPEN** — filed 2026-10-14 | — | CC: treat „no lock found" from `pct unlock` as already clear (INFO), with a test; ride the next agent release | CC |
| **R-1010** | Box system & updates | P3 | **The agent's stale-lock janitor cannot remove a half-removed `vzdump` snapshot: Proxmox's delete fails on the first missing volume („Failed to find logical volume pve/snap_vm-9201-disk-0_vzdump") and the janitor retries the same call every 10 min forever, leaving the other volume's snapshot and the snapshot entry.** Seen 2026-10-14 after a backup ran out of space on a full host root (LVM could not write its metadata; `audits/power-cut-fixes-2026-10-14/README.md` C4). Fixed by hand with `pct delsnapshot 9201 vzdump --force`. | **READY** — filed 2026-10-14 | — | CC: on that error, retry once with `--force` (a sudoers line or the API's `force=1`), with a test that replays the error text; next agent release | CC |
| **R-1052** | Box system & updates | P3 | **The new-box path is not proven: a fresh install from a golden older than the floor reaching the floor by itself at first boot.**`09` §3 decision 250 relies on it (the golden is weekly, releases ride the floor). Proof owed at the next controller release: a throwaway VM on demo-hp installed from the ISO (the vouched golden one release behind the floor); time from claim to the floor; the controller's self-update log lines; then the VM and its hub records removed. | **OPEN** — filed 2026-10-15 | the next controller release | Run it at the next release; quote the times | CC |
| **R-1053** | Box system & updates | P3 | **A box running a controller test tag (`0.313.0-rc.N`) is never moved by the floor, and says so only at DEBUG.**`util.ParseVersion` wants X.Y.Z, so `maybeAutoUpdate` logs `current … not parseable (dev?) — skip` and the box stays on the test build forever (measured 2026-10-15 on demo-hp and Tester 1 after the floor reached 0.313.0; demo-felhom, on a release tag, moved by itself). The same parser keeps an rc tag off the household's update button — that half is wanted. Today's guard is procedural (the `felhom-build-deploy` skill: write the release tag back after testing). Tester 1 still runs rc.4. | **READY** — filed 2026-10-15 | — | A controller fix: treat `X.Y.Z-rc.N` as a pre-release BELOW `X.Y.Z` for the floor (and keep it off the button), log the skip at WARN; a test per case. Meanwhile put Tester 1 on 0.313.0 | CC |
| **R-1049** | Monitoring & notifications | P3 | **A pulled drive mails the operator „unexpectedly disconnected” (error), and nothing ever mails that it came back.** MEASURED 2026-10-14 night (`audits/media-drive-events-2026-10-14/K8-hub-mails.txt`, the hub DB with its WAL from the running pod, and the mailbox as the second channel): two pulls → two `storage_disconnected` mails; the hub RECEIVED both `storage_reconnected` and both `health_recovered` (severity info) and logged no notification for any of them — they are not in `recoveredPairedDownTypes` (`hub/internal/notify/dispatcher.go`), so the severity gate drops them. The R-97a shape: told it broke, never told it healed — the operator cannot tell a returned drive from one still gone without opening the hub. **BUILT 2026-10-15 — hub v0.151.0 (`12552aca`): both pairs in `recoveredPairedDownTypes`, cooldowns ended by the next loss (`TestR1049_DriveBackMailsBothEdges`, red-proved). Not deployed: the operator's „yes” first (decision 162); the image build waits for it too.** | **WAITING-ON-OPERATOR** — hub v0.151.0 code pushed, not built, not deployed | the operator's „yes” to the hub deploy | On the „yes”: build + deploy hub v0.151.0; read a „came back” mail on the next pull | CC |
## Hub & operator — 12 rows (P3 7, P4 5)
## Hub & operator — 13 rows (P3 8, P4 5)
| ID | Category | Sev | What | State | Blocked on | Next action | Owner |
|---|---|---|---|---|---|---|---|
@@ -272,6 +273,7 @@ stopping line that lies.
| **R-844** | Hub & operator | P4 | **The household's OS-update line exists only on the hub's customer timeline.** 2026-10-04: the box itself has no event surface for agent results (the controller UI shows no timeline), so `os_update_applied` is a hub customer event (info: recorded, never mailed). Its stored text is the hub's English sentence; the hu/en bundle text (`mail.event.os_update_applied`) is used only if it is ever mailed. Fix direction: a controller-side line (the controller already polls the agent's local API) when the box gets a household timeline. `audits/os-guest-lane-2026-10-04/partG/hub-customer-timeline-demo-hp.txt` | **READY — owner: CC****2026-10-05 (burn-down night): NEEDS A DESIGN** — a household timeline on the box does not exist yet. | — | — | CC |
| **R-1002** | Hub & operator | P4 | **A box the household switched off reads ONLINE on the hub's hosts page for its first 45 minutes** (seen live 2026-10-13, R-998 proof, `audits/poweroff-2026-10-13/B-hub-watch.log`: the „Power: Kikapcsolva…" line at 18:53, the KIKAPCSOLVA badge only at 19:38, when the host report turned stale). The customer page read „warn" in part of that gap (seen, not captured). No alarm and no mail fired; the operator page is only slow to say it. | **READY** — filed 2026-10-13 | — | CC: let `hostDisplayStatus` read `deliberate_off_at` before staleness, with a render test per state; next hub release | CC |
| **R-1011** | Hub & operator | P3 | **The Day-0 „Agent" list offers step-bundle packages (`0.156.1-step1`, `0.152.0-step1` …) as agent versions; one has no agent binary, so the hub shows the bundle JSON's sha (`FileSHA256` falls back to the first file) and a vouch would point fresh installs at a package with no binary.** Seen 2026-10-14 (`/configuration`: two „0.156.1" entries, `ac83ea41…` is the step bundle). | **READY** — filed 2026-10-14 | — | CC: list only versions whose package holds the `felhom-agent` file (refuse a missing preferred file instead of falling back), with a test; next hub release | CC |
| **R-1051** | Hub & operator | P3 | **CC cannot raise the global floor without the operator's own password.**`09` §3 decision 250 lets CC raise the floor after the three test boxes, but the only route is the operator page (`POST /configuration/global-floor`, Basic auth with the operator password, `hub/internal/web/configs.go``handleSetGlobalFloor`); the hub's `/api/v1/` Bearer routes carry no floor. Read 2026-10-15 (no save made). The smallest grant: a hub route `POST /api/v1/operator/floor` (floor + declared MinAgent only, the same checks as the page) behind its own Bearer key stored out-of-band on DooPlex — or the operator's word that CC may use the operator password for this one save. | **WAITING-ON-OPERATOR** — filed 2026-10-15 | the operator's pick | On a scoped route: build it (a hub release, the operator's „yes"), prove a no-op save, write it into the `felhom-build-deploy` skill's release steps | operator |
@@ -41,7 +41,9 @@ it and do not bypass the hook for it.**
Two things still hold:
* A push that touches **code** is still refused — bake first or do not push code.
* A push that touches **code** is still refused — but since 2026-10-15 (`09` §3 decision 250) only when the newest
golden is **more than 7 days old** while a release is ahead of it; a release itself needs no golden. Bake the weekly
golden (Sunday, `nightly-rotation.md`) or do not push code.
* **Every other gate still refuses every push.** If a push is refused during a drill it is NOT the
golden gate, and the message will name which gate it was.
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.