Backlog triage Part B: 125 finished rows + 20 id-less rows moved to CLOSED-ITEMS (full text at 9e2786c); open rows normalised to one 6-column shape; narratives archived verbatim; closed_register_gate RULE 3 refuses a finished row in OPEN-ITEMS (decoys, seen red); rules rehomed to CONTEXT + 07 §11; loose notes triaged; R-814..R-819 filed; register 444 -> 325
gates / gates (push) Successful in 30s

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-03 09:17:22 +02:00
parent 9e2786c907
commit 71b8c8c62b
22 changed files with 1080 additions and 815 deletions
+10 -5
View File
@@ -394,8 +394,9 @@ or **what is open changed**), update **all four** in the SAME session:
> later by an overnight drill that planted files and watched them not come back**, and shipped as
> R-354. The work was right the first time; only the filing was missing.
**Report which `OPEN-ITEMS.md` rows the task opened, closed or re-ranked** (§15). Every row carries
an owner — a row nobody owns is how items got lost in the first place.
**Report which `OPEN-ITEMS.md` rows the task opened, closed (and moved to `CLOSED-ITEMS.md`), narrowed
or re-ranked** (§15). Every row carries an owner — a row nobody owns is how items got lost in the first
place.
### N.6 Website version bump (if controller/hub version is shown on the site).
@@ -406,10 +407,14 @@ over half of it finished work, one entry at 16 KB. **A file that cannot be read
be checked**, and this project has paid for that twice — a record nobody could find because it sat
inside an entry about something else, and a finding rediscovered because nobody could see it.
1. **Compress what this session closed.** A closed row keeps its title, the version it shipped in, its
evidence paths, and any sentence stating a rule. Everything else goes, and it moves to
`backlog/CLOSED-ITEMS.md`. **Nothing is deleted:** the compressed entry names the commit whose
1. **Move what this session closed — IN THE SAME COMMIT THAT CLOSES IT.** A closed row keeps its title, the
version it shipped in, its evidence paths, and any sentence stating a rule. Everything else goes, and it
moves to `backlog/CLOSED-ITEMS.md`. **Nothing is deleted:** the compressed entry names the commit whose
`git show` returns the full original text. **Open rows are not touched — their detail is doing a job.**
**This is a gate, not a habit (2026-10-03):** `scripts/closed_register_gate.py` RULE 3 refuses a push
whose `OPEN-ITEMS.md` holds a row LEADING with a finished word (CLOSED, SHIPPED, FIXED, DECIDED, …).
The rule existed from 2026-08-22 without a gate and was followed only sometimes: on 2026-10-03 the open
register held 113 finished rows, a quarter of the file.
2. **Rehome live reasoning before compressing it away.** If a closed entry carries the reason a rule
exists or a fence sits where it does, that reasoning moves — to `CONTEXT.md` if it is a decision,
to the owning `architecture/*.md` if it is a shape. **Where it is a decision, mark the resulting
@@ -1381,6 +1381,20 @@ to now *implement* D5 remains an open scheduling decision, not a blocked one.
---
**Decided, and deliberately not built — carried here 2026-10-03 when their register rows moved to
`backlog/CLOSED-ITEMS.md`** (the reasoning is in `CONTEXT.md`, "Rules carried out of rows closed 2026-10-03"):
- **[DESIGN] No automatic abandon on a date (R-245, decided 2026-08-07).** A customer who never decides is not
auto-abandoned after 30 days. An automatic ending, if ever built, triggers on the HARM (quota — old history
blocking new backups), with a dated warning, never on a date alone. Reopens on quota.
- **[DESIGN] `markOrphaned` keeps no guard against an active abandon countdown (R-303, decided 2026-08-13).** The
co-render is made harmless, not impossible; suppressing the orphan card during a countdown would hide a real
second fault. Reopens on a real-world sighting.
- **[DESIGN] No in-product route from the recovery screen to a set-aside store (R-312, decided 2026-08-13).**
Retention is an operator-only capability, and nothing anywhere may promise the customer can perform it
themselves. Re-evaluate on a real customer request. Its fixture, `demo-felhom`'s unrecoverable set-aside store
(R-313), is kept until R-312 ships or is abandoned.
## 12. Evidence index
| Claim | Grade | Source |
@@ -0,0 +1,361 @@
# OPEN-ITEMS — the narrative sections, as they stood on 2026-10-03
> **History, not a register.** These sections sat between the register tables of
> `documentation/backlog/OPEN-ITEMS.md` until the 2026-10-03 triage. They are moved here word for word
> (`git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` holds them in place). Every row they discuss is in
> `OPEN-ITEMS.md` (open) or `CLOSED-ITEMS.md` (finished). A status word below is the status ON THE DATE OF
> ITS SECTION and may be stale — the registers are the authority. The ranking paragraphs ("Why the TOP
> READY rows rank this way", "The 2026-08-02 intake, ranked") are superseded by
> `documentation/audits/backlog-triage-2026-10-03/RECOMMENDATION.md`.
## Operator rulings — 2026-08-04
Recorded here because a ruling that lives only in a conversation binds nobody (the R-96 standing rule).
1. **Run the recovery drill, after R-198.** R-198 shipped in hub v0.93.0; **the drill is the next
session.** Design: `audits/RECON-offsite-dr-chain-2026-08-04.md` §10 — demo-hp, ~3–4 h, a recovery
code created and KEPT, a sentinel file, wipe, reinstall, recover, and **pass = a byte-identical
sha256, not "the repository opened"**. → R-201
2. **Delete the orphaned ciphertext** (~1.2 GB across the two demo boxes, in set-aside restic stores
nothing prunes). **STILL OWED — not done in v0.93.0.** It is a destructive act on a protected
endpoint and belongs to a session that is scoped for it, not to a release that ships a schema
change. → R-193
3. **Accept the risk on R-193 candidate (c)** — no repository password retained on the Proxmox host.
**This is what makes R-198 load-bearing rather than tidy:** with no host-retained copy, the
customer-present recovery path is the ONLY way back from a rebuild, and that path runs entirely
through the retained identity blob. → R-193, R-199, R-200, R-201
~~**Still open and untouched by v0.93.0:** R-199, R-200, R-201.~~ **UPDATED 2026-08-04 (evening), after
hub v0.94.0 + agent v0.125.0 + controller v0.195.0:**
- **R-199 — CLOSED, proven on hardware.** Chain links 6–8 are assembled and walked. The offsite
repository password came back out of the sealed bundle **byte-identical** to the one on disk
(`c60c8bc737a6…` from three independent sources: the box's file, the recovered bundle, and the hash
the hub already stored).
- **R-200 — the plumbing half shipped**, the customer-facing form did not, deliberately.
- **R-201 — PASSED 2026-08-04 (night run).** A customer's file survived a machine rebuild and came
back byte-identical, through the customer's own restore flow. **It passed only because a person was
there:** four manual interventions stood between the recovered key and the restored file, none of
them in any design document → R-204.
- **R-202 — untouched.** The orphan card still promises recoverability unconditionally.
- **The orphaned ciphertext deletion (~1.2 GB) is STILL OWED** — ruling 2, above.
**UPDATED 2026-08-05, after controller v0.198.0 + hub v0.95.0 (R-204):**
- **R-204 items 1–3 — CLOSED.** The reset code works without a restart; a re-issue no longer marks a
healthy escrow stale (→ **R-196 CLOSED**); a unit restore states what it did NOT restore.
- **R-204 item 4 — OPEN and unstarted:** a rebuilt box cannot obtain an off-site credential unaided.
It needs an operator ruling on the one-shot credential design → **R-193**.
- **Still open and untouched by this session, stated so nothing is presumed closed by association:**
**R-202** (the orphan card's unconditional promise), **the ~1.2 GB orphaned-ciphertext deletion**
(ruling 2 — still owed, still needs its own scoped session), and **R-198's retention, which remains
UNIT-PROVEN ONLY** — nothing has superseded a key in production, and proving it needs a SECOND
deliberate wipe. That retention drill is the next item, and it is not this session's.
v0.93.0 made the key survive. v0.94.0/v0.125.0/v0.195.0 make it come back. v0.197.0 got the file into
the snapshot and the drill got it out again. **v0.198.0/v0.95.0 remove three of the four crutches the
drill needed — the fourth is R-193, and until it goes the recovery is still operator-assisted.**
## R-201 — THE RE-WALK, 2026-08-06 (attended)
**The question was asked a second time, on the fixed build, on a brand-new appliance built from the
published ISO. The answer is still no — but it is a nearer no.**
| half | verdict |
|---|---|
| **the data** | **PASS** — all three sentinels byte-identical, including a 12 MB binary and an accented Hungarian filename **whose NAME BYTES are also byte-identical** (verified as hex, not as rendered text). Restored in **23 s** out of the pre-destruction snapshot `a7bc23bd`, through the customer's own two-step full-restore flow |
| **the journey** | **FAIL — two dead ends**, against Phase 1's four. One needed a command line **inside the guest**, one a **Proxmox-host** action |
**RTO: the unaided figure is STILL UNDEFINED**, because the unaided journey still does not complete.
Attended: login 11:42:22 → key placed **+45 s** → tier up **+24 m 12 s** (after intervention 1) →
all three sentinels restored and verified **+30 m 13 s** (after intervention 2). **The 30 m figure must
not be quoted as the customer number.** The only segment that reflects the product working alone is
**23 seconds** to pull 12.8 MB back once everything was in place.
**The two dead ends:** **R-218's consume half** (row corrected above) and **R-220** (drives
unenrollable after a rebuild — reproduced and red-proved again; without it no app can be redeployed,
and without a redeployed app the restore page is empty, which is R-213's territory and follows from
R-220 rather than being separate).
**What PASSED and is worth keeping:** the recovery screen **appeared without being sought**
(`/` → `/launcher` → `/recovery`); it answered all three of its questions and its seal date matched the
hub's `created_at` exactly; the emailed reset code worked **first try**; the unlock took **1.528 s** —
a real unseal — and placed the key; and **R-225's fix was seen working in the wild** (the store read
„a pillanatképek száma még ismeretlen" rather than a false zero).
**R-216 part 4 reproduced live:** the reinstall **downgraded the agent 0.126.0 → 0.125.0**, back to the
vouched version — an operator's hand-fix undone by the very event that makes recovery necessary.
⚠ **THE DELIVERY GAP, and it is owed.** A fresh install landed on controller **0.201.0** / agent
**0.125.0** — the vouched versions, **neither carrying the fixes**. They were installed **by hand**.
Fleet delivery needs a golden carrying 0.202.0 **and** a vouched agent 0.126.0. **Nothing was vouched;
that is the operator's act.** **This re-walk proves the JOURNEY on the fixed build; it does NOT prove a
customer would receive that build.**
Evidence: `tests/rewalk-r201-2026-08-06/journal.md`.
## CAMPAIGN 11 — the recovery journey, 2026-08-05
**The whole journey was walked end to end for the first time, on a throwaway appliance built from the
published ISO. The data came back byte-identical; the journey did not exist.** **Ten findings from
Phases 1 and 3, R-214 … R-223** (seven fixed in controller **v0.201.0** + hub **v0.97.0/0.97.1**;
**three deliberately still open**, each blocking a real flow), **plus five from Phase 2's injected
faults, R-224 … R-228.** Evidence: `tests/campaign11-evidence-2026-08-05/journal.md` (Phases 0/1/3)
and `journal-phase24.md` (Phases 2/4). Campaign document:
`audits/CAMPAIGN-11-recovery-journey-2026-08-05.md`.
> **Phase 2's verdict in one line.** The cryptography, the retention and the transport all work and
> are now proven live. **What fails is being told the truth:** a mistyped code, a hub outage, a
> stopped agent and a correct code for a retained earlier package all produce **one** message, and
> three of the four are wrong.
### Phase 2 — the injected faults, 2026-08-05/06 (unattended)
> **ALL FIVE CLOSED 2026-08-06** in controller **v0.202.0** + agent **v0.126.0**. The rule they now
> enforce, stated so it outlives them: **on the unlock path the customer is blamed only after a real
> attempt REFUSED their code; every other outcome, including an unclassifiable one, says something
> else.**
>
> **STILL OPEN AND DELIBERATELY UNTOUCHED BY THAT WORK — said explicitly rather than left to
> inference:** **R-214** (the console never stops showing a stale pairing code), **R-220** (a rebuilt
> box's drives cannot be re-enrolled — *currently worked around BY HAND on the campaign venue*, which
> is the only reason an app could be deployed there at all), **R-221** (a rebuilt box cannot run the
> escrow ceremony), **R-213** (putting files back), **R-202** (the orphan card's unconditional
> promise — now the **last** place on that surface still promising recoverability, two doors from
> where R-228 removed the same promise).
Eleven faults, each judged on **the message, not the outcome**, each with a positive control proving
the fault was real. Full observables: `tests/campaign11-evidence-2026-08-05/journal-phase24.md`.
## Instruction files — deferred half, 2026-08-06
**Recorded against existing rows by Phase 2:**
- **R-216 — §4.1 is now MEASURED, not deduced.** The previous session could only offer two absences.
The box's own `/settings` renders „Minimális verzió (üzemeltető) **0.200.0**" (`GetFloor()`, whose
only writer is the report-ACK handler; **both hold branches serve `Floor=""`**, pinned by
`managed_floor_test.go:94`), and a **cold-started** controller logs
`settle-gate: GO — at/above floor 0.200.0 (we are 0.201.0)` against the same line reading
`floor still unknown after 1m30s` while the hold was in force. The hub's HELD lines ran every
15 min to 22:12:06 and stopped, with a liveness control proving the hub kept logging. **The floor
is served.**
- **⚠ A correction to how that positive was to be taken.** `SetFloor`'s line is `u.dbg(...)`, gated on
`cfg.Logging.Level == "debug"` and written to the **logger** — it can **never** reach the logx debug
ring, so it cannot appear in `/api/debug/logs` at any level. A controller restart alone would not
have produced it. Confirmed with a level census on the ring first (1196 DEBUG / 2802 INFO / 2 WARN),
so the absence was known to be structural rather than evidential.
- **R-218 — the live half is STILL NOT MEASURED, deliberately.** The fix is present and correct
(`needsOffsiteCredential` now retires on the **target**, not the key), but the venue **has** a
target, so the box correctly does not declare; declaring here would be the bug. The state that
exercises it is shape (a), which the venue no longer holds. **Recorded as not measured rather than
inferred from the unit test.**
- **R-217 — its fix HELD under exactly its fault** (F5): with the store blocked after a successful
unlock, the page rendered M3 and **no listing block at all**; the three false-claim strings are
absent, verified in UTF-8 with accented positive controls present.
- **R-215 — its fix is present** and the `GET /recovery` gate consults the same predicate as the POST
sibling.
- **R-199's back-pointer in `architecture/00-capability-map.md` was already added** — the brief lists
it as owed; it is present on the escrow-recovery row, explicitly labelled as the omitted
back-pointer. **No action taken; the brief's assumption was stale.**
**Untouched by this session, stated so nothing is presumed closed by association:** **R-213** (putting
files back — the half the recovery screen deliberately does not do) and **R-202** (the orphan card's
unconditional promise, which CAMPAIGN-11 §7 step 7 measured the customer-facing cost of).
## CAMPAIGN 12 — the class sweep, 2026-08-08 (unattended)
Eight rows, **grouped by class so the classes are visible as classes**. Full method, controls, blind
spots and the Part-4 gating ranking: `audits/CAMPAIGN-12-class-sweep-2026-08-08.md`. Gating candidates
are in `ROADMAP.md`, not here. **C1 produced no new instance** and has no row, deliberately.
**⚠ A correction the campaign owed to its own brief:** the task described C5's `escrow_stale` instance
as "closed individually". **It is not — it is R-247, `READY`.** The live repo is the source.
## CAMPAIGN 12 follow-through — G-1 built, R-260 and R-247 closed, 2026-08-08
**The gate was built BEFORE the fixes and was seen failing on 40 fields**, captured verbatim in
`documentation/tests/wire-contract-gate-2026-08-08/BEFORE.md`. That order was the method, not
bureaucracy: the night before, `deadcode` was rejected for class C6 precisely because it was made to
prove itself first and found neither of the two defects it was meant for.
**⚠ A COUNT THIS SESSION'S OWN PROMPT GOT WRONG, corrected against the repo rather than quoted.** The
prompt said *"465 emitted tags, eight unreachable"*. R-260's wording was "at least eight
DECISION-BEARING facts", never eight tags in total, and its own census already listed more. Measured
on the three declared wires: **210 tags checked, 51 skipped, 40 convicted.** Two prompt claims were
wrong this week and both were caught the same way.
**Two things the gate's CONTROL caught before it was trusted**, each a defect in the instrument:
1. **A substring false negative.** `grep -F healed_at` also matches `privsep_healed_at`, so a
genuinely dropped field read as received. R-260 named `healed_at`, so its absence from the output
was the tell. Now a whole-token regex.
2. **`dr_recipe` is not wholly opaque.** The hub stores each half as `json.RawMessage` and re-emits
nested shapes verbatim — but the TOP-LEVEL section keys are decoded by `hostHalfShape` /
`appHalfShape`, and **those are allow-lists**: a section an emitter adds is silently dropped until
named in both. That already cost `offsite_restic` (R-122). The gate is therefore opaque BELOW
depth 1, so the sections are checked; treating the whole subtree as opaque would have put R-122's
shape back outside its reach.
**THE FORTY, BY DISPOSITION.** Full per-field reasons live in the gate's own `ALLOWLIST`, where each
entry is a claim someone can re-check.
| # | field(s) | direction | decision | what changed |
|---|---|---|---|---|
| 1 | `oob.operator_key_configured` | agent → hub | **RECEIVE AND ACT ON IT** | decoded as a pointer; `oobDegraded` now fails when the key is absent, and the alert NAMES it. Hub v0.99.0 |
| 2 | `oob.wg_handshake_age_s`, `oob.healed_at` | agent → hub | **RECEIVE, for the message only** | decoded into `HostOOBRow` and put in the event payload; deliberately NOT in the predicate — widening the check beyond the fact that is now arriving is how a check stops being read |
| 3 | `escrow_stale` | hub → controller | **RECEIVE AND ACT ON IT** | `report.EscrowStatus.Stale`; the box tells a withheld hash from a hash-less one. Controller v0.209.0. **This is R-247** |
| 4 | `host.{cpu_temp_c,loadavg,memory_total_bytes,memory_used_bytes,uptime_seconds}`, `system.{load_avg_1,5,15, memory_total_mb, memory_used_mb, temperature_celsius, uptime_seconds}` | agent/controller → hub | **NO CONSUMER WANTED — redundant** | allowlisted: the hub decodes `cpu_percent` / `memory_percent` / `disk_percent` from the same stanzas and every threshold is expressed on those |
| 5 | `guests.spec.{disk_bytes,memory_bytes}` | agent → hub | **NO CONSUMER WANTED — redundant** | allowlisted: guest sizing is hub-owned INTENT (the manifest), not mirrored reality |
| 6 | `storage_targets.smart.model_name` | agent → hub | **NO CONSUMER WANTED — redundant** | allowlisted: a display label with no threshold on it; `smart.health` and every counter the hub bands on ARE decoded |
| 7 | `wireguard.last_handshake_age_s` | agent → hub | **NO CONSUMER WANTED — redundant** | allowlisted: wgsync reconciles from its own state, and the OOB path's own handshake age is now decoded |
| 8 | `guest_net` + its 7 children, `selfupdate_pending(_version)`, `mgmt_plane.healed_recently`, `pbs_dr.applied_at`, `restore_tests.mount_{parity,inventory}`, `config_hash`, `reporting_disabled`, `stacks`, `storage.migrated_to`, `backup.last_db_dump`, `backup.last_integrity_check` | agent/controller → hub | **NO CONSUMER TODAY, AND ONE IS ARGUABLY OWED** | allowlisted **against R-264, which stays OPEN**. Allowlisting is not deciding, and the entries say so |
**A C7 instance found while doing it, and corrected.** `HostReport.SelfUpdatePending`'s own comment
claimed *"The hub reads an absent field as pending=false, the correct default."* The hub has no field
for it and reads nothing either way. Comment corrected in the agent (no version bump — comment only).
**The hub's OOB test fixture was part of why this survived.** `oobReport()` omitted
`operator_key_configured` entirely, so every pre-existing scenario ran against a report shape **no
released agent produces**. Fixed, and the new tests drive the raw JSON decode boundary — a test that
builds the receiving struct by hand cannot see a field that never decodes, which is the whole class.
**Explicitly still open, untouched by this session:** R-246 (the wrong stale flag on `demo-hp` —
clearing it is an operator act hub-side), R-255, R-256, R-257, R-258, R-259, R-261, R-262, R-263, and
**C7's test-comment half**, which Campaign 12 recorded as *owed, not done* (60 of 2652 production
invariant comments sampled; none of the 1440 test comments).
## The seed that never ran twice, and three pictures that were not true — 2026-08-08
Four defects of one family: something the box already knows, either thrown away or drawn as its
opposite. Agent **v0.128.0**, controller **v0.210.0**, `gates.yml` (no hub change, no hub bump).
**R-221's writer, ESTABLISHED at `file:line` rather than assumed** — the prompt asked for this and it
was owed. `step_agent_config` (`felhom.eu/scripts/felhom-host-install.sh:2396`) renders `agent.json`
from `base = {}` unless an explicit `--preserve-from` is passed (flag `:1246`, defaulting empty at
`:256`), and writes it with `O_TRUNC` (`:2579`). **The render never writes an `escrow` section at
all** — grep over the whole heredoc returns zero hits. The pbsdr marker lives host-side
(`<agent-state>/pbsdr/marker.json`) and survives. So a rebuild keeps the marker and takes the key:
same descriptor, same hash, early return, seed never re-runs. **The attribution in R-221 was
correct.** A rebuild is nonetheless only the case that was measured — the same hole opens for a
hand-edited or restored config, which is the honest reason the fix is at the seam and not in the
installer.
**The idempotent early return was KEPT**, and that is load-bearing: it stops a converged box
re-running Proxmox operations every 60 s.
`TestSeedReasserted_OnConvergedTick_WithZeroProxmoxCalls` asserts **zero** recorded runner calls on
that tick, so a "fix" that simply deleted the return fails the test. Verified by mutation.
**The §7.3 truth table as implemented** (R-258):
| this app's own most recent dump result | restore point | verdict |
| any of its databases failed | yes | `error` — cross |
| all clean | yes | `ok` — tick |
| none recorded (no database / no run yet) | yes | **no icon**, time only, title „Erről a mentésről nincs eredményünk." |
| any | no | no tier-1 row at all, unchanged |
**An existing test was asserting the defect and was corrected, not deleted.**
`TestBuildAppBackupRows_Tier1FromRestorePoints` expected `"ok"` for a `FullBackupStatus` with **no
`LastDBDump` at all** — a green tick derived from nothing but a file's existence, i.e. Scenario G.
Its real subject, the `Tier1LastRun` time, is unchanged.
**The convention is now ruled** (§7.2, `CONTEXT.md` S-39): a `…Known bool` companion beside the
figures. `ROADMAP.md` G-3 was blocked on that decision and is unblocked.
**Six red-proofs, every one demonstrated failing and restored, each with the mutation asserted
applied.** The one that matters: Scenario A **fails against today's tree** with the intended message
— so the test tests the defect.
**Explicitly still open, untouched by this session:** R-246, R-255, R-256, R-257, R-261, R-262,
R-263, **R-264** (the twenty-one undecided facts — a design session of its own), R-240, R-243,
R-202, R-213, R-244, R-214/R-235, and **C7's test-comment half**, which Campaign 12 recorded as
*owed, not done*. **G-8's other half** (a hub-side check that notices a *vouch* has been forgotten)
was deliberately not built: it is hub work whose payoff is a daily email, and this session already
ends with a bake-and-vouch cycle in front of the operator.
## Why the TOP READY rows rank this way
This covers the next few only — it is deliberately **not** a full ordering of the table above, so that
there is one ranking to maintain rather than two.
1. **R-95** — the largest *data* exposure: the tier holding the customer's documents and photos is the
one whose credential can delete. **THIS PARAGRAPH WAS STALE UNTIL 2026-09-01 AND ITS OLD TEXT IS
NAMED SO THE CORRECTION IS NOT MISTAKEN FOR A RE-RANK.** It said the snapshot mitigation was
*"armed (daily 00:00, keep 7), but it has taken zero snapshots so far"*. **Both halves were wrong:**
seven daily snapshots do exist (R-429), and the word "armed" was withdrawn by the spike that same
day. **What is true now:** the snapshots exist and the box cannot write into them, but **no account
we hold can read anything out of one** (R-433, measured — 777,600 names, zero hits, controlled), so
they do not yet bound this exposure. The root cause is untouched either way — the box can still
`forget --prune` its own repo, from two call sites. **The ORDER of this list is unchanged and is
Viktor's**; only the facts under item 1 were corrected.
2. **R-94** — **de-ranked 2026-07-29.** The prior rationale ("until it moves every hub-driven
install gets the pre-R-82 default") was false: the constant selects no script and every install
already fetches 1.22.0. What remains is a wrong label plus two pieces of dead safety equipment —
a drift gate nobody runs and a test that compares a constant to itself. Cheap and worth doing;
not high-consequence, and it blocks nothing.
3. ~~**R-86**~~ — **CLOSED 2026-08-03**, agent v0.121.0 + hub v0.91.0, proven live on demo-felhom.
4. **R-87** — **SPIKED 2026-08-31 and now a DECISION, not work.** It also spent 2026-08-22..31 in
`CLOSED-ITEMS.md` by mistake while this paragraph ranked it fourth and pointed at nothing
(R-405). The spike measured it rather than designing it: a scratch restore of all 8 apps costs
25 s against the 40.3 s weekly check, but it would have caught ONE of the five drill-found
restore defects. **Recommendation: build the NARROW version — prove the snapshot still
CONTAINS a recoverable unit — or close the row.** Viktor's call; see
`audits/SPIKE-restic-restore-test-2026-08-31.md`. *The 2026-08-03 rationale, kept:*
R-86 built most of what it was waiting for (per-archive due-ness, a
proof that names its archive, and a staleness window that learns a tier's rhythm). What is left is
restic-specific — there is no scratch-guest analogue — so it still needs its own design, but it is
no longer waiting on a scheduling model that did not exist.
5. ~~**R-185**~~ — **CLOSED 2026-08-03**, agent v0.123.0 + installer 1.24.0, proven live on both demo
boxes. The silence was fixed as well as the grant: the box now asks whether it may READ each tier
it depends on, because an empty listing cannot distinguish forbidden from newborn.
6. ~~**R-189**~~ — **CLOSED 2026-08-03** with **R-188** and **R-186**, agent v0.122.0. The three
reporting/release signals that misreported their own work are fixed; **R-185 is the one that
remains open from that group** and is untouched by this — it is a missing storage ACL on
demo-felhom, not a reporting defect.
7. **R-110** — last **because it is not a READY row**: the ruling is the operator's, not CC's, and
there is nothing for CC to build until it lands. Ranked here rather than omitted because it is the
only item on this page about the *publish channel* of the most privileged artifact Felhom ships,
and today's exposure is zero — which makes now the cheapest moment it will ever be to decide.
### The 2026-08-02 intake (R-156 … R-164), ranked
Filed in one pass from Campaign 10, its two spikes, and the 53-template catalog persistence sweep.
**R-156 and R-157 had lived only in audit documents** — the identical *"minted in a spike doc and
never carried across"* failure the register already records for R-153/R-154/R-155, caught by the
sweep's own §8.0 while it was happening. R-158 was minted by a second session on the same day for an
unrelated finding, which is why the sweep's proposals were renumbered to R-159…R-162 at filing time.
1. **R-157** — highest: a `deployed: true` app can stay **down indefinitely** after a power cut or
hard reset, and in mechanism **B** nothing reports it on any channel (`0 currently down`). It is the
only row here where the customer loses service and has no signal at all.
2. **R-156** — the class is now detectable and two of three apps are fixed; what remains is **papra's
referral**, one app, well understood. *(Promoted 2026-08-02: R-161 was ranked here because nothing
ran the gate; it now has a mandated entry point, so R-156's residue is the larger remaining item.)*
4. **R-163** — a real ceiling that silently caps local backup once an app outgrows `mp1`, and it gates
Tier-2 and Tier-3 as well. Ranked below the above only because **overflow itself is safe today** —
it refuses per app and preserves the last good unit byte-identical. **RE-FRAMED 2026-08-02:** no
longer waiting on a ratio — decision **D-a** merges `mp1` away, so the row is now the record of the
constraint and the work moves to **R-165** (with **R-167** shipping in the same step). R-165 inherits
this rank; it is the highest-ranked item that must land **before any external install**.
5. **R-158** — the gap that makes R-163 dangerous: cross the size line and **one page** tells you. On
its own it is a notification gap, not a silent failure, which is why it sits here and not higher.
6. **R-164** — blocked on a predicate, no customer impact today; it only becomes urgent if the unit
size in R-163 is judged unacceptable, since the tar-drop is the cheapest way to halve it.
7. **R-161** — **de-ranked 2026-08-02, ruled and shipped at reduced scope.** The gate now has one
mandated entry point (`catalog_gates.py`), which is the shape that actually gets run here. What is
left is the automatic half, and that is sufficient while **one** person touches templates — so it
ranks low by design, not by neglect. Revisit when a second does.
8. **R-162** — `WATCHING` only. A limitation that fails closed; revisit if a non-overlay driver ships.
**R-159 and R-160 are SHIPPED** and are not ranked; they are filed to record the class, and R-159's
class (an image `VOLUME` at an unmounted path) is still live — `immich-server` has one today.
<!-- RESTORED 2026-08-22: these rows are NOT fully closed and were moved to CLOSED-ITEMS.md in
error by this session's compressor, whose status regex matched the word CLOSED inside
'PARTLY CLOSED' and the word FIXED inside 'OPEN - NOT FIXED'. They are restored VERBATIM
from commit fddfe00ce268, not from the compressed form: an open row keeps its detail. -->
<!-- ── MIGRATED FROM ROADMAP.md, 2026-08-22 (R-369 ruling: ONE register) ──────────────────
16 rows moved: every ROADMAP row that asserted something checkable about the shipped
product, plus one owed operator decision. Ideas and proposals stayed in ROADMAP, which
is their home. Each row below keeps its ORIGINAL identifier and filing date — the age is
the point. The roadmap keeps its copy as history, marked moved, with a pointer here. -->
@@ -5,7 +5,7 @@
Source findings: `DRILL-day0-cleanroom-2026-07-03.md` §9 **B5** (golden bakes a pre-floor controller →
mandatory manual D.1b on every fresh install) and **B1** (controller-bootstrap only fires at boot; the
hot-plugged config mount needed the installer's reboot crutch). Also closes the stale backlog note
`documentation/backlog/FOLLOWUP-golden-default-controller-tag.md`.
`documentation/archive/FOLLOWUP-golden-default-controller-tag.md` (moved from `backlog/` 2026-10-03).
**Verdict (short):** both findings are FIXED in the product. A fresh Day-0 install now lands controller
**0.98.3** on first boot and self-manages from there (no D.1b), and the baked
+160
View File
@@ -26,6 +26,166 @@
---
## 2026-10-03 — the triage: finished rows moved out of the open register
> Every row below sat in `OPEN-ITEMS.md` with a finished LEADING verdict (or was verified finished against
> live source on this day, or folded into an older duplicate). Full original text of each:
> `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`. Since this day `closed_register_gate.py` RULE 3
> refuses a finished row left in the open register.
| Row | What | Closed | Full text |
|---|---|---|---|
| **R-88a** | **~~Failing backup re-quiesces every 5 min, no backoff~~** **Reasoning kept:** «breaker 15m→4h, per-tier, never permanent» | SHIPPED (controller v0.176.0, 2026-07-27). Live on both boxes; breaker 15m→4h, per-tier, never permanent. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-88b** | **~~`/backup/due` cannot say *unknown*~~** | SHIPPED + PROVEN-LIVE (agent v0.105.0 + controller v0.178.0, 2026-07-27). `age_state=unknown` captured on real hardware during a deliberate ep0 outage; controller deferred, zero app stacks stopped. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-123** | **R-105 and R-106 were `READY` in `ROADMAP.md` with no row on THIS page** | CLOSED 2026-10-03 (triage, verified) — its last residue — "R-105 still needs a row" — is done: R-105 has its own row; the process gap is R-369's gate | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-131** | **`sess-f` is a fourth orphaned scratch customer** | CLOSED 2026-10-03 (triage, verified) — `sess-f` was already gone by 2026-08-05 (`documentation/tests/campaign11-evidence-2026-08-05/journal.md:24-27`) | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-176** | **Two prerequisites for the R-165 merge are UNMEASURED, and both are cheap.** | (a) ANSWERED 2026-08-03 (P1: PASS). (b) NOT REQUIRED — operator ruling: every node is reinstalled, none migrated (withdrawn, not deferred; CONTEXT.md:2076). — residue NOT-A-FINDING (2026-10-03): its residue ("re-run (a) once against agent v0.120.0") is moot: every node was reinstalled, none migrated (`CONTEXT.md:2076`) | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/SPIKE-r165-phase0-2026-08-03.md |
| **R-191** | **Every weekly offsite backup UPLOADS successfully and then FAILS the job on a prune the box is deliberately not allowed to do — on both demo boxes.** **Reasoning kept:** «R-89 moved PBS pruning SERVER-SIDE — *"boxes set `keep_last: 0`, ep0 runs prune jobs; box tokens stay write-only, never widen the grant"*.» | CLOSED — SHIPPED 2026-08-04 (installer 1.25.0; both live boxes corrected to offsite `keep_last: 0`, `prune_pbs_allowed=false`). ep0 prune jobs verified first (18 tasks OK). hostinstall_gates.py asserts it (red-proved). The 'next weekly run OK' observation: offsite snapshots dated 2026-08-11 recorded in audits/INCIDENT-ep0-pbs-fd-exhaustion-2026-08-18.md:118. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/INCIDENT-ep0-pbs-fd-exhaustion-2026-08-18.md |
| **R-201** | **Nothing in the offsite DR chain has ever been exercised past the ceremony — and the one live proof that exists predates the field it is cited for.** **Reasoning kept:** «**The pass condition is unchanged** — a byte-identical sentinel sha256, not "the store opened"» «**a good snapshot is not durable against a later bad run on the same day.**» | CLOSED 2026-08-07 — BOTH HALVES PASS on the fifth walk (DATA 2026-08-04 night run; JOURNEY 2026-08-07, zero guest command lines). Does NOT claim a smooth journey (R-252/R-253, both since CLOSED 2026-08-08) nor shape (c)'s positive half. Walls along the way: R-203, R-204, R-241. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/DRILL-r201-night-run-2026-08-04.md, audits/RECON-offsite-dr-chain-2026-08-04.md, tests/finalwalk-r201-2026-08-07/journal.md, tests/walk5-r201-2026-08-07/journal.md |
| **R-202** | **NOW EVIDENCED, NOT ARGUED (2026-08-10).** | CLOSED 2026-10-03 (triage, verified) — the false recoverability promise was removed (R-294/R-299; `controller/internal/web/templates/backups_remote.html:93-108`) | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-209** | **~~Should the containerd store move to SSD2 at all?~~** **Reasoning kept:** «**A TRAP was found while proving the guard, and it is the reusable part: `RequiresMountsFor` on a path with NO mount unit is a SILENT NO-OP**» «**`-X` is load-bearing** — overlayfs stacking rides `trusted.overlay.*`.» | EXECUTED 2026-08-05 on operator ruling — reboot validation DEFERRED → R-209a (still open, WATCHING, OPEN-ITEMS.md:339). Zero-loss move verified on four observables; storageReserved on SSD2 0 → 80 GB; pre-move tree moved aside. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/SPIKE-dooplex-buildcache-2026-08-05.md |
| **R-214** | **The physical console never stops asking to be paired.** | CLOSED 2026-09-20 - ISO 1.28.0+/R-535, proven on a fresh install (localisation slice 6's walk on ISO 1.29.0: last console paint after self-bind is the bilingual bound banner). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/i18n-slice6-2026-09-20/drill/screens/22-console-after-claim.png |
| **R-229** | **The instruction-file rightsizing landed for `felhom-controller` and the workspace root; three pieces were deliberately deferred.** | CLOSED 2026-10-03 (triage, verified) — legs (a), (b), (c) CLOSED 2026-08-06 in the row's own text; leg (d) moved to R-230 | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-233** | **The golden bake's acceptance checks were a list of strings the script does not print.** **Reasoning kept:** «**The general lesson:** a runbook's pass markers must be **copied from a captured log, never written from memory**» | CLOSED 2026-08-06 — fixed in `RUNBOOK-manual-build.md` (§4.1): markers re-captured from the real log, pre-gate URL corrected to `golden.tar.zst`, token moved off the command line into an in-VM runner script, positive control on the token-leak grep, vouch step rewritten. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: runbooks/RUNBOOK-manual-build.md |
| **R-245** | **Should a customer who never decides be auto-abandoned after 30 days? RECORDED, NOT BUILT.** **Reasoning kept:** «**The real harm, if it comes, is QUOTA** — old history blocking new backups — and **that is a condition, not a calendar**. An automatic ending should trigger on the harm, with a dated warning, never on a date alone. **If this is ever built, build it that way.**» | DECIDED 2026-08-07 — not built; reopens on quota. Built instead: escalating reminders (1/3/7/14 days) and operator levers `--abandon-extend` / `--abandon-stop`. Re-filed 2026-08-08 as a decision taken. Reopens verbatim: "THE CONDITION THAT REOPENS IT, which the reasoning already names: QUOTA — old set-aside history blocking new backups." | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-248** | **A flag that changes behaviour is visible to nobody who would look for it.** | FOLDED into R-246 2026-10-03 — the same ruling: give `stale_at` a visible, evidence-bearing setter, or retire it | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-254** | **The same render-then-hide pattern R-249 fixed is live in two more places, and one of them carries a real per-install secret.** **Reasoning kept:** «`escrow_handlers.go`'s rule that a secret is revealed by an XHR and never templated server-side into HTML.» «**A form must carry what it submits.**» | CLOSED 2026-08-08 — both sites closed in controller v0.208.0: `POST /apps/<slug>/initial-credentials/reveal` (re-reads container, no-store, CSRF, logged) and `POST /stacks/<name>/auto-field/reveal` for deployed pages; pre-deploy hidden input kept deliberately. Gate `scripts/secret_in_markup_gate.py`; its blind spot → R-255. Measured exposure: none; rotation not indicated. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: scripts/secret_in_markup_gate.py |
| **R-272** | **RANK 1 — Felhom's `--uninstall` leaves the exact condition that makes Felhom's own reinstall REFUSE.** | CLOSED 2026-10-03 (triage, verified) — the uninstall purges the dnsmasq Felhom installed (R-316; `scripts/felhom-host-install.sh:858-882`, `_dnsmasq_purge_owned`) | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-295** | **One name per secret — CONTROLLER HALF SHIPPED.** | CLOSED 2026-10-03 (triage, verified) — hub half shipped in hub v0.104.0 (`4d6ec7c`, hub `CHANGELOG.md:844,905`); the controller half shipped v0.211.0 | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-303** | **`markOrphaned` has no guard against an active abandon countdown — the co-render is made HARMLESS, not IMPOSSIBLE.** **Reasoning kept:** «the wrong fix (suppressing the orphan card during a countdown) would hide a real second fault.» | DECIDED 2026-08-13 — left as it is; reopens on a real-world sighting. Verbatim trigger: "an observation of the combined state occurring OUTSIDE a constructed test." Related: R-302. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-312** | **There is no in-product route from the recovery screen to a set-aside store, and building one is not wiring — it is new surface.** **Reasoning kept:** «retention is an **operator-only** capability, and nothing anywhere may promise the customer can perform it themselves» | DECIDED 2026-08-13 — deliberately not built; re-evaluate on a real customer request. Verbatim trigger: "a real need appearing — one request from a customer who is not us." Related: R-304, R-311. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-313** | **`demo-felhom`'s set-aside store is UNRECOVERABLE — 36 snapshots whose key we destroyed ourselves.** | DECIDED 2026-08-13 — kept as a test fixture; delete when R-312 ships or is abandoned. Verbatim: "when R-312 is built, or when R-312's ruling above is made permanent. On either event, delete it deliberately and record why". Related: R-198, R-307, R-312. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-341** | **Does the fd slope change after the PBS 4.2.5 upgrade? — two dated checks, and the answer is expected to be NO.** **Reasoning kept:** «the anchor must be `ps -o lstart= -p $MainPID`, NOT `systemctl show -p ActiveEnterTimestamp`, which reads 03:54:54Z for this generation (the upgrade re-exec'd the proxy; systemd never saw a stop, `NRestarts` is still 0) and would put the rate ~15% low.» | CLOSED 2026-08-30 — second check taken (fd back to baseline 17, ESTAB 0) but it cannot answer the question: the leak was REMOVED mid-interval by our own fix (R-344, agent 0.130.0). Question moot; fix confirmed holding at 12 days. First check 2026-08-20: unchanged, 201.6 fd/day. R-336 keeps the request-rate scaling item; ActiveEnterTimestamp trap → R-346. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/SPIKE-ep0-established-connections-2026-08-20.md, audits/evidence-r341-plus7d-2026-08-30/step1-fd-and-sockets.txt, evidence-ep0-pbs-upgrade-2026-08-18/stop1-ruling.txt |
| **R-343** | **The managed controller floor was raised 0.214.0 → 0.216.0 — and it was NOT the no-op it was expected to be: it moved a live box nine seconds later.** | CLOSED 2026-10-03 (triage, verified) — obsolete — its only step was "confirm 0.216.0 is healthy in normal operation"; both demo boxes have run every release since and are on 0.288.0 (`STATUS.md`, 2026-10-02) | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-369** | **There are TWO registers, only one calls itself the source of truth, and work filed in the other is invisible to every standing rule that says "grep the register".** | CLOSED 2026-10-03 (triage, verified) — RULED 2026-08-22 (one register) and enforced by `scripts/one_register_gate.py` (`ef6ac6f`) | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-378** | **A status word inside a longer verdict fooled the session's own compressor, moving six still-open rows into the closed file.** **Reasoning kept:** «Match the LEADING verdict.» | CLOSED 2026-08-22 — corrected in the same session; the six rows (R-123, R-190, R-214, R-264, R-295, R-352) restored verbatim from commit `fddfe00ce268`. Successor: R-369. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-385** | **A controller was built, baked AND vouched with no CHANGELOG entry of its own, and every gate stayed green.** **Reasoning kept:** «**Membership, not `baked > released`, deliberately:** a comparison against the newest heading alone goes green the moment any later entry is written, leaving the unrecorded version permanently unrecorded and the gate permanently silent about it.» | CLOSED — 2026-08-23. v0.221.1 given its own CHANGELOG heading (commit da75603); scripts/golden_currency_gate.py now requires the baked version's own `## vX.Y.Z` heading anywhere in the CHANGELOG (membership); INCONCLUSIVE exit 2 preserved; both directions red-proofed. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/DRILL-r384-dead-db-alarm-2026-08-23/evidence/gate-0*.txt |
| **R-387** | **The hub REWRITES an unknown severity and says nothing, and the guard built to catch that sits downstream of the rewrite.** **Reasoning kept:** «**The coercion STAYS; only the silence is fixed** — a rejected event is a LOST event, and losing an alarm is worse than mis-routing one.» | CLOSED — hub v0.107.0, 2026-08-23. Coercion kept; a WARN now names customer, event type, rejected value and consequence. Dispatcher severity branch kept (it is the only guard for monitor-originated events). Proven live with an `error` control silent. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/DRILL-r329-r386-2026-08-23/evidence/live-19-scenarioH-after.txt |
| **R-398** | **`resticStep` is not a seam, so no test can drive any restic-backed path.** | CLOSED 2026-10-03 (triage, verified) — premise corrected 2026-08-30; the execution test exists (`controller/internal/backup/r358_scratch_marker_test.go:200-205`, `TestR358_MarkerOrderingIsExecuted`) and the `resticStepFn` seam was deliberately NOT built — it would hide the `unlock --remove-all` escalation the test asserts | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-405** | **R-87 sat in CLOSED-ITEMS.md for nine days while still open; the ranking paragraph ranked it fourth pointing at nothing.** | CLOSED 2026-08-31 — corrected + gated in the same session: R-87 restored verbatim from `ef6ac6f^`; `scripts/closed_register_gate.py` is the 12th gate; R-398 stub turned to prose. Gate's residual holes in its docstring; hole 4 is R-406. Predecessor R-378. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-413** | **R-87's proof caught a naturally-produced hollow snapshot, end to end, unattended.** | CLOSED 2026-08-31 — the claim it upgrades is recorded (opengist `volumes_expected_none_captured`, one `offsite_proof_empty` at error; four apps ahead passed). Related R-87, R-412. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-428** | **The decoy-coverage gate identified a repository by its DIRECTORY NAME.** | CLOSED 2026-09-01 — fixed, and kept as the class's best example (felhom.eu CI job 490 found it; the gate now identifies a repo by which registered runner FILE exists under the root; verified under a renamed directory). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-432** | **A customer's own sub-account can REACH the snapshot door and is REFUSED writes — but sees it EMPTY, so per-file recovery is not product-reachable.** | ANSWERED 2026-09-01 — negatively; the panel-read next step is WITHDRAWN as unnecessary (777,600 exact snapshot names, zero hits; /.zfs/snapshot is a different filesystem from /home). Per-file recovery unreachable from the box entirely → R-433. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/evidence-drill-r95-recovery-2026-09-01/ |
| **R-434** | **The snapshot-drop alarm promises a recovery that cannot be performed.** **Reasoning kept:** «when a verdict changes which fact it counts from, the alarm text has to change with it, or the operator acts on a promise nobody can keep.» «**That sentence is true under EVERY possible answer to the provider questions, so it never needs a second rewrite** — which is the whole reason it was not blocked.» | CLOSED 2026-09-01 — hub v0.111.1; the promise was DELETED (not replaced) with a sentence true under every provider answer; three tests in `hub/internal/monitor/offsite_r434_test.go`, red-proofed. The "blocked on R-433" verdict above was mine and it was wrong. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-463** | **The day the catalog moves postgres:16 to 17, ELEVEN apps are affected and the container image will NOT perform the conversion. (P2)** **Reasoning kept:** «A later move of any of the three needs its own two-venue proof: the engine gate enforces it per app.» «PostgreSQL majors are converted BY THE BOX as a guarded-update step — save everything from the old engine, start the new one empty, load it back, check.» | CLOSED 2026-09-30 — 8 of 11 moved by the box's own conversion (docmost, paperless-ngx, tandoor, claper, calcom, rallly, outline, sparkyfitness; controller v0.273.0+, `09` decisions 16/42/43); 3 stay by decision 42's rule (zipline, adventurelog, immich). A later move of any of the three needs its own two-venue proof: the engine gate enforces it per app. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/SPIKE-r459-mariadb-upgrade-2026-09-06.md, audits/update-night-2026-09-21/24-Q5-postgres-conversion-costed.md, audits/pg-calcom-claper-2026-09-28/, audits/pg-last-six-2026-09-30/ |
| **R-497** | **No product channel ever gives the customer the „Tulajdonosi jelmondat”, yet the self-bind mail says they received it at setup. (P2)** | CLOSED — hub v0.113.0 (2026-09-14, ArgoCD Synced): customer page renders the hand-over sentence; tests red first (`passphrase_handover_test.go`); mail change pinned by test, not read live. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/DOORSTEP-walk-1270-2026-09-14.md |
| **R-500** | **[P3-LOW] The dashboard shows the last backup in UTC while every backup page shows it in local time — two different clock times for one backup.** | CLOSED 2026-10-03 (triage, verified) — controller v0.283.0 — the dashboard's last-backup time uses `fmtTime`, local time (`dashboard.html:154`; controller `CHANGELOG.md:222`) | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-505** | **A fresh box on customer tester-1 connects its Cloudflare tunnel but receives NO routes, so the dashboard answers 503. (P1)** | CLOSED 2026-09-29 — proven end to end on a fresh box. Cause: the tester-1 tunnel had no public hostnames; operator added a route 2026-09-14; box-side hop proven by the 2026-09-29 new-household drill. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-506** | **[P3-LOW] `day0-install.md` A.1 says "the controller manages per-app hostnames itself via the tunnel" — it does not.** | CLOSED 2026-10-03 (triage, verified) — the day-0 runbook names the published-route step and says the controller creates no routes or DNS (`documentation/runbooks/day0-install.md:47-53`) | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-511** | **A rebuilt customer's box keeps its ep0 PBS token, and the DR tier can be neither provisioned nor re-issued. (P2)** | CLOSED 2026-09-16 — proven live on a fresh box: hub v0.114.0 re-issue ADOPTS the endpoint token (shipped 2026-09-15); made live by R-534's ep0 grant. Token-only release on host delete not built → R-526. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/evidence-p1fixes-2026-09-15/ |
| **R-520** | **A power cut during a guarded Update leaves no record that an update was running. (P3)** **Reasoning kept:** «**`pct mount` maps the guest's ROOTFS ONLY and does not apply the guest's own internal mounts**, and 9202 keeps `/var/lib/docker` on its own ext4 mount under the `mp0` volume, so from the host that path is an EMPTY STUB.» «**An empty directory is not evidence of an absent file.**» | CLOSED 2026-09-21 — measured on a real version change (scratch 9202, controller v0.260.0, uptime-kuma 2.4.0→2.5.0 cut in `pulling`; box put the pin back itself). The `starting`-phase cut → R-610. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/update-arc-2026-09-21/ |
| **R-524** | **When the catalog moves an app back to an older version, a box that already updated shows „Frissítés elérhető” — and the offered Update is a downgrade. (P2)** **Reasoning kept:** «**Ahead is NARROW on purpose:** every differing service must be orderable AND newer, or the verdict falls back to Behind — this gate can BLOCK an update, so it errs towards letting one run.» | CLOSED 2026-09-21 — controller v0.260.0: `stacks.CatalogOrder` four verdicts incl. Ahead; `UpdatePreflight` refuses `downgrade` (409); three red-proofs; `09` §3 decision 10 (decided by CC unattended — operator may reverse). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-534** | **The off-site tier cannot be provisioned or adopted for a rebuilt box: the hub's endpoint token lacks Datastore.Modify. (P1)** | CLOSED 2026-09-16 — grant given and proven end to end: DatastoreAdmin for the hub's `felhom@pbs` on `/datastore/felhom-offsite` only (narrowest role carrying Modify; PBS has no custom roles); re-issue then ADOPTED on a fresh box. Closes R-511. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/evidence-drill-0243-2026-09-16/phase1-pbsdr.txt, audits/evidence-backup-promise-2026-09-16/phaseC-ep0-grant.txt, audits/evidence-backup-promise-2026-09-16/phaseC-reissue.txt |
| **R-535** | **The box's console still says it waits for pairing long after the box is bound — and promises the screen refreshes itself. (P2)** | CLOSED 2026-09-16 — shipped in ISO 1.28.0 (published); on-screen effect not photographed (`print_bound_banner`). Later SEEN on screen by R-214's closure 2026-09-20. Deliberately does not name the dashboard URL nor reflect the later claim. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/evidence-drill-0243-2026-09-16/screens/33-console-after-claim.png |
| **R-536** | **The hub is told „Alkalmazás telepítve” the moment a deploy is ACCEPTED, so an install that never finishes is recorded as completed. (P2)** **Reasoning kept:** «The accept-time `app.yaml` is deliberately NOT deleted on failure — it is the crash-safe record with `Deployed:false` and it holds the settings the customer typed; the state every surface reads is `not_deployed`.» | CLOSED 2026-09-16 — controller v0.244.0 + hub v0.116.0: `app_deploy_started` at accept, `app_deployed` from the async end, `app_deploy_failed` (warning); both new types in `allowedEventTypes` and `customerMessages`; two red-proofs. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-537** | **The app-backup page labels the tier-1 backup „DB + Konfig + Adatok" and shows the data-drive size, but the tier-1 unit contains NO drive-side app data (P1-HIGH)** **Reasoning kept:** «The contents label is computed PER TIER from what that tier captures: Tier 1 says „Adatok" only when the app's data really is in the volumes the unit captured» | CLOSED 2026-09-16 — controller v0.244.0, proven live on demo-hp; label computed per tier; re-proven on a fresh box (ISO 1.28.0). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/evidence-drill-0243-2026-09-16/phase2-f10.txt |
| **R-538** | **A tier-1 app restore reports plain success, leaves Nextcloud listing files whose bytes were never backed up, and destroys the app's own trash (P1-HIGH)** **Reasoning kept:** «never present a DB-only restore of a class-A app as a complete one.» «A unit restore refuses before anything is touched when the unit cannot return the app's drive-side files, and names the route that can.» | CLOSED 2026-09-16 — controller v0.244.0, proven live on demo-hp; unit restore refuses when the unit cannot return the drive-side files; re-proven on a fresh box, off-site route returned 5/5 photos sha256-identical. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/evidence-drill-0243-2026-09-16/phase2-f10.txt, audits/evidence-backup-promise-2026-09-16/phaseE-photos.txt |
| **R-543** | **Off-site ON by default is not off-site WORKING: a fresh box's tier 3 waits on escrow and nothing asks the household. (P1)** **Reasoning kept:** «The pause is untouched: it is the zero-knowledge escrow design, and this row was never about the mechanism.» | CLOSED 2026-09-16 — shipped in controller v0.245.0 and proven live: escrow-pending bar on every authenticated page (via `executeTemplate`) + tier-1 sentence rendered by `tier3State`; both red-proofed; live on 9202 (paused) and 9201 (escrowed). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/evidence-recovery-code-2026-09-16/ |
| **R-557** | **Localisation slice 2 — Go-side customer strings follow the language. (P3)** **Reasoning kept:** «Plurals are a BUNDLE rule (a key with `.one`/`.other` takes its count first), not a call-site flag.» «EXCEPT one producer: `"Sikeres — nincs mentésre jelölt alkalmazás"` (controller/internal/backup/offbox.go) must stay Hungarian until **R-570** closes, because the page's legacy fallback still reads it on boxes that have not run off-site since 0.251.0.» | CLOSED 2026-09-18 - controller v0.252.0 + v0.253.0 + v0.254.0 (release A: 226 literals + `i18n_go_parity.py`; B: all 179 error literals keyed via `util.MsgError`; C: saved notes in box language, globe switch). Gaps filed: R-570, R-572..R-578; R-566 closed with it. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-558** | **Localisation slice 3 — the hub's customer e-mails follow the household's language. (P3)** **Reasoning kept:** «The bind page is per-language with `expired` pinned to Hungarian (the language would otherwise be the oracle the text refuses to be).» | CLOSED 2026-09-18 - hub v0.118.1 + controller v0.256.1 (hub v0.118.0/v0.118.1 + controller v0.256.0/v0.256.1; 56 Hungarian mail goldens unchanged; proven live twice). Gaps filed: R-581..R-585; R-555 closed with it. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-559** | **Localisation slice 4 — the console banner and the download page in English. (P3)** | CLOSED 2026-09-18 — ISO 1.29.0 published (sha256 `dceacae5…e94829`), proof installs on both menu entries + reboot; `felhom.eu/en/download` live; release gate G16 rewritten per ruling 1b. Gaps filed: R-586, R-587, R-588. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-560** | **Localisation slice 5 — catalog cards, settings and first steps in English. (P3)** **Reasoning kept:** «lists replace WHOLE and every other list is matched by its own key (`env_var`, option `value`, `match_group`, `target`, `path`), never by position;» «**The blocks are GENERATED from a flat `{path: english}` map**, not hand-written: a mistyped `env_var` is INERT on the box rather than an error, and the generator can only write paths that exist on the Hungarian side.» | CLOSED 2026-09-20 — controller v0.257.0, catalog fully translated (catalog `e81d41e` + three batches; 1 031/1 032 strings, ceiling 1), fleet floor 0.257.0 (min_agent 0.131.0). Gaps filed: R-589..R-594. — residue NOT-A-FINDING (2026-10-03): its residue (Peti's box and tester-1 taking floor 0.257.0 unattended) is moot: Peti's box was retired 2026-09-25 and tester-1 takes every floor like any box | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-561** | **Localisation slice 6 — the volunteer guide in English, then a stranger's first hour in English; closes R-516. (P3)** | CLOSED 2026-09-20 - controller v0.258.0, the English guide, and the walk (`runbooks/VOLUNTEER-first-hour.en.md`; R-589/R-590/R-573 fixed; R-214 closed as a side effect). Verdict: not yet ready for an English tester (R-596). Gaps filed: R-596..R-599. R-516 does NOT close. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/DRILL-first-hour-en-0258-2026-09-20.md, audits/i18n-slice6-2026-09-20/R-516-item-by-item.md, runbooks/VOLUNTEER-first-hour.en.md |
| **R-566** | **Three page titles built in Go around an app name stay Hungarian in the English browser tab. (P3)** | CLOSED 2026-09-18 - controller v0.252.0 (four `%s` keys via `data["TitleArgs"]`; pinned by `TestParameterisedPageTitles` and `i18n_go_parity.py`). Part of R-557. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-572** | **[P3-LOW] Two copy-producing template helpers have no English form, so an English page renders „vasárnap" and „%d órája" in Hungarian.** | CLOSED 2026-10-03 (triage, verified) — controller v0.258.0 — `pruneLabel`/`nextPruneLabel` deleted as dead code (`controller/internal/web/funcmap.go:368-377`) | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-580** | **[P3-LOW] `curl -w '%{redirect_url}'` prints Basic-auth credentials back into the session transcript.** | FOLDED into R-132 2026-10-03 — the same `curl -w %{redirect_url}` credential echo, seen again 2026-09-18 | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-582** | **An English copy guard written from the Hungarian one matches ordinary words instead of the claim. (P3)** **Reasoning kept:** «**The general form worth keeping: a guard ported between languages must be re-derived from what the claim IS in the new language, not translated word for word — and a guard that convicts 141 true sentences is worse than no guard, because it earns an allowlist entry per sentence and then nobody reads it.**» | CLOSED 2026-09-18 - hub v0.118.0 (recorded for the lesson; three decoys incl. an innocent control): patterns carry the modal and admit an adverb; gate scans bundle VALUES only. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-583** | **The test-notification mail was the one customer mail that did not follow the language. (P3)** **Reasoning kept:** «**The general form worth keeping: the surface you would use to CHECK a feature is the one most worth checking first — a broken instrument that reports success is worse than a broken feature.**» | CLOSED 2026-09-18 - hub v0.118.1 (`mail.test.subject`/`mail.test.body`, red-proofed, proven live in both languages 74 s apart on demo-hp). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-589** | **The update badge is Hungarian on an English app page — and it is the badge, not a corner case. (P3)** **Reasoning kept:** «**The lesson, because it cost a reviewer pass and half a task brief: a reviewer who reads ONE producer cannot see a SECOND producer that overrides it** — reading `updatebadge.go` alone gives exactly the wrong answer.» | CLOSED 2026-09-21 — shipped in v0.258.0; the row was stale (English built in `web.localeFuncs` "updateBadge", pinned by `TestUpdateBadgeFollowsTheLanguage`; verified at controller `19ef0329ab66`). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/DRILL-first-hour-en-0258-2026-09-20.md |
| **R-590** | **[P3-LOW] The data-folder card tells an English household, in Hungarian, whether its files are backed up.** | CLOSED 2026-10-03 (triage, verified) — controller v0.258.0 — `consequenceFor` renders through message keys in the household's language (`controller/internal/web/datapath_card.go:32-50`) | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-592** | **Two defects in the new catalog copy gate, each found by its own decoy rather than by reading it. (P3)** | CLOSED 2026-09-20 — fixed in the same session, decoys added (`scripts/test_gate_decoys.py`, 33 cases): coverage scoped to named apps, credential and ASCII-stem bare-substring matches. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-595** | **The catalog's new copy gate could not RUN in CI at all — six pushes red, six alarm mails, while the local hook was green. (P2)** **Reasoning kept:** «**The general form, and the reason this is P2 rather than P3:** a new gate is written and tested on the machine that has every library, and the runner deliberately has none.» | CLOSED 2026-09-20 — degraded mode; CI job 791 green (catalog `18a6d2d`: without PyYAML the gate runs the freeze check and prints what it did not check; five decoys with PyYAML shadowed). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-596** | **The claim page — the FIRST screen an English household touches — is English chrome with HUNGARIAN messages. (P1)** **Reasoning kept:** «It was **deleted, not translated**: a translated dead field would have read for ever after as evidence that this page's title is decided in the handler.» | CLOSED 2026-09-21 — controller v0.259.0, proven live (fourteen sites via `s.msg(r, "claim.msg.*")`; dead `data["Title"]` deleted; language chain pinned by a test; live on guest 9201 both languages; red-proofed). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-597** | **The setup code is three Hungarian words inside an otherwise fully English e-mail to an English household. (P2)** **Reasoning kept:** «**The task's proposed "read it over the phone" filter was MEASURED and NOT adopted** — it removes 5270 of 7772 words (68%, 12.92 → 11.29 bits/word) and would make this list stricter than the one the product already uses for the code a household writes on paper during a disaster;» «The hub does not own that secret and no row was opened for it: a second definition here is the drift `backupTargetAbsentText` already demonstrates across two repos.» | CLOSED 2026-09-21 — hub v0.119.0: `RandomPassphraseFor(lang, use)`; setup code 4 en words (51.7 bits), owner passphrase 6 en (77.5); entropy floor computed from embedded lists, red-proofed. Recovery code was never Hungarian (agent, EFF list). Decision in source, operator may reverse. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-598** | **The Backup page's two protection warnings are Hungarian on an English dashboard. (P2)** **Reasoning kept:** «**The English is asserted to carry the same NEGATION the Hungarian does** ("protects against corrupted files, **but not** against a disk failure"); an English sentence that promised disk-failure protection would be worse than leaving it Hungarian.» | CLOSED 2026-09-21 — controller v0.259.0; the two warnings proven by render test, not live (tier names proven live on 9201; `degradedMessageFor` returns a KEY). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-601** | **demo-hp is unreachable — WRONG, WITHDRAWN THE SAME DAY. The box was never down; MY ROUTES WERE. (P2)** **Reasoning kept:** «**The standing rule says a "no access" claim must list what was tried; it does not say the list makes the claim true.**» «Six failed routes to a stale address are six failures of one assumption, not six pieces of evidence.» | CLOSED 2026-09-21 — withdrawn, the claim was false; the routes are fixed (both `~/.ssh/config` entries repointed to 192.168.0.104; `nodes.md` corrected; tailscale not installed on demo-hp). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-608** | **The controller swaps ITSELF in the middle of a guarded app update, and 04:30 sits inside the proposed auto-update window. (P2)** **Reasoning kept:** «**THE PROPERTY THAT MATTERS MOST IS THAT THE LOCK DOES NOT LATCH:** `Stack.Updating` is cleared on done, failed AND held, so a HELD app does not block the controller's own updates — including the release that might fix whatever held it.» «**`stacks` never imports `selfupdate`.**» | CLOSED 2026-09-21 — controller v0.261.0: `Manager.AnyUpdating()` → `Updater.SetAppUpdatingCheck`; `UpdatePreflight` refuses `self_updating`; lock does not latch (`TestR608_LockReleasesAfterHold`); four red-proofs. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-609** | **An update refusal has a machine-readable reason inside the process and none on the wire, so an unattended caller cannot tell "wait" from "never" (P3-LOW)** **Reasoning kept:** «`busy`/`updating`/`deploying`/`migrating`/`self_updating` are TRANSIENT and `held`/`downgrade` are TERMINAL until a person acts.» | CLOSED 2026-09-21 — controller v0.261.0: 409 body gains data.reason additively; the held refusal in actionStack (before UpdatePreflight) carries it too; red-proofed. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-611** | **A session reported "everything is done" over a phase it had silently skipped — the process failure, not the missing measurement (P3-LOW)** **Reasoning kept:** «the report's FIRST section is "not done", even when empty.» «Every part and scenario of a brief is listed there if it was skipped, shortened or changed, with the reason.» | CLOSED 2026-09-21 — run by the successor session (scenarios F and G); "not done" is now the report's first section. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/update-arc-gaps-2026-09-21/ |
| **R-614** | **A stale `update_phase` survives a remove and redeploy, so a freshly installed app can read "Frissitve" before it has ever been updated (P3-LOW)** **Reasoning kept:** «The name is the only thing a new install shares with the old one, so the record has to go when the app does.» | CLOSED 2026-09-22 — controller v0.262.0: RemoveStack calls ClearUpdateState; red-proofed. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-623** | **The unattended-update caller turned every SUCCESS into a `timeout`, then refused to press that app again — the instrument, not the box (P3-LOW)** **Reasoning kept:** «an instrument that can report a success as a timeout is not a measurement» | CLOSED 2026-09-21 — fixed in audits/update-arc-gaps-2026-09-21/unattended-caller.py (unwraps data). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/update-arc-gaps-2026-09-21/unattended-caller.py |
| **R-627** | **Nothing checked that the register is a well-formed table; an append ate two rows' state cells and R-254 was broken for 45 days (P2-MEDIUM)** | CLOSED 2026-09-22 — gate 14 (scripts/register_shape_gate.py in repo_gates.py), four decoys, register repaired 317→315. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-628** | **An empty search of a mailbox I do not control was turned into a claim about what a THIRD PARTY had done, and entered the register as fact (P2-MEDIUM)** **Reasoning kept:** «*an empty search may be reported as "absent from the place I looked", never as "it did not happen" — and only after a control query that MUST hit has been seen to hit.*» | CLOSED 2026-09-22 — rule recorded in the Gmail-access memory; R-433 corrected with the real answers. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-629** | **The drill catalog sent the operator 47 CI-failure alarms in one night; the drill method did not mention CI (P2-MEDIUM)** | CLOSED 2026-09-22 — has_actions false on admin/app-catalog-drill, `09` §6.5 method updated; the 47 mails are the operator's to clear. — residue NOT-A-FINDING (2026-10-03): its residue (47 unread CI mails) is the operator's inbox housekeeping, not product work | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-630** | **`paperless-ngx`'s health probe has never run on any box; a successful update stops the app (P2-MEDIUM, raised to P1)** **Reasoning kept:** «A stack with no probe is not healthy and not failing — it is SETTLED ON CONTAINER STATE (`09` §3), and never a reason to stop a running app.» | CLOSED 2026-09-22 — controller v0.262.0 + catalog: the no-probe wait settles on container state, the target is decidable (explicit container field), the gate refuses an ambiguity. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/the-28-2026-09-22/sidejobs/r630-controller-words.txt, audits/the-28-2026-09-22/sidejobs/r630.json |
| **R-631** | **Five templates cannot be judged by the probe gate at all, and one is correct only by accident (P3-LOW)** **Reasoning kept:** «Add `expect: {status: 200}` to that template — a change that looks like a tightening — and home-assistant goes permanently unhealthy and every successful update of it starts stopping it.» | CLOSED 2026-09-22 — all five read live on guest 9202, all five probes correct; home-assistant's fragility is now a number (401 with no expect). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/the-28-2026-09-22/sidejobs/r631.json |
| **R-632** | **Twenty-eight of the 53 templates have never been deployed by any update drill (P3-LOW)** | CLOSED 2026-09-22 — all 28 walked in one night (26 deployed, 6 proven); findings → R-630, R-633, R-634. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/DRILL-the-28-2026-09-22.md, audits/probe-fix-2026-09-22/not-judged.json |
| **R-633** | **A remove sent while a restore runs reports success, deletes the record, and leaves a container restarting forever with a live public route (P2-MEDIUM)** **Reasoning kept:** «`down` returning 0 is a request, not a result» «A refusal from the wrong rule is not evidence for the new one.» | CLOSED 2026-09-22 — controller v0.262.0 (busy guard + verified teardown) + v0.262.1 (typed RemoveBusyError → HTTP 409); both halves proven live on 9202. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/the-28-2026-09-22/apps/gokapi/came-back-evidence.txt, audits/the-28-2026-09-22/apps/gokapi/log.txt |
| **R-701** | **demo-hp's whole-guest restore test can never run: every 6 h it picks the right archive and the space preflight refuses it (P3-LOW)** | CLOSED — option (b), 2026-09-28 (operator, `09` §3 decision 44): restore_storage → nvme-scratch, FelhomAgentStore granted on /storage/nvme-scratch; manual and first scheduled cycle passed. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/version-travel-2026-09-26/D1/D1-cycle-demo-hp.txt, audits/evidence-golden-0276-2026-09-28/phaseD1-reclaim.txt, audits/logins-nvme-2026-09-28/C/ |
| **R-702** | **Every claper install creates an admin `admin@claper.co` with the public password `claper`, published on the household's domain (P1-HIGH)** | CLOSED — claper fixed 2026-09-28 (catalog 9dc8a05, controller v0.279.0 after_install); the class → R-707. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/pg-calcom-claper-2026-09-28/box/C0-claper-default-admin.txt |
| **R-703** | **calcom v6.2.0 cannot start at its catalog memory limit — a fresh install crash-loops and the box stops it (P2)** | CLOSED — catalog 9555e73, 2026-09-28: memory 768M → 1536M (anon peak 817 MiB, 53%). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/pg-calcom-claper-2026-09-28/box/C0-calcom-crash.txt, audits/pg-calcom-claper-2026-09-28/box/C0-calcom-memory.txt |
| **R-708** | **grafana falls back to password `admin` when its admin field is empty (P3-LOW)** **Reasoning kept:** «no default in the compose (`${GF_SECURITY_ADMIN_PASSWORD:?}` refuses to start instead).» | CLOSED — 2026-09-29 (catalog d0e7e2e): `${GF_SECURITY_ADMIN_PASSWORD:?…}` refuses to start empty. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/login-gate-2026-09-29/D/D4-grafana-r708.txt |
| **R-709** | **The deploy page writes the generated admin passwords of installed apps into its HTML (P3-LOW)** | CLOSED — 2026-09-29, controller v0.280.0: password field renders empty with a reveal eye; red-proofs RP14/RP15; live on 9202. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/login-gate-2026-09-29/D/D6-r709-live.txt |
| **R-710** | **An app installed before its template gained an `after_install:` is never warned about its default login (P2-MEDIUM)** | CLOSED — 2026-09-29, controller v0.280.0 (RP13): absent record is "not run yet" only 30 min after install; „Megváltoztattam" press; live on demo-hp. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/login-gate-2026-09-29/A/A2-page-warning-after.txt, audits/login-gate-2026-09-29/A/A3-demo-hp-changed-it.txt |
| **R-711** | **About a dozen class-4 apps keep open sign-up after their first admin exists — the setup gate does not close that (P2-MEDIUM)** | CLOSED — 2026-09-29 (decision 47, controller v0.281.0, catalog 6faf432): per-app signup_block written when the gate opens; wanderer → R-714. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/login-gate-2026-09-29/B/B-VERDICT.md, audits/gate-rollout-2026-09-29/ |
| **R-712** | **wger refused every browser sign-in behind traefik: "CSRF verification failed" (P2-MEDIUM)** | CLOSED — 2026-09-29 (catalog d0e7e2e): CSRF_TRUSTED_ORIGINS + X_FORWARDED_PROTO_HEADER_SET; proven on a fresh install. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/login-gate-2026-09-29/D/D2-live.txt |
| **R-713** | **claper's `after_install` pastes the household's password into Elixir code, and the controller does not refuse a value that would break such code (P3-LOW)** **Reasoning kept:** «a code-bound value holding a quote, backslash, `$`, `{`, `}`, backtick or line break is refused» | CLOSED — 2026-09-29, controller v0.281.0 (RP24) + catalog 6faf432: code-bound unsafe values refused, `${NAME¦base64}` added; proven live. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/gate-rollout-2026-09-29/ |
| **R-714** | **wanderer cannot be gated: its web part calls its own database host through the public name (P2-MEDIUM)** | CLOSED — 2026-09-29 (decision 48, controller 0.282.0): resolved without a gate; sign-up closed by "Close sign-up now" block + PUBLIC_DISABLE_SIGNUP; proven on 9202. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/signup-lock-2026-09-29/ |
| **R-715** | **The setup gate's probe reads only an HTTP-200 JSON object, so three apps with a real status get the button (P3-LOW)** **Reasoning kept:** «a catalog gate that refuses a probe without a before/after measurement in its comment.» | CLOSED — 2026-09-29, controller v0.282.0 (RP29, RP30): list indexes in field, done_status:; new catalog gate check-probe-measured.py (5 decoys). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/signup-lock-2026-09-29/ |
| **R-716** | **Apps installed before controller 0.281.0 keep their open sign-up — decision 47 closes it only where the box opened the gate (P3-LOW)** **Reasoning kept:** «a catalog change never touches an installed app» | CLOSED — 2026-09-29: operator ruled A (decision 49); built in controller v0.282.0 and pressed on demo-hp adventurelog/opengist and demo-felhom opengist. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/gate-rollout-2026-09-29/0/P0-3-demo-boxes-after-push.txt, audits/signup-lock-2026-09-29/ |
| **R-720** | **A new household's apps are not in the off-site copy: every app starts „3. mentés Kikapcsolva" (P2-MEDIUM)** | CLOSED 2026-09-30 — controller v0.283.0 (decision 50): fresh install on an off-site box switches the app ON; one-press offer for older apps. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/evidence-fixes-first-tester-2026-09-30/partA/ |
| **R-721** | **The household presses Stop during a whole-guest backup, and the backup starts the app again (P2-MEDIUM)** **Reasoning kept:** «unquiesce restarts only stacks whose desired state is still running» | CLOSED 2026-09-30 — controller v0.283.1, proven live (v0.283.0 was wrong in production; v0.283.1 wires the adapter and pins the production types). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/evidence-drill-new-household-2026-09-30/phase1/step10-stop-undone-by-quiesce.log, audits/evidence-fixes-first-tester-2026-09-30/partE/ |
| **R-722** | **The volunteer guide is stale in five places a volunteer reads literally (P2-MEDIUM)** | CLOSED 2026-09-30 — both guides rewritten as measured (operator: CC rewrites); every changed line dated. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: runbooks/VOLUNTEER-first-hour.md |
| **R-727** | **The whole-guest restore test picks a PREVIOUS box's archive, fails on its key, and the ✗ is labelled with the wrong tier (P2-MEDIUM)** **Reasoning kept:** «The restore test skips an archive written with another key (logged by name).» | CLOSED 2026-09-30 — agent v0.138.0 + decision 51 (restore test skips archives written with another key); ✗ card names the tier in controller v0.283.0; ep0 drill archives in tester-1 removed; delivered by signed jobs to both demo boxes. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/evidence-fixes-first-tester-2026-09-30/partC/ |
| **R-730** | **The published ISO 1.29.0 was built from a tree that git cannot name, so it cannot be tagged (P3-LOW)** **Reasoning kept:** «`scripts/iso/build-felhom-iso.sh` now runs a clean-tree gate right after argument parsing: any uncommitted or untracked change, or HEAD ≠ `origin/main`, refuses the build (no bypass flag); the manifest records `repo-commit` from the gate and `iso-version-tag : iso-v<version>`.» «the `installer-v*` tags are the host-install SCRIPT's versions (`SCRIPT_VERSION` 1.28.0 on main; the website serves `/scripts/` from `installer-v1.28.0`), a different number line from the ISO — an `installer-v1.29.0` tag would block the next script release.» | CLOSED 2026-09-30 — the gate + test; 1.29.0 untagged by decision (its build tree is not a commit). build-felhom-iso.sh clean-tree gate (no bypass), manifest records iso-v<version>; test scripts/iso/test/clean-tree.sh red-proofed. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/pg-last-six-2026-09-30/E/E4-installer-tag.txt, scripts/iso/test/clean-tree.sh |
| **R-732** | **immich's FIRST start at the catalog pin could not finish on the bench: its database container was OOM-killed at 512 MiB (P2-MEDIUM)** | CLOSED 2026-09-30 — catalog `56c4888` (768M + v3.2.4); older step 0b8272068aab36bf re-proven at 768M (catalog `48440ce`, `63a96b0`); an installed immich gets it with its next guarded Update. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/pg-last-six-2026-09-30/F/immich-first-start-oom.txt, audits/immich-first-start-2026-09-30/A-cause.md, audits/more-night-apps-2026-09-30/ |
| **R-735** | **The test bench generates a `password:N:special` deploy field WITHOUT a special character, so calibre-web refuses it (P3-LOW)** | CLOSED 2026-09-30 — catalog `5b1972b` (`_gen` mirrors `randomWithSpecial`; test seen failing first). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/more-night-apps-2026-09-30/bench/apps/calibre-web/, A/R735-red.txt, A/R735-green.txt |
| **R-736** | **Removing or updating an app never deletes the image it pulled, so a box's Docker disk fills until an install or update is refused (P2-MEDIUM)** **Reasoning kept:** «Keep set box-wide at delete time; exact id; no pass while an update runs; one summary line per pass; a one-time sweep.» «the Remove button runs `RemoveStack`, not `DeleteStack`; `docker image ls` without `-a` hides the untagged digest-pulled images» | CLOSED 2026-09-30 — controller v0.284.2, floor 0.284.2 (decision 53, option A; 0.284.0/0.284.1 never floored). Old controller images → R-745. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/more-night-apps-2026-09-30/box/T2-images-removed-by-name.txt, audits/night-rulings-2026-09-30/ |
| **R-737** | **wger's app login API answers 500 on a CORRECT password: the template sets no `JWT_PRIVATE_KEY` (P3-LOW)** | CLOSED 2026-09-30 — catalog `45d8482` (RSA pair generated once by wger's `manage.py generate-jwt-keys`, kept 0600 on the data volume; bench-proven, not proven on a box). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/more-night-apps-2026-09-30/B/wger-probe.txt, audits/night-rulings-2026-09-30/ |
| **R-740** | **A same-tag upstream security fix (postgres:18-alpine, redis:7-alpine, mariadb:12.3 …) reaches NO box, because the catalog never records a re-test of a tag at a new digest (P2-MEDIUM)** **Reasoning kept:** «Not a cron job (runbook `runbooks/monthly-floating-retest.md` says why); who presses it monthly is the operator's word (STATUS).» | CLOSED 2026-09-30 — built (catalog `6a3ead9`); the monthly run is a standing step. Decision 52 option A: re-test entries + `scripts/retest-floating.py`; proven end to end on 9202. Exact-tag re-pushes → R-743. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/more-night-apps-2026-09-30/C/C3-floating-repush.txt, audits/more-night-apps-2026-09-30/C/C4-same-tag-retest.txt, runbooks/monthly-floating-retest.md, audits/night-rulings-2026-09-30/ |
| **R-741** | **For a few seconds after a fresh install, an `after_install` app answers its PUBLIC default login through the front door (P3-LOW)** **Reasoning kept:** «an `after_install` app is installed HELD behind the setup gate's door until the login is replaced (or the household says it changed it).» | CLOSED 2026-09-30 — controller v0.284.2 (install hold: an after_install app is installed held behind the setup gate's door until the login is replaced; red-proofs RP-IH1..4). mealie lock → R-747. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/more-night-apps-2026-09-30/A/A3-calibre-default-login-window.txt, audits/night-rulings-2026-09-30/ |
| **R-742** | **zipline 4.8.0 cannot be reached from 4.6.1 in one step: it refuses to start until the database ran the release before it (P3-LOW)** | CLOSED 2026-09-30 — catalog `a9700e2` + `fb87030` (two steps 4.6.1 → 4.7.0 → 4.8.0, each proven on both venues). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/more-night-apps-2026-09-30/bench/apps/zipline/, audits/more-night-apps-2026-09-30/box/zipline/ |
| **R-743** | **Exact version tags are re-pushed under the same name too — not only floating lines (P3-LOW)** **Reasoning kept:** «decisions 54 (a CC session the operator starts with the standing brief runs it monthly) and 55 (every app with a proven ladder; `--engines-only` a switch).» | CLOSED 2026-10-01 — decisions 54, 55; both tags re-tested (nextcloud catalog `3b59dfb`, sonarr `1a37032`). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/night-rulings-2026-09-30/A/, audits/retest-2026-10/ |
| **R-744** | **outline's fixture cannot seed outline 1.10.1: no `csrfToken` cookie after `installation.create` (P3-LOW)** | CLOSED 2026-10-01 — fixture fixed, proven on 9202 (accepts `__Host-csrfToken`; catalog `9fc7052`). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/rulings-2026-10-01/D/D2-outline-fixture-9202.txt |
| **R-745** | **Old CONTROLLER images are never deleted: ~50 versions on each demo box (P3-LOW)** **Reasoning kept:** «the agent rolls back to the RUNNING image (what `/etc/felhom-controller-image` named), never to a previous one the controller hands it.» | CLOSED 2026-10-01 — controller v0.285.0, floor 0.285.0 (decision 56: keep running + previous, delete older/untagged). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/night-rulings-2026-09-30/E/, audits/rulings-2026-10-01/B/ |
| **R-746** | **`image_digest.resolve` ignores a `@digest` in its argument — it answers the TAG's current digest (P3-LOW)** | CLOSED 2026-10-01 — catalog 804884a (manifest asked by digest; malformed digest refused; scripts/test_image_digest.py red-proofed). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/rulings-2026-10-01/D/D1-r746-digest.txt |
| **R-748** | **The register-shape gate skipped every row whose id has a letter suffix (R-88a, R-88b, R-209a) (P3-LOW)** | CLOSED 2026-09-30 — `scripts/register_shape_gate.py` (`R-\d+[a-z]?`; decoy `suffix-row-eaten-state`). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-749** | **`retest-floating.py` could never start on a fresh bench: it checked for `/opt/upg/upgrade-test.py` before the step that copies it (P3-LOW)** | CLOSED 2026-10-01 — catalog 9e53205. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/rulings-2026-10-01/A/ |
| **R-750** | **The registry no longer holds controller releases older than 0.213.0 — something removed them, and nothing records what (P3-LOW)** **Reasoning kept:** «`gitea-image-prune.sh` keeps the newest 20 + every version in use (floor, vouched golden, vouched agent, `min_agent`, the golden's baked images, the running hub), refuses (exit 3) when that list is unreadable» | CLOSED 2026-10-01 — decision 62, misc-scripts c9d5ed5 (cause: manual `gitea-image-prune.sh --keep 7` run 2026-08-22/23, HM-024; prune now keeps newest 20 + every in-use version, exit 3 when unreadable). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/rulings-2026-10-01/B/, audits/lockouts-2026-10-01/C/C1-registry-read.txt, audits/calibre-name-and-prune-2026-10-01/B/ |
| **R-751** | **The image clean-up after an app update could crash the whole controller: nil stack dereference when the app was gone (P2)** | CLOSED 2026-10-01 — controller v0.285.0, floor 0.285.0 (TestRetainImagesAfterUpdate_AppGoneDoesNotPanic seen panicking first). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/rulings-2026-10-01/B/B1-red-proofs.txt |
| **R-752** | **Four more catalog apps let a stranger lock the household out with wrong passwords for a known login name — like mealie (P3-LOW)** **Reasoning kept:** «Installed apps: a settings-only change reaches the stack file at the next sync (images equal, ≤15 min) and the running app at the next `compose up -d` — Restart/Start (measured: the env changed only at Restart), an Update, or a backup's restart (`backup.go:972`, read).» | CLOSED 2026-10-01 — decisions 58–61 (wger catalog `82fff32`; BookStack, Grafana kept; calibre-web generated ADMIN_USER catalog `e9f50b5`). Installed calibre-web's invented name → R-757. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/lockouts-2026-10-01/B/ |
| **R-753** | **Behind the tunnel every visitor reaches an app with the SAME address, so every per-address guard is an 'everyone' guard (P3-LOW)** **Reasoning kept:** «cloudflared alone on `felhom-tunnel` at the fixed `172.16.253.2`, traefik trusts forwarded headers from it only, an entrypoint middleware removes client-writable host/path/address headers» | CLOSED 2026-10-01 — controller v0.286.1 (decision 63; v0.286.0 never floored; catalog `04e9516`..`50e4fb4`) (rows R-776..R-779 carry what is left). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/lockouts-2026-10-01/A/A1-client-address.txt, audits/visitors-2026-10-01/A/ |
| **R-754** | **`01-topology-and-trust.md` §7 says cloudflared runs on the Proxmox HOST; on every box it runs INSIDE the guest (P3-LOW)** **Reasoning kept:** «the data path is not independent of the guest.» | CLOSED 2026-10-01 — document corrected (operator: the build is right). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-765** | **A new app that builds its login from a `type: password` value at EVERY start loses the login on a restore after a remove — Radicale (P2-MEDIUM)** **Reasoning kept:** «a restore carries only `type: secret` values (`stacks.PortableSecretEnvVars`, the D5 ruling — a `type: password` is an internet-reachable login and stays out of the drive's backup)» «the login file is written on the FIRST start only and lives on the data volume.» | CLOSED 2026-10-01 — Radicale's template, before publishing (login file written on first start only, on the data volume). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/new-apps-2026-10-01/box/radicale-attempt1/restore.txt, audits/new-apps-2026-10-01/box/radicale/restore.txt |
| **R-767** | **MeTube has no login at all, by design, and the box has no permanent household-only door — so it is not built (P3-LOW)** | CLOSED 2026-10-02 — controller v0.287.0 + catalog (MeTube published) behind the family gate, decision 64, no exception. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/new-apps-2026-10-01/FIT.md, audits/family-gate-2026-10-02/A/items.txt, audits/family-gate-2026-10-02/B/box/metube-fresh.txt |
| **R-772** | **A health probe that finds NO container to probe records `healthy: true` (P3-LOW)** **Reasoning kept:** «a not-run probe records `healthy: false, not_checked: true`, is looked at on the next tick» | CLOSED 2026-10-01 — controller v0.286.1 (not-run probe records healthy false, not_checked true; red-proofs RP-D1, RP-D1b). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/new-apps-2026-10-01/box/karakeep-768M/neg-and-crawl.txt, audits/visitors-2026-10-01/D/ |
| **R-773** | **After a remove + restore, an app's sign-up route block (decision 47) is gone (P3-LOW)** **Reasoning kept:** «a REMOVED app restored from its backup gets the lock record (`opened_by: restore`) and the block written before anything starts» | CLOSED 2026-10-01 — controller v0.286.0 (restored app gets lock record `opened_by: restore` + block before start; red-proof RP-D2). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/new-apps-2026-10-01/box/karakeep/restore.txt, audits/visitors-2026-10-01/D/r773-live.txt |
| **R-780** | **A permanent household gate with family accounts — the spike PASSED; the build waits for the operator's go (P2-MEDIUM)** **Reasoning kept:** «**Build requirement F1:** exceptions must be anchored (`PathPrefix(/api/v1/opds)` also matched `/api/v1/opdsx`).» | CLOSED 2026-10-02 — controller v0.287.0 (decision 64; internal/family, Család card, family_gate fields, catalog gate check-family-gate.py; Grimmory and MeTube published). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/permanent-gate-2026-10-01/VERDICT.md, audits/family-gate-2026-10-02/A/items.txt, audits/family-gate-2026-10-02/E/floor.txt |
| **R-787** | **No catalog app's LICENCE has been checked against Felhom being a paid service (P2-MEDIUM)** **Reasoning kept:** «New apps carry their licence in record row 0.1.» | CLOSED 2026-10-02 — the read (decisions are R-784, R-789..R-795). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/licences-2026-10-02/TABLE.md |
| **R-788** | **The volume-persistence gate reads an EMPTY declared volume as CLEAN (P3-LOW)** **Reasoning kept:** «an app that wrote nothing is UNDETERMINED (exit 2), never a pass.» | CLOSED 2026-10-02 — catalog (empty declared volume after the exercise is UNDETERMINED, red-proofed; APP_EXERCISE added). Bind mounts → R-805. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/family-gate-2026-10-02/C-metube-bench/C2-volume-persistence-metube.txt, audits/persistence-sweep-2026-10-02/A/RP-R788-empty-volume.txt |
| **R-790** | **Emby is proprietary: a personal, non-commercial, non-transferable licence (P2-MEDIUM)** **Reasoning kept:** «KEPT — the household is the licensee; Felhom installs the official, unchanged image.» | CLOSED 2026-10-02 — operator ruling (kept), decision 66; part of the lawyer's review R-802. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/licences-2026-10-02/TABLE.md |
| **R-791** | **n8n's Sustainable Use License allows own internal or personal use; distribution only free of charge for non-commercial purposes (P3-LOW)** **Reasoning kept:** «KEPT — the household is the user; the official, unchanged image.» | CLOSED 2026-10-02 — operator ruling (kept), decision 66; part of the lawyer's review R-802. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/licences-2026-10-02/TABLE.md |
| **R-792** | **Plex is proprietary: the HOUSEHOLD is the licensee under its own Plex account (P3-LOW)** **Reasoning kept:** «KEPT — the household is the licensee under its own Plex account.» | CLOSED 2026-10-02 — operator ruling (kept), decision 66; part of the lawyer's review R-802. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/licences-2026-10-02/TABLE.md |
| **R-795** | **recipe-importer, Felhom's own image, has no licence file (P3-LOW)** **Reasoning kept:** «Felhom's own code — no action unless it is shared with anyone.» | CLOSED 2026-10-02 — operator ruling (no action unless shared), decision 66. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/licences-2026-10-02/TABLE.md |
| **R-800** | **[P2-MEDIUM] "Delete your data from the hard drive" keeps the app's files in the household's userdata folder — and neither the dialog nor the result says so.** | CLOSED 2026-10-03 (triage, verified) — controller v0.288.0 (`580b656`, decision 67) — the remove dialog and result name the kept userdata folder; live on 9202 (`0945332`) | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-801** | **The volume-persistence gate never sent a request to ANY app: it read the routed port from label VALUES, not the label NAME (P2-MEDIUM)** | CLOSED 2026-10-02 — catalog (the fixed gate `routed_ports()` + the re-sweep of all 58: CLEAN 39 · UNDETERMINED 19 · BROKEN 0). Follow-ups R-805..R-807. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/family-gate-2026-10-02/B/volume-persistence-metube-exerciser-diag.txt, audits/family-gate-2026-10-02/B/RP-R801-routed-ports.txt, audits/persistence-sweep-2026-10-02/A/sweep/TABLE.md |
| **R-803** | **papra cannot start under its own memory limit: a fresh install is OOM-killed in its migration and crash-loops (P1-HIGH)** | CLOSED 2026-10-02 — catalog (papra 768M, mem_request 256M; measured from birth, 0 kills, persistence CLEAN; no box ran papra). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/persistence-sweep-2026-10-02/A/sweep/papra-oom-diagnosis.txt, audits/persistence-sweep-2026-10-02/A/papra/SUMMARY.md |
**Rows that never had an `R-` id** (the 2026-07-28 table of campaign findings and watches):
| Row | What | Closed | Full text |
|---|---|---|---|
| E-2d | **Prove E-2 on a fresh VM — a real host-install 1.22.0 run, Case B, a claimable customer, add a drive and unplug it.** | CLOSED — PARTIALLY PROVEN (2026-07-29). C1, C2 proven; C3, C4 proven live; C5 FAILED → R-116 (the single named open leg; R-116 since CLOSED in CLOSED-ITEMS). Per Session-C runbook §9 a failed claim closes as partially proven, no re-run. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/E2D-fresh-vm-2026-07-29.md, audits/SESSION-C-2026-07-29.md |
| — (watch, line 285) | **Storage Box **snapshots** on `storage-box-pool-1` — plan SET (daily 00:00, keep 7) but **0 taken yet**** | CLOSED 2026-10-03 (triage) — snapshots exist: R-429 recorded seven daily Storage Box snapshots (2026-09-01) | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| — (watch, line 289) | **demo-felhom's next weekly PBS backup (newest is 2026-07-26)** | CLOSED 2026-10-03 (triage) — folded into R-91, whose trigger is exactly this backup | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| — (watch, line 290) | **demo-felhom's next restore-test (84 h cadence, last 2026-07-27 06:38 UTC)** | CLOSED 2026-10-03 (triage) — restore tests have run on demo-felhom on their cadence ever since (R-672, R-689 in `CLOSED-ITEMS.md`) | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| F-CRIT-2 | **~~A failed offsite backup left a phantom snapshot that RESET the tier's freshness clock — 7 days silent~~** **Reasoning kept:** «`NewestArchiveTime` now counts only plausibly-complete entries (measured 1 MiB floor; undecidable ⇒ not counted).» | SHIPPED + PROVEN-LIVE (agent v0.106.0, 2026-07-28). Campaign fault 2 replayed on demo-hp: phantom rejected + logged once, tier DUE and backed up, no thrash on the inverse. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| F-CRIT-1 | **~~An app that fails to restart after a quiesce never alarms on any channel~~** | SHIPPED + PROVEN-LIVE (controller v0.179.0, 2026-07-28). Live on demo-hp: alarmed 9s after grace expiry; a deliberate user stop stayed silent through 9 dead-app scans. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| F-A1 | **~~A restore-test in flight made a healthy backup report as FAILED (HTTP 409 read as a tier failure)~~** **Reasoning kept:** «409 → contention: tier stays DUE, dropped before anything stops (15m), and BLOCKED alarm if contention outlives the agent's 120m ceiling (3h).» | SHIPPED + PROVEN-LIVE (controller v0.179.0, 2026-07-28). Hub DB: 409 → 0 operator emails, real failure → 1. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| C9-F1 | **~~Tier-2 „Fájlok visszaállítása" is offered for apps whose copy has no restorable file leg; restores 0 files and reports all files present~~** **Reasoning kept:** «`Tier2RestoreCoverage` refuses UP FRONT without stopping the app and NAMES the working action; a run that proceeds claims only what it **examined** and discloses that the database and volumes are not covered.» | SHIPPED + PROVEN-LIVE (controller v0.183.0, 2026-07-28). Live on demo-felhom: bookstack refused without restart; paperless re-run byte-identical, 16/16 docs. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| C9-F2 | **~~An app in a Docker crash loop never alarms on any channel; `StateRestarting` is in no down-set~~** **Reasoning kept:** «`StateRestarting` deliberately NOT added to `IsDownState` (that alarms on every deploy fleet-wide)» | SHIPPED + PROVEN-LIVE (controller v0.183.0, 2026-07-28). Sustained restarting becomes down after `crashLoopAfter`=5m; dashboard counter uses the same predicate. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| C9-F3 → R-104 | **An **interrupted offsite run leaves an exclusive restic lock the self-heal cannot reach**: `resticStep` (`offbox.go:634-648`) has `unlock --remove-all`, but `ensureOffboxRepo`'s probe fails first, `classifyResticProbe` (`offbox.go:77-93`) has no lock case → …** | FOLDED into R-104 — the same finding; R-104 has its own open row | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| D5 | **~~Move app secrets into the LOCAL recovery unit so Tier-1/Tier-2 restore stop needing the guest~~** **Reasoning kept:** «**Operator ruling 2026-07-30: `type: secret` travels (45 fields), `type: password` NEVER (7) plus a code register (`vaultwarden/ADMIN_TOKEN`); plaintext.** The exclusion is what LICENSES the plaintext — coupled, not independent.» «the register is **code, not a catalog flag** (a boundary a catalog push can move is not a boundary — R-97a).» «**Precedence: the UNIT WINS** over the guest, because the unit's secrets were captured in the same run as the dumps beside them and therefore match the data being restored; pinned both directions.» | SHIPPED + PROVEN-LIVE (controller v0.188.0, 2026-07-30). Operator ruling 2026-07-30 on which secrets travel; manifest schema 2; live proof on scratch guest (AdventureLog secrets recovered 2/2, app read seeded row over TCP); 4 red-proofs. Related: R-127. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/D5-drive-alone-restore-2026-07-30.md |
| F-DIAG | **~~Four distinct offsite failure causes collapse into two operator-visible strings~~** **Reasoning kept:** «Unclassifiable says so rather than being folded into a neighbour.» | SHIPPED (controller v0.182.0, 2026-07-28). `ClassifyOffsiteFailure` → quota / orphaned / no_repo / no_units / transport / unknown; redaction by the target's actual host/user/path. Unit-proven; not yet exercised by a live offsite failure of each class. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| F-OPS | **~~A manual `pct restore` inherits the source guest's bind mounts — during a real DR, on a different host, under pressure~~** **Reasoning kept:** «Docs only by design — the agent already neutralises binds on its own restore paths, and a second implementation would drift» | DOCUMENTED (2026-07-28) in `runbooks/RUNBOOK-manual-guest-restore.md` (mpN volumes vs binds, the mp9 source-VMID trap, strip-and-re-add, positive pre-start verification). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: runbooks/RUNBOOK-manual-guest-restore.md |
| F-REBOOT | **~~A guest rebooted during its backup does not come back — 9m47s outage with every alarm silent~~** **Reasoning kept:** «`onboot` is the deliberate-stop discriminator (already the stale-lock path's, and what `pve-guests` consults)» | SHIPPED + PROVEN-LIVE (agent v0.107.0, 2026-07-28). 60 s guest-power watchdog, retry 3x/1m-2m-4m then escalate. Live on demo-hp: 120 s unattended vs 587 s; `onboot:0` guest left stopped. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| F-LEAK | **~~A failed restore-test cannot destroy its own scratch guest (403 `VM.Allocate`); the 10-slot VMID band shrinks silently~~** **Reasoning kept:** «4th root-fenced exception, band enforced in sudoers **literally** (`pct destroy 99000[0-9] --purge`) + in code + at the caller; API destroy still tried first.» | SHIPPED + PROVEN-LIVE (agent v0.110.0 + host-install v1.21.0, 2026-07-28). Third attempt shipped: 4th root-fenced exception; live band permitted, 9201/9100/9999/990010/1 and `pct start 990000` refused. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| F-OBS | **~~`deadapp-check` leaves NO positive observable on a default (info-level) box~~** | SHIPPED + PROVEN-LIVE (controller v0.180.0 + agent v0.109.0, 2026-07-28). INFO summary every 20th scan; agent v0.109.0 fixes the same shape in the guest-power watchdog. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| E-2 | **~~Drive-role machinery around the moved vzdump target~~** | CLOSED — PARTIALLY PROVEN (Session C, 2026-07-29). C1/C2 proven in E-2d; C3, C4 proven live (R-114, R-112); C5 FAILED → R-116. Broken legs R-112, R-113, R-114, R-116 all since CLOSED (CLOSED-ITEMS). Arc's definition of done: R-106+R-109, R-108, D5. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/SESSION-C-2026-07-29.md, audits/E2D-fresh-vm-2026-07-29.md |
| E-2a | **~~The target move needs a root-fenced wrapper — the agent cannot do it~~** **Reasoning kept:** «the agent's PVE role was NOT widened.» «has NO storage-removal path (grep-assertable), is idempotent and refuses to repoint.» | SHIPPED + PROVEN-LIVE (agent v0.113.0 + host-install v1.22.0, 2026-07-29). `felhom-backup-target-apply` behind literal `FELHOM_BACKUPTARGET` sudoers alias; all five laws proven live as root on demo-hp, 0 stray storages. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| E-2b | **~~`NotifyStorageDisconnected`/`Reconnected` defined and called NOWHERE — a drive going absent emitted no event~~** | SHIPPED + PROVEN-LIVE (controller v0.184.1 + agent v0.112.0 + hub v0.81.0, 2026-07-29). Seam wired in `ReconcileDriveGates`; keying bug (guest path vs host MountPath) caught before deploy. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| E-2c | **~~E-1 put the whole-guest backups on a drive `POST /disks/eject` would eject~~** **Reasoning kept:** «NOT a role reclassification — `RoleForStorage` untouched, because on both boxes that drive is ALSO the enrolled user-data drive» | SHIPPED + PROVEN-LIVE (agent v0.112.0, 2026-07-29). Eject + decommission refuse 409 on the backup-target mount; refused live on both boxes. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
## 2026-09-17 — nothing decides by reading a Hungarian word (controller v0.251.0)
| Row | What | Closed | Full text |
@@ -1,3 +1,6 @@
> **Status 2026-10-03 (backlog triage): SHIPPED** — agent v0.84/v0.85 (CAMPAIGN-3). Kept here as history because
> `CONTEXT.md`, `controller/network-storage-nas.md` and the controller's `CONTEXT.md` link to this path.
# FOLLOW-UP — NAS automount trigger does not survive a guest reboot (agent reassert gap)
**Opened:** 2026-07-11 · **Severity:** HIGH (data-safety-adjacent for media apps) · **Class:**
File diff suppressed because one or more lines are too long
+22 -20
View File
@@ -1,27 +1,29 @@
# documentation/backlog/
**`OPEN-ITEMS.md`** is the register of open work and the file to read first — it holds only what is
open, one row per item, every row with a state and an owner. **`ROADMAP.md`** is the full history and
reasoning behind the `R-n` IDs, including shipped and killed items; an ID is minted there, and a new
instance of an existing item attaches to that ID rather than getting its own.
open, one row per item, every row with a category, a severity, a state and an owner (the layout is at its
top). **`CLOSED-ITEMS.md`** holds every finished row, compressed, with the commit that keeps its full text;
a row moves there **in the same commit that closes it** (`scripts/closed_register_gate.py` RULE 3).
**`ROADMAP.md`** holds intentions — features and spikes that do not exist yet — and **`ROADMAP-HISTORY.md`**
the shipped and killed ones. An `R-n` id is minted once and never reused.
The rest of this folder: verified-LIVE findings with implementable fix plans that are **not yet
implemented**. Preserved here
(instead of on git branches) per the trunk-based, no-branches rule — the fix itself is implemented later
**directly on `main`**, during a normal/supervised session.
## The other files in this folder — verdict per file (2026-10-03 triage)
- **FIX-M18-NOTES.md** — dump re-validation runs every 5 min (perf). **FIXED** in controller v0.62.0 @
`f8afe5c` (2026-06-14). (was on the deleted `felhom-controller` branch `fix/m18-dump-validation-cache`.)
- **FIX-M19-NOTES.md** — `deriveStackName` misattribution edge (low-incidence correctness). **FIXED** in
controller v0.62.0 @ `6bab68b` (2026-06-14). (was on the deleted branch `fix/m19-stackname-crossref`.)
| File | Verdict |
|---|---|
| `SPEC-app-data-placement-2026-08-21.md` | **KEEP — still referenced by open rows** R-352 (placement still open) and R-368. |
| `SPEC-r85-phase4-5-2026-07-26.md` | **KEEP as history — a spec of shipped work** (R-85, agent v0.104.0). `CONTEXT.md` and `architecture/00-capability-map.md` link here. |
| `FOLLOWUP-nas-automount-guest-reboot-reassert.md` | **FINISHED, kept in place** — shipped in agent v0.84/v0.85 (CAMPAIGN-3). Not moved because `CONTEXT.md`, `controller/network-storage-nas.md` and `felhom-controller/CONTEXT.md` link to this path. |
- **FOLLOWUP-golden-default-controller-tag.md** — the golden bakes a stale controller (`:0.43.0` when
queued; had rotted again to `:0.85.1` by resolution). **FIXED** in felhom-agent @ `ceca355`
(2026-07-03): `build-golden.sh` v2.0.0 makes the controller tag a MANDATORY argument (a required arg
cannot rot) and golden **0.98.3** was baked + clean-room-validated (bake → first-boot-current →
self-manage → app deploy, on the drill VM — no supervised touch of live guests needed) + published +
vouched. Evidence: `../audits/DRILL-golden-098-2026-07-03.md`.
**Moved to `documentation/archive/` on 2026-10-03 (finished; nothing open refers to them):**
Related: the live-drive fixspec (`../audits/live-drive-fixspec-2026-06-14.md`) carries the **deferred
supervised items** F9 (HDD provisioning/guest-attach), F20-BUG2 (durable_id scheme), F20-BUG3 (async
mkfs) — to be implemented in the agent/golden supervised session.
- `FIX-M18-NOTES.md` — dump re-validation every 5 min. **FIXED** in controller v0.62.0 @ `f8afe5c` (2026-06-14).
- `FIX-M19-NOTES.md` — `deriveStackName` misattribution edge. **FIXED** in controller v0.62.0 @ `6bab68b` (2026-06-14).
- `FOLLOWUP-golden-default-controller-tag.md` — the golden baked a stale controller. **FIXED** in felhom-agent
@ `ceca355` (2026-07-03): `build-golden.sh` v2.0.0 makes the controller tag a mandatory argument. Evidence:
`../audits/DRILL-golden-098-2026-07-03.md`.
- `DIAGNOSIS-f9-storage-registration-gap-2026-06-14.md` — a read-only diagnosis of June 2026, superseded by the
shipped F9 fixes (controller `CHANGELOG.md` cites it as history).
The archive also holds `OPEN-ITEMS-narratives-2026-10-03.md`: the campaign write-ups, rulings and ranking
paragraphs that sat between the register's tables until the triage, word for word.
+4 -4
View File
@@ -66,8 +66,8 @@
| R-7b | **Share backup EXECUTION** — put share data into the live tier-2 + offsite runs (the design fork reported by R-7 slice 1) | M | **SHIPPED (controller v0.145.0, 2026-07-18)** | **Viktor's ruling: Model B′ — a SIBLING shares source.** New, additive job/leg code reusing the proven primitives (tier-2 mirror seam, restic wrappers, soft-quota/enlargement gate, status recorders) while leaving **every per-app engine path byte-identical** — NOT a synthetic recovery unit (breaks on multi-drive shares, wraps 1 KB of JSON in dump machinery) and NOT engine-loop surgery. The B′ invariant is enforced by test in both tiers, red-proofed. Tier 2 → `RunSharesTier2` (legs grouped by SOURCE drive → `backups/secondary/_shares/<driveKey>/<share>`, payload at `_payload/`, layout marker LAST). Tier 3 → `runOffboxSharesLeg`: ONE extra `restic backup --tag felhom-offbox --tag _shares` placed after the app loop and BEFORE retention, so `forget --group-by host,tags` covers the new group with no flag change; a quota-blocked push degrades to the **manifest only, never to nothing**. Restore → „Megosztások" on `/backups/restore`: scratch, then a missing-only merge whose every destination is PREFIX-ASSERTED against live storage roots, definitions merged existing-wins, then `ReconcileSamba`, then the credential. The **payload** (`_shares-manifest.json` + a best-effort secret-bearing `passdb.tar`) is what makes DR return files + configuration + password rather than loose bytes. **Fold-in: samba joins the liveness set** — `EffectiveProtected` adds the CONTAINER `felhom-samba` exactly while sharing is on. **FULLY PROVEN-LIVE on demo (2026-07-18), all four legs.** (1) tier-2: real `/api/backup/tier2` trigger → `_shares` tree + marker + payload on the cross-drive target, mirrored file md5-identical, payload 0600 preserved. (2) offsite: Viktor's manual run 12:18:16Z → snapshot **`e0b9d723`** (tags `felhom-offbox,_shares`) with the payload dir + both share folders; a second run via the „Távoli mentés" button → **`4e2b15ec`**, containing `_shares-manifest.json` (418 B) AND `passdb.tar` (855 040 B), both 0600, share files with uid 1000 preserved. (3) restore round-trip: probe file + the `dokumentumok` DEFINITION deleted via the real endpoints, then „Megosztások" restore + place → `1 file(s), 1 definition(s) re-added, 1 kept, 0 refused, credential=true`; probe back md5-identical, the two pre-existing files NOT overwritten (missing-only proven on live data), definition back with its ORIGINAL flags and created_at, `smb.conf` re-rendered, `filmek` untouched. (4) liveness: samba stopped → `health_critical` pushed and hub-accepted (200) → self-healed. Remaining human leg: SMB positive auth with the real household password (never persisted by design). **Correction:** an earlier revision of this row and of the ship REPORT wrongly claimed the demo box had no offsite target — the verification read a guessed settings key (`offbox_target`) instead of the real one (`offbox`); root cause dissected in REPORT §7b. Findings: the reserved-name assumption was FALSE (`nbNameRe` accepted „_shares" as a share name — now refused); the alert/e-mail pipeline needed NO change and adds no new event type. Docs: `controller/sharing.md`; ship report `felhom-controller/REPORT.md`. |
| R-8 | DLNA (**gate input now exists — R-6 spiked 2026-07-18: SSDP reaches LAN clients from host-net**): validate Jellyfin's built-in DLNA server first; only add minidlna to the catalog if Jellyfin-DLNA fails | S | idea (unblocked) | Don't add catalog weight before proving the cheap path. **R-6 confirmed the cheap path is physically viable — Jellyfin DLNA must run host-network (same multicast constraint as R-7)** |
| R-9 | Uninstaller trio (from 07-15 Peti session): cluster-aware `felhom_guests` guard (node-local `pct list` deletes cluster-wide pveum objects); saferemove detection + time estimate + opt-in `--quick-remove` (never mutate `storage.cfg`); smarter `restore_storage` default for BYO clusters (shared storage, not local-lvm) | M | idea | Second item's rejected alternative (temp-disable-and-restore) stays rejected — crash window silently downgrades cluster wipe policy |
| R-202 | **The orphan card promises recoverability unconditionally**, which after R-198 is true going forward and false for anything already orphaned | S | **gate hit 2026-08-04 — card untouched, sentence still live** | Blocked on knowing which escrow generation an orphaned repo belongs to (R-199/R-201). A single ACK boolean can say a retained recoverable blob EXISTS but not that one COVERS this repo; a conditional promise that can still be false is worse on that surface than a hedged one |
| R-201 | **The wipe-and-recover drill** | L | **PREPARED, HALTED BEFORE THE WIPE (2026-08-04)** | Nothing irreversible done. Established live for the first time: a rebuilt box's off-site run REFUSES (the orphan card, not a silent fresh history — closes R-193 Q3), the orphan reset works move-aside-never-delete, and demo-hp's pre-rebuild key is permanently gone (superseded four hours before v0.93.0). Resume needs R-203 |
| R-202 | **The orphan card promises recoverability unconditionally**, which after R-198 is true going forward and false for anything already orphaned | S | **CLOSED 2026-10-03 — the register row moved to `CLOSED-ITEMS.md` (triage)** — **gate hit 2026-08-04 — card untouched, sentence still live** | Blocked on knowing which escrow generation an orphaned repo belongs to (R-199/R-201). A single ACK boolean can say a retained recoverable blob EXISTS but not that one COVERS this repo; a conditional promise that can still be false is worse on that surface than a hedged one |
| R-201 | **The wipe-and-recover drill** | L | **CLOSED 2026-10-03 — the register row moved to `CLOSED-ITEMS.md` (triage)** — **PREPARED, HALTED BEFORE THE WIPE (2026-08-04)** | Nothing irreversible done. Established live for the first time: a rebuilt box's off-site run REFUSES (the orphan card, not a silent fresh history — closes R-193 Q3), the orphan reset works move-aside-never-delete, and demo-hp's pre-rebuild key is permanently gone (superseded four hours before v0.93.0). Resume needs R-203 |
| R-19 | Internet-outage customer-experience drill: pull WAN on demo, verify lan_resolver path, document what the customer actually sees/does | S | idea | Flips map row E "LAN access" IMPLEMENTED→PROVEN-LIVE |
| R-34 | **Backup data lifecycle management.** An "inactive backups" section on „Távoli mentés": apps that have snapshots but no active backup — **disabled OR uninstalled** — listed with name / size / last snapshot / restorable, plus an explicit **double-confirmed per-app delete** via `restic forget --tag` + nightly prune. | M | idea | **RULING: the offsite toggle NEVER offers deletion — policy and destruction stay decoupled.** Turning backups off must never be a data-destroying act, and deletion must never hide behind a toggle. Origin: 2026-07-18 rehearsal. Pairs with R-32 (that one is the operator's view of dead bytes; this one is the customer's) |
| R-27c | **Customer self-bind, slice 2 — console-passphrase bind.** Viktor's direction: bind using a passphrase shown on the box console, alongside (not instead of) the emailed capability link. | M | idea | **Security constraints from the session ruling, all load-bearing:** passphrase **issued at customer creation**; the global-lookup endpoint must be **spray-hardened** — per-appliance **and** per-IP caps, constant-time comparison, a **single generic failure** (no oracle), alerting on abuse; an **accent-free wordlist** (console keymaps are not Hungarian); the **web capability-link path is RETAINED**; **claim-by-email is RETAINED** as the delivery-channel proof. **Also under this item:** the self-bind email gains the **public universal-ISO download link + two-line instructions** (the DIY case). **Secret-bearing per-customer ISOs are ruled OUT.** Sibling of R-27b (second-box flow) — different axis, both build on the same `/bind/` page |
@@ -167,5 +167,5 @@ Everything here is **remote-doable** — the N100 is packed, and none of it need
## Absorbed / superseded notes in this folder
- `FOLLOWUP-nas-automount-guest-reboot-reassert.md` — **shipped** (agent v0.84/v0.85, CAMPAIGN-3); keep for history
- `FOLLOWUP-golden-default-controller-tag.md` — verify against current golden flow; close or promote to an item
- `FIX-M18-NOTES.md`, `FIX-M19-NOTES.md`, `DIAGNOSIS-f9-storage-registration-gap-2026-06-14.md` — historical diagnoses; superseded by shipped fixes
- `FOLLOWUP-golden-default-controller-tag.md` — **FIXED** (agent `ceca355`); moved to `documentation/archive/` 2026-10-03
- `FIX-M18-NOTES.md`, `FIX-M19-NOTES.md`, `DIAGNOSIS-f9-storage-registration-gap-2026-06-14.md` — historical diagnoses; superseded by shipped fixes; moved to `documentation/archive/` 2026-10-03
@@ -1,3 +1,6 @@
> **Status 2026-10-03 (backlog triage): SHIPPED — a spec of finished work, kept as history** (R-85: agent v0.104.0,
> `ROADMAP-HISTORY.md`). `CONTEXT.md` and `architecture/00-capability-map.md` link to this path.
# SPEC — R-85 Phases 4 & 5: docs, then deploy
**Written** 2026-07-26 by Claude Code, at the operator's request, after Phases 0–3 shipped.