One register, enforced by a gate; closed work compressed into siblings (R-376..R-378)
gates / gates (push) Successful in 16s
gates / gates (push) Successful in 16s
Records and process only. No machine contacted. ONE REGISTER (operator ruling). 17 roadmap rows moved into OPEN-ITEMS.md keeping their identifiers, evidence and original filing dates - the oldest R-10, filed 2026-07-15, 38 days. 15 ideas stay in ROADMAP.md, which is their home; the gate exempts them by their own state word. 59 already-closed rows stay as history. Sorting rule recorded in the roadmap header: does the item assert something about the shipped product a reader could check and find false? scripts/one_register_gate.py, wired as the 11th gate. Control run: baseline passes, a planted open roadmap-only row is convicted by name, removing it passes with the file byte-identical, and a planted `idea` row is correctly exempt. Its four residual holes are in its docstring. The gate earned its keep immediately: it caught R-103, a READY finding my hand-sort mis-read as done because my regex matched the whole row where the body contains "shipped" - the gate matches the state cell. It also caught R-203 and R-163, recorded closed in the register and still open in the roadmap; the roadmap copies are marked SUPERSEDED with the register's verdict. HOUSEKEEPING. OPEN-ITEMS 672,376 -> 327,109 bytes (-51%); ROADMAP 239,306 -> 78,110 (-67%). Closed work compressed to 17% into CLOSED-ITEMS.md and ROADMAP-HISTORY.md; every entry names the commit whose git show returns the full original text. Rule-sentences are kept verbatim under "Reasoning kept" rather than judged entry by entry - 25 carry one. CONTEXT.md deliberately NOT compressed and the disagreement is argued in the report: 86% of it is standing rulings still in force, this prompt's own 3.4 says the log is never edited, and it has no per-ruling delimiter. Filed as R-377 - the problem is navigational, not volumetric. The hot/bulk placement decision was NEVER recorded as a decision anywhere - established, not assumed. Now marked [DESIGN] with a pointer honest about having no original date, given a decision-log entry that records what was rejected, and the [DESIGN]/[FACT] legend carried from 1 of 8 architecture documents to 8 of 8. Existing statements deliberately left unmarked (R-376). PROMPT-TEMPLATE gains N.7: compress what you closed, rehome live reasoning before it goes, state the register's size before and after. Ceiling R-375 -> R-378.
This commit is contained in:
+27
@@ -14,6 +14,33 @@
|
|||||||
> language, one screen, no identifiers in the prose. Same subjects, different readers; merging them
|
> language, one screen, no identifiers in the prose. Same subjects, different readers; merging them
|
||||||
> would make one of the two audiences stop reading. `STATUS.md` is also a **view of `OPEN-ITEMS.md`**
|
> would make one of the two audiences stop reading. `STATUS.md` is also a **view of `OPEN-ITEMS.md`**
|
||||||
> and holds nothing of its own; this file does hold its own content, namely the standing rulings below.
|
> and holds nothing of its own; this file does hold its own content, namely the standing rulings below.
|
||||||
|
## App data placement is a DECISION, and it had never been written down as one (2026-08-22, R-376)
|
||||||
|
|
||||||
|
**Recorded here on 2026-08-22 to give an existing decision a home. It is NOT new, and this entry is
|
||||||
|
not its date** — the choice is older and its original date is not on record. That is itself the
|
||||||
|
finding (R-376).
|
||||||
|
|
||||||
|
**The decision.** App data is placed **per volume, not per app**. `.felhom.yml` classes each volume
|
||||||
|
**hot** — database, config, cache — which goes to fast storage inside the guest and is **enforced**;
|
||||||
|
or **bulk** — media and files — which may live on slow attached storage. A photo app's database stays
|
||||||
|
on the SSD while its blobs go to the USB drive. Recorded shape:
|
||||||
|
`architecture/01-topology-and-trust.md:150-152`, now marked **[DESIGN]**.
|
||||||
|
|
||||||
|
**What follows from it, and is a consequence rather than a separate choice:** 40 of the 53 catalogue
|
||||||
|
templates declare no configurable data path, because they are all-hot apps. The 13 that do are the
|
||||||
|
media and library apps. Stated as **[FACT]** at `07-backup-architecture.md:296-299`.
|
||||||
|
|
||||||
|
**What was rejected, so the same proposal does not return.** Moving named-volume (all-hot) app data
|
||||||
|
onto the default data drive was proposed in `SPEC-app-data-placement-2026-08-21.md` §5 and is
|
||||||
|
**rejected**: it would move hot data onto storage this design classes as possibly-slow, and would put
|
||||||
|
it outside the guest vzdump that currently protects it (`01-topology-and-trust.md:154-156`). The spec
|
||||||
|
carries the correction inline.
|
||||||
|
|
||||||
|
**Why this entry exists at all.** The decision was real, implemented and load-bearing, and lived in a
|
||||||
|
single unmarked bullet. Between 19 and 22 August it was called a defect in four places (R-370),
|
||||||
|
because a reader met a marked `[FACT]` beside an unmarked choice. **A decision nobody wrote down as a
|
||||||
|
decision is one somebody will eventually report as a bug.**
|
||||||
|
|
||||||
## Read the architecture folder BEFORE calling something a defect (2026-08-22, R-370 / R-369)
|
## Read the architecture folder BEFORE calling something a defect (2026-08-22, R-370 / R-369)
|
||||||
|
|
||||||
**Between 19 and 22 August the reviewing side called a documented architectural decision a defect, in
|
**Between 19 and 22 August the reviewing side called a documented architectural decision a defect, in
|
||||||
|
|||||||
@@ -1,308 +1,296 @@
|
|||||||
# REPORT — correcting what we mis-called a defect, and finding what we wrote down and never filed (2026-08-22)
|
# REPORT — one register, and a rule that keeps it readable (2026-08-22)
|
||||||
|
|
||||||
**Documentation and survey only. No code, no version bump, no bake, no deploy, no machine contacted.**
|
**Records and process only. No controller, agent or hub code. No release, no bake, no machine
|
||||||
The previous report is preserved at `documentation/audits/REPORT-v0.218.0-r354-r355-2026-08-22.md`.
|
contacted.** Previous report preserved at
|
||||||
|
`documentation/audits/REPORT-mis-called-defect-and-sweep-2026-08-22.md`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 1. §2's four claims — ALL FOUR HELD. No halt.
|
## 1. Baselines — confirmed
|
||||||
|
|
||||||
| # | claim | verdict |
|
Controller **0.218.0**, agent **0.130.0**, golden **0.218.0 vouched**, floor **0.218.0** — all read
|
||||||
|
back from the hub, not assumed. Register ceiling was **R-375** (grepped, not trusted). All four repos
|
||||||
|
clean, `HEAD == origin/main`.
|
||||||
|
|
||||||
|
**The prompt's measurements, re-measured.** `ROADMAP.md` 249 lines / 239,306 B and `CONTEXT.md`
|
||||||
|
2,580 lines matched **exactly**. `OPEN-ITEMS.md` read 691 lines / **672,376 B / 286 entries, 134
|
||||||
|
closed** against the prompt's 642,731 / 272 / 104 — the difference is **this project's own last
|
||||||
|
session**, which added 17 rows, plus a wider closed-vocabulary on my side (I count `RESOLVED` and
|
||||||
|
`FIXED` as terminal). Longest single entry **16,108 B (R-193)**, not 15,965 — same entry, since grown.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. The 59-versus-72 reconciliation — **both were right when taken, and neither should size anything**
|
||||||
|
|
||||||
|
| method | value | stable? |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| 1 | `07-backup-architecture.md:297` — 53 templates, 52 named volumes, exactly 13 with a `backup:` block, and those 13 are exactly the ones that bind a configurable path | **HOLDS**, at `:296-299`. The heading above it reads *"Coverage per app class — and an unresolved count"*, which the sweep then followed |
|
| ids **mentioned** in ROADMAP minus ids mentioned in the register | **72** at commit `5a7502b`, **61** today | **no** |
|
||||||
| 2 | `_recovery-inventory-2026-07-28.md` Tier-3 row — *"…unpacked by no offsite action… no single action does it and no UI routes it. This is my own enumeration; it is not currently filed as a finding."* | **HOLDS**, verbatim, at `:373` |
|
| ROADMAP **rows** minus register **rows** | **90**, unchanged across all five commits checked | **yes** |
|
||||||
| 3 | `:392-400` — `rootfs`, `mp0 /var/lib/docker`, `mp1 /mnt/sys_drive` as separate volumes | **HOLDS**, at `:392-396` — **but it describes a layout no current box has.** See below |
|
| …of those 90, marked shipped/closed/killed/ruled | **59** | — |
|
||||||
| 4 | `00-capability-map.md:94` — one data volume; a local backup bounded by free space | **HOLDS**, at `:94` |
|
| …of those 90, **not** so marked | **31** | — |
|
||||||
|
|
||||||
**Claims 3 and 4 describe different layouts, six days apart, and 4 is the live one.** The inventory
|
**Why 72 became 61 without anything moving:** my own **R-369** row, written last session, *names* 11
|
||||||
(2026-07-28) records the pre-R-165 split. `felhom-agent/configs/build-golden.sh:29-40` — live source —
|
of those identifiers in its prose. A "mentioned" count therefore falls when someone merely writes
|
||||||
bakes **ONE** data volume at `/var/lib/felhom`, with `/var/lib/docker` and `/mnt/sys_drive` as two
|
about the problem. **That measure is unusable for sizing a migration.**
|
||||||
**binds of that same volume**, and states at `:99` that *"There is deliberately no mp1"*.
|
|
||||||
|
|
||||||
**So the prompt's own §1.2 instruction is out of date:** it asked me to say that named volumes and
|
**And 59 is real** — it is the *history* half of the row-based population. So the prompt's figure and
|
||||||
first-tier backups are *"on two different guest volumes on one physical disk"*. They are on **one**
|
mine were measuring the two halves of the same 90. **The count that matters is 31**, and the gate
|
||||||
guest volume, by two binds. The disk claim is stated precisely in §4 below.
|
later found the true figure is **32** (§4).
|
||||||
|
|
||||||
**A correction to my own §2 working.** In verifying claim 2 I reported that a literal grep matched
|
|
||||||
`:373`. It did not — it returned nothing, and what printed was the second grep in the same cell.
|
|
||||||
`**not** currently filed` does not match `not currently filed`, because of the markdown bold. The
|
|
||||||
claim still holds (I read the line), but the check I quoted was wrong — and that false zero turned out
|
|
||||||
to matter, see §3.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 2. The sweep — three counts, and the control that earned them
|
## 3. The population, and the rule used to sort it
|
||||||
|
|
||||||
**Method** (`scripts` not committed; it is a throwaway, and the terms below are the durable part):
|
**The rule, stated so it need not be re-invented:**
|
||||||
enumerate every **survey-class** document — inventory / audit / spike / review / diagnosis / recon /
|
|
||||||
campaign / report / validation / rehearsal / walk / finding / spec / triage — under
|
|
||||||
`documentation/{audits,architecture,pilot,backlog}`, excluding runbooks, templates, READMEs,
|
|
||||||
CHANGELOGs and ROADMAP, because those instruct rather than conclude. For each, ask whether any
|
|
||||||
`OPEN-ITEMS.md` row cites it by filename, then search every line for the **shapes** an unfiled gap
|
|
||||||
takes.
|
|
||||||
|
|
||||||
| count | value |
|
> **Does the item assert something about the shipped product that a reader could go and check, and
|
||||||
|---|---|
|
> find false?** If yes it is a **finding** and belongs in the register. If it proposes something that
|
||||||
| **documents examined** | **113** (survey-class; 114 after this session added one) |
|
> does not exist yet — a feature, a spike, a curation task — there is nothing to be wrong about, and
|
||||||
| **gaps found** (documents saying, in their own words, that something was not filed) | **14** statements |
|
> it stays in the roadmap as an intention.
|
||||||
| **of those, already filed** | **2** — and the other 12 break down in §3 |
|
|
||||||
|
|
||||||
**The search terms, so the next person can widen them:**
|
Two refinements the population forced: **an owed operator decision moves too** (it is work owed, not
|
||||||
|
an idea), and **an item already satisfied moves as CLOSED**, so nobody redoes it.
|
||||||
|
|
||||||
```
|
| group | count | disposition |
|
||||||
not (currently )?filed never filed no finding not a finding
|
|
||||||
unresolved unfiled disagreement no single action
|
|
||||||
nothing (does|routes|reads|consults) never been should be
|
|
||||||
ought to is not (currently )?(tested|proven|observed|covered|wired)
|
|
||||||
has (never|not) been (seen|observed|walked|proven|done)
|
|
||||||
no (test|row|register) (pins|covers|cites) TODO FIXME ⚠ not covered gap
|
|
||||||
```
|
|
||||||
|
|
||||||
Every word-gap in the first four patterns is `[*_`~ ]*`, i.e. **markdown emphasis is tolerated** —
|
|
||||||
see §3.
|
|
||||||
|
|
||||||
### The positive control — and it convicted the sweep twice before it convicted the corpus
|
|
||||||
|
|
||||||
**Plant → find → remove → fail to find**, on a scratch copy of the whole `documentation/` tree:
|
|
||||||
|
|
||||||
| step | strongest-shape hits |
|
|
||||||
|---|---|
|
|
||||||
| unplanted scratch copy | **14** |
|
|
||||||
| a synthetic gap planted, **bolded and on one line** | **15** — convicted by name and quoted back |
|
|
||||||
| scratch discarded, real corpus re-run | **14** |
|
|
||||||
|
|
||||||
**The control found two defects in my own sweep before it found anything in the corpus, and both were
|
|
||||||
false zeros of exactly the class this session is about:**
|
|
||||||
|
|
||||||
1. **Markdown emphasis broke the strongest pattern.** The single most important line in the corpus is
|
|
||||||
written `it is **not** currently filed as a finding`, and `not (currently )?filed` does not match
|
|
||||||
it. Fixed by tolerating `[*_`~ ]*` between words.
|
|
||||||
2. **The reporter re-searched a truncated copy of the line.** Hits were stored as `line[:200]`; that
|
|
||||||
line is a long table row and the phrase sits past column 200, so the detector caught it and the
|
|
||||||
**report** dropped it. Fixed by matching the full line and truncating only for display. This alone
|
|
||||||
moved the count **12 → 14**.
|
|
||||||
|
|
||||||
A sweep whose first two runs would have under-reported by 2 is exactly why the control is mandatory.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 3. What the 14 were, judged
|
|
||||||
|
|
||||||
| verdict | n | which |
|
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| **never a gap** — a reasoned "no finding" | 4 | `_design-review.md:9` (nothing deferred), `CAMPAIGN-11:501` (a measurement was honest), and the two **explicit `### Not filed` sections** in `CAMPAIGN-10-two-storage-soak` and `SPIKE-recovery-unit-space` — each item disposed with a reason. **This is good practice, not a failure, and it is what the rest should look like.** |
|
| **open work — findings and owed items** | **17** | **moved to the register**, keeping identifier, evidence and original filing date |
|
||||||
| **already filed** | 2 | `REPORT-DRILL-backup-truth:321` (its Part 5 became R-360 and R-364, filed the same session) and `REPORT-DRILL:569` (the hub's controller-version blindness — the register already covers it, 2 hits; not re-filed) |
|
| **ideas and proposals that were never findings** | **15** | **stay in `ROADMAP.md`** — see below |
|
||||||
| **still open → NEWLY FILED** | 5 | R-371, R-372, R-373, R-374, R-375 |
|
| **already closed** | **59** | stay as history in the roadmap |
|
||||||
| **the headline** | 3 | `_recovery-inventory:373`, `:1043` and `07-backup-architecture.md:337` — all three the same gap, and it turns out it **was** given a number |
|
|
||||||
|
|
||||||
### The headline: it was filed. In the other register.
|
**Where the 15 ideas live, since "not the register" is not an answer:** they stay in **`ROADMAP.md`**,
|
||||||
|
which is exactly what that file says it is — *"the prioritized decision log of planned/open work…
|
||||||
The gap the 2026-08-21 drill rediscovered **was already numbered R-107**, with a full write-up:
|
Items are intentions"*. The gate exempts them **by their own state word**, so they are not
|
||||||
|
second-class; they are correctly filed. They are R-6, R-8, R-9, R-12, R-14, R-19, R-34, R-45, R-46,
|
||||||
> `ROADMAP.md:122` — *"**No offsite action unpacks the named-volume tars Tier-3 captures on every
|
R-56, R-58, R-62, R-65, R-69, R-72.
|
||||||
> run.** `ReconstituteFromOffsite` skips the unit outright … `PlaceOffsiteRestore` places it only when
|
|
||||||
> the live unit is ABSENT"* — **M, READY, 2026-07-28**
|
|
||||||
|
|
||||||
and cross-referenced twice in `07-backup-architecture.md` (`:337`, `:902`). **It is absent from
|
|
||||||
`OPEN-ITEMS.md`**, which opens with the words *"the single source of truth for open work"*.
|
|
||||||
|
|
||||||
**The dating makes it a rule violation, not a gap in the rules.** `OPEN-ITEMS.md` was created and the
|
|
||||||
template gained its *"single source of truth"* bullet on **2026-07-27** (`655b69f`). R-107 went into
|
|
||||||
ROADMAP alone on **2026-07-28** (`070b0ce`) — **the day after**.
|
|
||||||
|
|
||||||
**Measured scope: 72 `R-` ids are in ROADMAP and not in OPEN-ITEMS; 29 are not marked shipped, closed
|
|
||||||
or killed.** Most of the 29 are feature *ideas*, which arguably belong only in ROADMAP. A minority are
|
|
||||||
**findings**: R-30, R-31, R-32 (all P2-HIGH), R-35, R-40, R-76, R-79, R-25, R-49, R-10, R-107.
|
|
||||||
|
|
||||||
**The risk is not double-minting** — the two files share ids and OPEN-ITEMS' ceiling (368) is above
|
|
||||||
ROADMAP's (331). **The risk is rediscovery**: a session greps one file, finds nothing, and redoes the
|
|
||||||
work. That is precisely what happened, and it cost an evening and a night. **Filed as R-369, HIGH.**
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 4. Part 1 — the record corrected
|
## 4. The migration — 17 items, and the oldest
|
||||||
|
|
||||||
### The specification
|
Each moved **verbatim**, nothing added or reinterpreted; the roadmap keeps its copy marked
|
||||||
|
`MOVED -> OPEN-ITEMS.md` and is not deleted, because that file's job is history.
|
||||||
|
|
||||||
`SPEC-app-data-placement-2026-08-21.md` now opens with a marked **⚠ CORRECTED 2026-08-22** block and
|
| id | originally filed | note |
|
||||||
carries four inline `[CORRECTED 2026-08-22]` marks. **Nothing was deleted; every measurement stands.**
|
|---|---|---|
|
||||||
|
| **R-10** | **2026-07-15** | **the oldest — 38 days** (T-6E-1, dir-fsync asymmetry) |
|
||||||
|
| R-25, R-40, R-49 | 2026-07-19 | |
|
||||||
|
| R-30, R-31, R-32, R-35 | 2026-07-21 | three of them marked **[P2-HIGH]** |
|
||||||
|
| R-78 | 2026-07-25 | an **owed operator decision**, not a defect |
|
||||||
|
| R-76, R-79 | 2026-07-26 | |
|
||||||
|
| R-96 | 2026-07-27 | **moved as CLOSED** — both rules are committed at `workspace-CLAUDE.md:48-70` under a heading naming R-96 |
|
||||||
|
| R-102, R-104, R-105, **R-107** | 2026-07-28 | R-107 **moved as CLOSED** — shipped as R-354 on 2026-08-22 |
|
||||||
|
| **R-103** | 2026-07-28 | **found by the gate, not by me** — see §5 |
|
||||||
|
|
||||||
What it got wrong: it treated the absence of a storage field on 40 templates as a choice being denied.
|
**Staleness named, not edited away**, as instructed:
|
||||||
The architecture states the rule and states it as **enforced**:
|
|
||||||
|
|
||||||
> *"**App data placement is per-volume, not per-app:** `.felhom.yml` classifies each volume **hot**
|
- **R-104** — *partly stale.* The self-heal it calls unreachable **was built**: `resticStep`
|
||||||
> (DB/config/cache → fast storage, **enforced**) vs **bulk** (media/files → may be slow)."*
|
escalates to `unlock --remove-all` and retries once (`offbox.go:763-768`), and `unlockStale` runs
|
||||||
> — `01-topology-and-trust.md:150-152`
|
before every off-site run and restore (`:1274`). Its premise is also doubtful — the probe is
|
||||||
|
`restic cat config`, a read that takes no lock. **What remains true:** `ClassifyOffsiteFailure`
|
||||||
|
(`:179-193`) still has no lock case.
|
||||||
|
- **R-105** — *partly fixed by its own update.* The `drives` third was traced and populated
|
||||||
|
2026-07-28; the other two thirds were not re-verified and are carried as written.
|
||||||
|
|
||||||
The 40 are all-hot apps; the 13 are the bulk apps. **And the deploy page has been telling the customer
|
**No halt.** I checked the three READY findings for urgency before migrating them quietly. R-104 is
|
||||||
exactly this the whole time** — `deploy.html:624-625`: *„A kiválasztott meghajtón az alkalmazás
|
the one that reads urgent — *"the tier stays dead until a human runs `restic unlock --remove-all`"* —
|
||||||
**fájljai** (média, dokumentumok) tárolódnak. Az **adatbázis a gyors belső SSD-n** fut."*
|
and **it is not**, for the reasons above. Nothing in the 31 is both open and urgent.
|
||||||
|
|
||||||
### The disk claim, stated precisely
|
|
||||||
|
|
||||||
- **REAL:** a **physical-disk failure** loses the app's data and its first-tier copy together. That is
|
|
||||||
what the off-site and whole-machine tiers exist for, and it is **equally true of a drive-resident
|
|
||||||
app**, whose unit sits beside its data on the drive deliberately so a restore needs the drive and
|
|
||||||
nothing else.
|
|
||||||
- **OVERSTATED:** a **full data volume stopping the operating system.** The OS rootfs is a separate
|
|
||||||
volume, and the capture floor refuses per app before exhaustion (`00-capability-map.md:94`).
|
|
||||||
Watched working 2026-08-21 with `/var/lib/felhom` at 99%: the reserve refused one app per run, told
|
|
||||||
the hub, and all 15 containers stayed healthy.
|
|
||||||
- **WITHDRAWN:** the comparison to Tier 2's same-disk refusal (`tier2.go:329`). Tier 2 refuses a
|
|
||||||
**second** copy on the same disk; Tier 1's unit is *meant* to sit beside the data.
|
|
||||||
|
|
||||||
### Rows re-framed
|
|
||||||
|
|
||||||
- **R-352** — measurements (1)–(4) all stand; the conclusions drawn from (1) and (3) are marked
|
|
||||||
re-framed. (2) is now filed on its own as **R-368**; (4) needs no ruling.
|
|
||||||
- **R-356** — **re-checked and it SURVIVES UNCHANGED, strengthened.** Because the 40-class correctly
|
|
||||||
has no `HDD_PATH`, a restore that reads that as *"the app is not installed"* is misreading a correct
|
|
||||||
configuration. One sentence in it that leaned on the old framing is corrected in place.
|
|
||||||
|
|
||||||
### My own errors, named — R-370
|
|
||||||
|
|
||||||
**Four instances, not three.** The prompt said three; the evidenced count is four, all authored
|
|
||||||
2026-08-21: SPEC §1, SPEC §2.3, SPEC §5, and R-352 point (3). Recorded as a **process** failure with
|
|
||||||
its mechanism — the register and live source were read, `documentation/architecture/` was not — and
|
|
||||||
the missing step is now in the template. Also written into `CONTEXT.md`.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 5. What still needs the operator's ruling — **from this specification, almost nothing**
|
## 5. The gate — and it convicted me before it convicted anything else
|
||||||
|
|
||||||
- **The placement question is ANSWERED and the answer is no.** Points 1, 2 and 3 of the spec's §5 are
|
`scripts/one_register_gate.py`, wired into `scripts/repo_gates.py` as **`one-register`** (11 gates now,
|
||||||
withdrawn as a live question; hot data belongs where it is.
|
all OK). It fails when a `ROADMAP.md` row is **neither an idea nor done** and has **no counterpart row
|
||||||
- **Point 4 is the only thing left**, and it is smaller than it looked — see R-368 below.
|
in `OPEN-ITEMS.md`**. The predicate is the roadmap's **own state column**, so it reads data that
|
||||||
- **Point 5 needs no ruling.** The Drives count is honest; it is a wording question the design system
|
already exists rather than asking anyone to maintain a new marker.
|
||||||
already owns.
|
|
||||||
|
|
||||||
**One new thing does want your ruling, and it is R-369:** whether the two registers become one, or
|
**The control — plant → convict → remove → pass:**
|
||||||
whether a gate enforces that a not-done `ROADMAP` row has an `OPEN-ITEMS` counterpart. Triage the 29
|
|
||||||
first — most are ideas, a minority are findings.
|
| step | result |
|
||||||
|
|---|---|
|
||||||
|
| baseline | **PASS** |
|
||||||
|
| plant an open roadmap-only row | **CONVICTED by name**, rc=1, quoted back |
|
||||||
|
| remove the plant | **PASS**, file byte-identical (md5) |
|
||||||
|
| plant an `idea` row | **not convicted** — the exemption is real, not a blanket pass |
|
||||||
|
|
||||||
|
**The control caught my control first.** The first plant did *not* convict, and the counts did not
|
||||||
|
move at all. Cause: **`ROADMAP.md` has no trailing newline**, so `>>` appended the row onto the last
|
||||||
|
line and it never started at column 0. A flaw in my test, not the gate. Third session running in which
|
||||||
|
the instrument caught the operator before the corpus.
|
||||||
|
|
||||||
|
**And the gate then caught three rows my hand-sort missed** — the reason is worth recording because it
|
||||||
|
is this project's most repeated shape: **my sorting regex matched the whole row, where a finding's
|
||||||
|
body routinely contains the word "shipped"; the gate matches the state cell only.**
|
||||||
|
|
||||||
|
- **R-103** (`READY — 2026-07-28`) — a genuine finding, mis-read by me as done. **Migrated.**
|
||||||
|
- **R-23** (`BANKED in full`) and **R-13** (`first slice PROVEN-LIVE`) — this project's own done-words,
|
||||||
|
which the gate did not know. Vocabulary extended; both are correctly exempt.
|
||||||
|
|
||||||
|
**And on the split it caught a genuine disagreement between the two files:** **R-203** and **R-163**
|
||||||
|
are recorded closed in the register and still open in the roadmap — R-203 even carries two
|
||||||
|
contradictory roadmap rows. The register is right in both cases; the roadmap copies are marked
|
||||||
|
`SUPERSEDED 2026-08-22` with the register's verdict.
|
||||||
|
|
||||||
|
### What the gate cannot see — named, not implied
|
||||||
|
|
||||||
|
1. **A finding filed with the state `idea` escapes.** The state column is a human judgement.
|
||||||
|
2. **A finding written in prose with no `R-` identifier escapes entirely** — this gate matches ids.
|
||||||
|
That is the previous session's sweep territory and the template rule *"an enumerated gap becomes a
|
||||||
|
row"*.
|
||||||
|
3. **A finding that never reaches the roadmap escapes.** Nothing here reads audits or spikes.
|
||||||
|
4. It checks a counterpart **exists**, never that the two agree. A stale register row passes.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 6. Part 4 — the three answers, and the finding is smaller and different than believed
|
## 6. Housekeeping — sizes before and after
|
||||||
|
|
||||||
**1. Is the default store consulted at deploy time, for the 13 apps that take a path? — YES.**
|
| file | before | after | |
|
||||||
`internal/web/templates/deploy.html:612`:
|
|---|---|---|---|
|
||||||
|
| `backlog/OPEN-ITEMS.md` | 691 lines / **672,376 B** | 594 lines / **327,109 B** | **−51%**, and it now holds open work only |
|
||||||
|
| `backlog/CLOSED-ITEMS.md` | — | 156 lines / 61,580 B | **new sibling**, 128 compressed closed entries |
|
||||||
|
| `backlog/ROADMAP.md` | 249 lines / **239,306 B** | 160 lines / **78,110 B** | **−67%** |
|
||||||
|
| `backlog/ROADMAP-HISTORY.md` | — | 116 lines / 28,683 B | **new sibling**, 105 finished items |
|
||||||
|
| `CONTEXT.md` | 2,580 lines / 217,260 B | 2,607 lines / 219,104 B | **deliberately not compressed** — §7 |
|
||||||
|
|
||||||
```
|
**A sibling, not the bottom of the file** — appending keeps the byte count and the scroll, which is
|
||||||
{{else if and .IsDefault (not .NotAllowed)}}selected{{end}}
|
the thing being fixed. Closed work compressed to **17%** of its bytes in both files.
|
||||||
```
|
|
||||||
|
|
||||||
For a new deploy the default drive **is pre-selected**. `DeployStoragePath` embeds
|
**Nothing was deleted.** Every compressed entry ends `full text: git show fddfe00ce268:<path>`.
|
||||||
`settings.StoragePath` (`web/handlers.go:89-99`), so `.IsDefault` resolves.
|
|
||||||
|
|
||||||
**So `// new apps use this by default` (`settings.go:453`) is IMPRECISE ABOUT THE MECHANISM, NOT
|
**Load-bearing reasoning was not compressed away.** Rather than judge 134 entries by hand, the
|
||||||
FALSE.** The earlier claim — *"the deploy route never reads it"* — is **wrong**, and it is wrong for
|
compressor **keeps any sentence stating a rule, a fence or a deliberate refusal, verbatim**, under
|
||||||
the same reason as everything else this week: the grep behind it
|
**Reasoning kept** — 25 entries carry one. Spot-checked: R-320's kept sentence itself records that its
|
||||||
(`grep -nE 'GetDefaultStoragePath|primaryHDDPath|IsDefault' deploy.go manager.go`) **searched Go files
|
rule is now standing rule 5 in `workspace-CLAUDE.md`, and R-110's rule is in `felhom.eu/CLAUDE.md`
|
||||||
and never the templates.**
|
("The installer publishes by TAG, not by push (R-110)") — **both already homed outside the register,
|
||||||
|
verified by grep with a negative control.**
|
||||||
|
|
||||||
**The residual, and it is the whole finding:** the default lives in the **template**, not the server.
|
### The compressor moved six rows it should not have — caught and reversed
|
||||||
`POST /api/stacks/<n>/deploy` takes `values` verbatim; omit `HDD_PATH` and `withPathVars`
|
|
||||||
(`stacks/deploy.go:584`) gets `""` and no default applies. **That is why the invariant has no test —
|
|
||||||
there is nothing server-side to test.** Filed **R-368, LOW**.
|
|
||||||
|
|
||||||
**2. What the label promises the customer, quoted:** `storage.html:469` —
|
**`PARTLY CLOSED` and `OPEN — NOT FIXED` both matched a closed-vocabulary applied to the whole status
|
||||||
**„Legyen alapértelmezett új telepítéseknél"** ("Be the default for new installations"), with the
|
field.** R-123, R-190, R-214, R-264, R-295 and **R-352** were moved out of the register. Caught by a
|
||||||
badge **„Alapértelmezett"** at `:34`. **The promise is kept**: it is the default for new installs,
|
follow-up check in the same session and **restored verbatim from commit `fddfe00ce268`** — not from
|
||||||
which is exactly what the pre-selection does. It does not promise that every app's data goes there.
|
the compressed form, because an open row keeps its detail. **The same bug, in the same session, as the
|
||||||
|
one the gate had.** Filed as **R-378** so the next person to write a status predicate reaches for the
|
||||||
**3. What the Drives count counts:** `countAppsUsingPath` (`web/handlers.go:2162-2175`) counts
|
leading verdict rather than the whole field.
|
||||||
deployed apps where `appCfg.Env["HDD_PATH"] == storagePath`. **A named-volume app has no `HDD_PATH`,
|
|
||||||
so it can never be counted — by construction, not by accident.** The number is honest; it means *"apps
|
|
||||||
that place bulk data on this drive"*, not *"apps using storage"*. Worth one sentence of wording, not a
|
|
||||||
ruling.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 7. Part 3 — the template's two new rules
|
## 7. Why `CONTEXT.md` was not compressed — a disagreement with the task, stated
|
||||||
|
|
||||||
**It had neither.** `enumerat` → 0 hits, `register row` → 0 hits; `architecture` appeared 9 times but
|
**86% of that file — 187,913 of 217,260 bytes — is one `## Standing rulings` section**, carrying 39
|
||||||
§4 item 4 named only `02-controller-module-map.md`, and the S-1 rule at the end governs *updating* a
|
`S-` ids and 153 bullets under a single heading. **Standing rulings are live reasoning, not finished
|
||||||
design doc, not *reading* one first. **No halt.** There is no separate authoring companion.
|
work.**
|
||||||
|
|
||||||
**Rule 1, in §4 where files are read** — abridged; the full text carries a file→area table for all
|
**This prompt's own §3.4 says the decision log is *"dated, never edited afterwards"*** and is *"the
|
||||||
eight architecture documents:
|
only place that answers 'has this been proposed before, and why did we say no?'"*. Compressing it
|
||||||
|
destroys exactly that, and there is no per-ruling delimiter, so a mechanical split risks cutting a
|
||||||
|
live ruling from its reason — **the failure this whole arc is correcting.** 13 mentions of
|
||||||
|
`SUPERSEDED` sit inside that blob and cannot be separated from live text safely today.
|
||||||
|
|
||||||
> **THE ARCHITECTURE DOCUMENT FOR THE AREA THIS TASK TOUCHES — NAME IT AND SAY WHAT IT SAYS.** Not
|
**So the problem is navigational, not volumetric, and the fix is structural:** give each ruling a
|
||||||
> "read the architecture folder": name the file, and state in one line what it rules about this area.
|
sub-heading with its `S-` id and date, and it becomes linkable and findable **without a word being
|
||||||
> **A prompt that cannot name one says so explicitly, and that absence is itself recorded** — an
|
edited**. Filed as **R-377 (LOW)** rather than done, because it is a careful pass of its own.
|
||||||
> undocumented architectural decision is how a deliberate design gets "fixed" by someone who did not
|
|
||||||
> know it was one.
|
The file grew by 1,844 bytes — the new decision-log entry in §8.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Where the hot/bulk decision was recorded — **nowhere. That is the finding.**
|
||||||
|
|
||||||
|
Established by reading, not by citation: it exists as **one unmarked bullet** at
|
||||||
|
`architecture/01-topology-and-trust.md:150-152`. **No dated entry in the decision log, no `R-` row, no
|
||||||
|
record of when it was taken.** The only mention in `CONTEXT.md` before today was the entry I wrote
|
||||||
|
*yesterday about failing to read it*.
|
||||||
|
|
||||||
|
Meanwhile its **consequence** — 40 of 53 templates declare no path — is marked **[FACT]** at
|
||||||
|
`07-backup-architecture.md:296-299`. **A reader met a marked observation beside an unmarked choice**,
|
||||||
|
and reasonably asked whether it should be so. Four times.
|
||||||
|
|
||||||
|
**Marker usage, measured** (the prompt said "three times"; the real figure is 45):
|
||||||
|
|
||||||
|
| | before | after |
|
||||||
|
|---|---|---|
|
||||||
|
| documents carrying the legend | **1 of 8** (`07`: 10 `[DESIGN]`, 35 `[FACT]`) | **8 of 8** |
|
||||||
|
|
||||||
|
**Done:**
|
||||||
|
|
||||||
|
1. **The legend is carried into all seven** other architecture documents — same wording, no third
|
||||||
|
marker invented — each stating explicitly that **an unmarked statement means *not yet classified*,
|
||||||
|
never *observed***.
|
||||||
|
2. **The hot/bulk split is marked `[DESIGN]`**, with a pointer that is honest about what it can point
|
||||||
|
at: the decision has **no original date on record**, and the new log entry exists *to give it a
|
||||||
|
home, not to claim it was decided then*.
|
||||||
|
3. **A decision-log entry** in `CONTEXT.md` — what was chosen, what follows from it, and **what was
|
||||||
|
rejected** (moving all-hot data to the data drive), so the same proposal does not return.
|
||||||
|
|
||||||
|
**Deliberately not done, and filed:** the existing statements in those seven documents were **not**
|
||||||
|
swept into one marker or the other. A wrong mark is worse than none. **R-376 (MEDIUM)** records the
|
||||||
|
remainder and the rule: mark what a session touches.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. The template's closing step, as written
|
||||||
|
|
||||||
|
It had none — `compress` → 0 hits, `CLOSED-ITEMS` → 0, `size before` → 0, with a negative control.
|
||||||
|
Added as **§N.7**, abridged here:
|
||||||
|
|
||||||
|
> **N.7 Housekeeping — before the report, not after (2026-08-22 ruling)**
|
||||||
>
|
>
|
||||||
> **Three sources, in this order, before any claim: the architecture folder holds the REASONING, the
|
> 1. **Compress what this session closed.** A closed row keeps its title, the version it shipped in,
|
||||||
> register holds the WORK, source holds the TRUTH.**
|
> 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
|
||||||
> **And the test that catches it: _is what I am about to call a defect something we chose?_** If it
|
> `git show` returns the full original text. **Open rows are not touched — their detail is doing a
|
||||||
> was chosen and the choice is wrong, that is **a proposal to change a decision** — it goes to the
|
> job.**
|
||||||
> operator as a decision, not filed as a bug. **Cost of learning this (R-370):** between 19 and 22
|
> 2. **Rehome live reasoning before compressing it away.** … **Where it is a decision, mark the
|
||||||
> August a documented placement decision was called a defect in four places.
|
> resulting shape `[DESIGN]` in the architecture document and point it at the log entry.**
|
||||||
|
> **Losing a reason is how a deliberate design becomes a bug in someone's eyes** — that cost four
|
||||||
**Rule 2, on the `OPEN-ITEMS.md` bullet, where a survey session lands:**
|
> mis-filed defect reports in August 2026 (R-370, R-376).
|
||||||
|
> 3. **State the register's size in the report, before and after.** A number every session is what
|
||||||
> **AN ENUMERATED GAP BECOMES A ROW, IN THE SAME SESSION. PROSE IS NOT A RECORD.**
|
> makes growth visible; prose about tidiness is not a mechanism.
|
||||||
>
|
|
||||||
> This binds **surveys, inventories, spikes, reviews and diagnoses**, not only implementation
|
|
||||||
> sessions… If a document says a thing is missing, unhandled, unreachable or *"not currently filed"*,
|
|
||||||
> it does not leave the session as prose. It leaves as a row here, with a rank and an owner. Writing
|
|
||||||
> *"not filed"* is not a disposition; it is a note that the work was seen and dropped.
|
|
||||||
>
|
|
||||||
> **A row in `ROADMAP.md` alone does not satisfy this** … invisible to every standing rule that says
|
|
||||||
> *"grep the register before minting"* (**R-369**).
|
|
||||||
>
|
|
||||||
> **The cost, recorded so the rule can be narrowed later rather than becoming permanent by accident:**
|
|
||||||
> R-107 … was enumerated on **2026-07-28** … and never entered here. **It was rediscovered from
|
|
||||||
> scratch 25 days later by an overnight drill**, and shipped as R-354.
|
|
||||||
|
|
||||||
Two rules, one place each, ~40 lines added to a 521-line document.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 8. Rows opened — ceiling R-367 → **R-375**
|
## 10. Part 4.2 — the storage default
|
||||||
|
|
||||||
| id | rank | what | first written down | age |
|
**Confirmed correctly filed.** **R-368**, rank **LOW**, open, and it states the corrected position:
|
||||||
|---|---|---|---|---|
|
the default **does** apply at deploy time (`deploy.html:612` pre-selects `.IsDefault`), the residual
|
||||||
| **R-368** | LOW | The storage default **does** apply at deploy time (`deploy.html:612`); the earlier "never reads it" was wrong. Residual: the default lives in the template, not the server, so the API has none and nothing server-side can be tested | 2026-08-21 (as the wrong claim) | 1 d |
|
is that it lives in the **template, not the server**, so the API has no default and there is nothing
|
||||||
| **R-369** | **HIGH** | **Two registers.** 72 ids in ROADMAP only, 29 not done, some of them findings. R-107 sat there 25 days and was rediscovered by a drill | 2026-07-28 | **25 d** |
|
server-side to test.
|
||||||
| **R-370** | CLOSED | Process: a documented decision called a defect four times; architecture folder never read. Rule now in the template | 2026-08-19 | 3 d |
|
|
||||||
| **R-371** | LOW | The off-site tier is the only tier that announces nothing on success | 2026-08-05 | 17 d |
|
|
||||||
| **R-372** | LOW | A Tier-2 copy that has **never** been produced (source missing) is not surfaced distinctly | **2026-07-15** | **38 d — the oldest** |
|
|
||||||
| **R-373** | LOW | `SysDataGrowGB` is the intended sizing lever, it works, and nothing sets it | 2026-08-02 | 20 d |
|
|
||||||
| **R-374** | LOW | Three C1 refusal cases judged borderline, left unfiled **and never named**, so nobody can re-judge them | 2026-08-08 | 14 d |
|
|
||||||
| **R-375** | LOW | A PBS datastore audit signal noted and explicitly not filed | 2026-08-18 | 4 d |
|
|
||||||
|
|
||||||
**Oldest gap recovered: R-372, written down 2026-07-15, 38 days.**
|
**No row anywhere still asserts the default never applies.** The one occurrence of *"the deploy route
|
||||||
**Most consequential: R-369**, because it explains the other seven.
|
never reads it"* in the register is inside R-368's own quotation of the claim it corrects; the SPEC
|
||||||
|
carries `[CORRECTED 2026-08-22]` blocks; `grep` across the register, `CLOSED-ITEMS.md` and the SPEC
|
||||||
|
returns nothing else.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 9. What was dropped, and observations
|
## 11. Rows and ceiling
|
||||||
|
|
||||||
**Dropped: nothing.** Parts 1, 2, 3 and 4 all completed. Part 4 was droppable-first and was done.
|
**Opened:** R-376 (MEDIUM), R-377 (LOW), R-378 (CLOSED same session).
|
||||||
|
**Migrated in, keeping their original identifiers and dates:** R-10, R-25, R-30, R-31, R-32, R-35,
|
||||||
|
R-40, R-49, R-76, R-78, R-79, R-96 (closed), R-102, R-103, R-104, R-105, R-107 (closed).
|
||||||
|
**Restored after a compressor error:** R-123, R-190, R-214, R-264, R-295, R-352.
|
||||||
|
**Ceiling R-375 → R-378.**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12. What was dropped, and observations
|
||||||
|
|
||||||
|
**Dropped: nothing from Parts 1–4.** Part 4 (droppable first) and Part 3 both completed.
|
||||||
|
**One thing deliberately not done and argued rather than obeyed:** compressing `CONTEXT.md` — §7,
|
||||||
|
filed as R-377.
|
||||||
|
|
||||||
**Observations — noticed, not acted on:**
|
**Observations — noticed, not acted on:**
|
||||||
|
|
||||||
- **The two `### Not filed` sections are the model.** `CAMPAIGN-10-two-storage-soak:383` and
|
- **`ROADMAP.md` has no trailing newline.** It broke my own gate control and would break any future
|
||||||
`SPIKE-recovery-unit-space:228` list what they decided not to file **and why, item by item**. That
|
`>>` append. One byte; not fixed here because it belongs to whoever next edits that file
|
||||||
is a disposition, not a shrug. If the new rule needs an exemplar, those are it.
|
deliberately.
|
||||||
- **`07-backup-architecture.md:337` already names this gap and points at R-107**, so the architecture
|
- **R-203 has two contradictory rows in the roadmap** — one open, one shipped. Only the open one was
|
||||||
doc was right and current while the register was empty. The document was not the weak link.
|
marked superseded; the duplicate remains as history.
|
||||||
- **The `_recovery-inventory` cites `07-backup-architecture.md:81` for a phrase that is no longer
|
- **The migrated rows are large.** Seventeen verbatim roadmap rows are now in the register, which is
|
||||||
there** (`grep 'missing-only merge'` → only the inventory's own copy). A stale line-number citation;
|
why it fell 51% rather than further. They are open work and keep their detail, by the rule.
|
||||||
not filed, because the doc it points into has since been rewritten wholesale and the claim it
|
- **`OPEN-ITEMS.md` is still 327 KB.** The remaining bulk is open rows with long evidence sections —
|
||||||
supported is now handled by R-354.
|
that is detail doing a job, and the next reduction comes from closing work, not from editing it.
|
||||||
- **The sweep's "cited by no row" count (89) is not a defect count.** Many audits are cited by the
|
- **The one-register gate compares existence, not content.** R-203/R-163 were caught only because the
|
||||||
capability map, by `where-felhom-stands.yaml` or by other reports rather than by a register row. It
|
register had *no* row for them after the split; a stale-but-present row would pass. Named in the
|
||||||
is a *candidate* filter, and it earned its keep only in combination with the shape search.
|
gate's own docstring as residual hole 4.
|
||||||
- **`REPORT-hub-blindness.md` exists at the repo root and is cited by no register row**, but the
|
|
||||||
register does cover its subject (2 hits). Left alone.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 10. CI, confirmed by ID
|
|
||||||
|
|
||||||
`felhom.eu` **id=382 / run_number=250**, head `091a4b74` — **success**. Commit `091a4b7`, pushed to
|
|
||||||
`main`, all 10 gates OK, tree clean. No other repo was touched.
|
|
||||||
|
|||||||
@@ -398,6 +398,26 @@ 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).
|
### N.6 Website version bump (if controller/hub version is shown on the site).
|
||||||
|
|
||||||
|
### N.7 Housekeeping — before the report, not after (2026-08-22 ruling)
|
||||||
|
|
||||||
|
**Nothing was ever pruned and the numbers got bad:** the register reached 672 KB across 286 entries,
|
||||||
|
over half of it finished work, one entry at 16 KB. **A file that cannot be read is a file that cannot
|
||||||
|
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
|
||||||
|
`git show` returns the full original text. **Open rows are not touched — their detail is doing a job.**
|
||||||
|
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
|
||||||
|
shape `[DESIGN]` in the architecture document and point it at the log entry** (§4's map).
|
||||||
|
**Losing a reason is how a deliberate design becomes a bug in someone's eyes** — that cost four
|
||||||
|
mis-filed defect reports in August 2026 (R-370, R-376).
|
||||||
|
3. **State the register's size in the report, before and after.** A number every session is what makes
|
||||||
|
growth visible; prose about tidiness is not a mechanism.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 11. Tests
|
## 11. Tests
|
||||||
|
|||||||
@@ -1,5 +1,20 @@
|
|||||||
# 00 — Felhom Capability Map
|
# 00 — Felhom Capability Map
|
||||||
|
|
||||||
|
> **How to read this document.** Two kinds of statement appear, and where this document marks them it
|
||||||
|
> marks them like this — the same wording as `07-backup-architecture.md:11-17`, carried here on
|
||||||
|
> 2026-08-22 (R-376) so a reader meets one convention and not eight:
|
||||||
|
>
|
||||||
|
> - **[DESIGN]** — a decision taken. Not derived from code; the code may not implement it yet.
|
||||||
|
> - **[FACT]** — an observed property, carrying a `file:line`, a live command output or a citation.
|
||||||
|
>
|
||||||
|
> **Statements in this document are NOT yet all marked.** Marking them wholesale is a large judgement
|
||||||
|
> exercise and a wrong mark is worse than none, so only what a session touches is marked (R-376).
|
||||||
|
> **An unmarked statement therefore means "not yet classified", never "observed".** That ambiguity is
|
||||||
|
> exactly what cost this project three sessions in August 2026: the hot/bulk placement decision sat
|
||||||
|
> unmarked beside a marked `[FACT]`, and was read as an observation and reported as a defect.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
> **What this is:** the single cross-component truth table of what the Felhom platform can do
|
> **What this is:** the single cross-component truth table of what the Felhom platform can do
|
||||||
> **today**, at what confidence level, with verifiable evidence. Rows are *scenarios* (user- or
|
> **today**, at what confidence level, with verifiable evidence. Rows are *scenarios* (user- or
|
||||||
> operator-visible outcomes), not modules — a scenario spans agent + controller + hub + catalog,
|
> operator-visible outcomes), not modules — a scenario spans agent + controller + hub + catalog,
|
||||||
|
|||||||
@@ -1,5 +1,20 @@
|
|||||||
# Felhom Controller Architecture — Part 1: Topology & Trust
|
# Felhom Controller Architecture — Part 1: Topology & Trust
|
||||||
|
|
||||||
|
> **How to read this document.** Two kinds of statement appear, and where this document marks them it
|
||||||
|
> marks them like this — the same wording as `07-backup-architecture.md:11-17`, carried here on
|
||||||
|
> 2026-08-22 (R-376) so a reader meets one convention and not eight:
|
||||||
|
>
|
||||||
|
> - **[DESIGN]** — a decision taken. Not derived from code; the code may not implement it yet.
|
||||||
|
> - **[FACT]** — an observed property, carrying a `file:line`, a live command output or a citation.
|
||||||
|
>
|
||||||
|
> **Statements in this document are NOT yet all marked.** Marking them wholesale is a large judgement
|
||||||
|
> exercise and a wrong mark is worse than none, so only what a session touches is marked (R-376).
|
||||||
|
> **An unmarked statement therefore means "not yet classified", never "observed".** That ambiguity is
|
||||||
|
> exactly what cost this project three sessions in August 2026: the hot/bulk placement decision sat
|
||||||
|
> unmarked beside a marked `[FACT]`, and was read as an observation and reported as a defect.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**Status:** draft (decisions from the topology/trust design sessions).
|
**Status:** draft (decisions from the topology/trust design sessions).
|
||||||
**Platform facts** referenced here live in `docs/proxmox-platform.md`; this document
|
**Platform facts** referenced here live in `docs/proxmox-platform.md`; this document
|
||||||
records *Felhom's decisions*, not Proxmox behaviour.
|
records *Felhom's decisions*, not Proxmox behaviour.
|
||||||
@@ -147,9 +162,23 @@ credentials.
|
|||||||
at attach), role, encrypted credentials, schedule/retention. The agent creates the Proxmox
|
at attach), role, encrypted credentials, schedule/retention. The agent creates the Proxmox
|
||||||
storages, continuously checks presence/reachability, and reports per-target status (a
|
storages, continuously checks presence/reachability, and reports per-target status (a
|
||||||
disconnected target → actionable notification).
|
disconnected target → actionable notification).
|
||||||
- **App data placement is per-volume, not per-app:** `.felhom.yml` classifies each volume
|
- **[DESIGN] App data placement is per-volume, not per-app:** `.felhom.yml` classifies each volume
|
||||||
**hot** (DB/config/cache → fast storage, enforced) vs **bulk** (media/files → may be slow).
|
**hot** (DB/config/cache → fast storage, enforced) vs **bulk** (media/files → may be slow).
|
||||||
A photo app's DB stays on SSD while its blobs go to the USB.
|
A photo app's DB stays on SSD while its blobs go to the USB.
|
||||||
|
|
||||||
|
> **Marked [DESIGN] on 2026-08-22 (R-376), and the pointer is honest about what it can point at.**
|
||||||
|
> **This decision was never recorded as a decision anywhere** — it was established by reading, not
|
||||||
|
> by citation: it exists as this bullet and nowhere else, with no dated entry in the decision log
|
||||||
|
> and no `R-` row. A log entry was written on 2026-08-22 (`CONTEXT.md`, "App data placement is a
|
||||||
|
> DECISION") **to give it a home, not to claim it was decided then**; the choice is older than the
|
||||||
|
> entry and its original date is not on record.
|
||||||
|
>
|
||||||
|
> **What being unmarked cost.** The consequence of this bullet — that 40 of 53 catalogue templates
|
||||||
|
> declare no configurable path because they are all-hot — is stated as **[FACT]** at
|
||||||
|
> `07-backup-architecture.md:296-299`. A reader met a marked observation beside an unmarked choice
|
||||||
|
> and reasonably asked whether it *should* be so. **Between 19 and 22 August that reader called this
|
||||||
|
> decision a defect in four places** (R-370), and one session's work went into correcting the record
|
||||||
|
> rather than into the product.
|
||||||
- **Backup scoping:** hot data (LXC rootfs) rides the guest `vzdump` → tiers + PBS. Bulk data
|
- **Backup scoping:** hot data (LXC rootfs) rides the guest `vzdump` → tiers + PBS. Bulk data
|
||||||
on external mount points is **excluded** from the guest vzdump (per-mount `backup` flag) and
|
on external mount points is **excluded** from the guest vzdump (per-mount `backup` flag) and
|
||||||
gets its own per-volume policy (file-level to a tier, slower cadence — or explicitly *not*
|
gets its own per-volume policy (file-level to a tier, slower cadence — or explicitly *not*
|
||||||
|
|||||||
@@ -1,5 +1,20 @@
|
|||||||
# Felhom Controller Architecture — Part 2: Controller Module Map
|
# Felhom Controller Architecture — Part 2: Controller Module Map
|
||||||
|
|
||||||
|
> **How to read this document.** Two kinds of statement appear, and where this document marks them it
|
||||||
|
> marks them like this — the same wording as `07-backup-architecture.md:11-17`, carried here on
|
||||||
|
> 2026-08-22 (R-376) so a reader meets one convention and not eight:
|
||||||
|
>
|
||||||
|
> - **[DESIGN]** — a decision taken. Not derived from code; the code may not implement it yet.
|
||||||
|
> - **[FACT]** — an observed property, carrying a `file:line`, a live command output or a citation.
|
||||||
|
>
|
||||||
|
> **Statements in this document are NOT yet all marked.** Marking them wholesale is a large judgement
|
||||||
|
> exercise and a wrong mark is worse than none, so only what a session touches is marked (R-376).
|
||||||
|
> **An unmarked statement therefore means "not yet classified", never "observed".** That ambiguity is
|
||||||
|
> exactly what cost this project three sessions in August 2026: the hot/bulk placement decision sat
|
||||||
|
> unmarked beside a marked `[FACT]`, and was read as an observation and reported as a defect.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
> **EXECUTED (slice 8C, 2026-06-10 — controller v0.37.0).** This map's target state is now realized:
|
> **EXECUTED (slice 8C, 2026-06-10 — controller v0.37.0).** This map's target state is now realized:
|
||||||
> the disk-execution subsystem (`storage/*`, restic, cross-drive, drive-restore, `disk_layout`,
|
> the disk-execution subsystem (`storage/*`, restic, cross-drive, drive-restore, `disk_layout`,
|
||||||
> `local_infra`, `infra_backup`, `setup/scanner`, `monitor/watchdog`+`pinger`, the storage UI) is
|
> `local_infra`, `infra_backup`, `setup/scanner`, `monitor/watchdog`+`pinger`, the storage UI) is
|
||||||
|
|||||||
@@ -1,5 +1,20 @@
|
|||||||
# Architecture Part 3 — The Host Agent
|
# Architecture Part 3 — The Host Agent
|
||||||
|
|
||||||
|
> **How to read this document.** Two kinds of statement appear, and where this document marks them it
|
||||||
|
> marks them like this — the same wording as `07-backup-architecture.md:11-17`, carried here on
|
||||||
|
> 2026-08-22 (R-376) so a reader meets one convention and not eight:
|
||||||
|
>
|
||||||
|
> - **[DESIGN]** — a decision taken. Not derived from code; the code may not implement it yet.
|
||||||
|
> - **[FACT]** — an observed property, carrying a `file:line`, a live command output or a citation.
|
||||||
|
>
|
||||||
|
> **Statements in this document are NOT yet all marked.** Marking them wholesale is a large judgement
|
||||||
|
> exercise and a wrong mark is worse than none, so only what a session touches is marked (R-376).
|
||||||
|
> **An unmarked statement therefore means "not yet classified", never "observed".** That ambiguity is
|
||||||
|
> exactly what cost this project three sessions in August 2026: the hot/bulk placement decision sat
|
||||||
|
> unmarked beside a marked `[FACT]`, and was read as an observation and reported as a defect.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
> Status: design draft (decision content). To be grounded by Claude Code against
|
> Status: design draft (decision content). To be grounded by Claude Code against
|
||||||
> `docs/proxmox-platform.md` and `docs/architecture/02-controller-module-map.md`,
|
> `docs/proxmox-platform.md` and `docs/architecture/02-controller-module-map.md`,
|
||||||
> then placed at `docs/architecture/03-host-agent.md`.
|
> then placed at `docs/architecture/03-host-agent.md`.
|
||||||
|
|||||||
@@ -1,5 +1,20 @@
|
|||||||
# Architecture Part 4 — Control-plane authorization (operator signing)
|
# Architecture Part 4 — Control-plane authorization (operator signing)
|
||||||
|
|
||||||
|
> **How to read this document.** Two kinds of statement appear, and where this document marks them it
|
||||||
|
> marks them like this — the same wording as `07-backup-architecture.md:11-17`, carried here on
|
||||||
|
> 2026-08-22 (R-376) so a reader meets one convention and not eight:
|
||||||
|
>
|
||||||
|
> - **[DESIGN]** — a decision taken. Not derived from code; the code may not implement it yet.
|
||||||
|
> - **[FACT]** — an observed property, carrying a `file:line`, a live command output or a citation.
|
||||||
|
>
|
||||||
|
> **Statements in this document are NOT yet all marked.** Marking them wholesale is a large judgement
|
||||||
|
> exercise and a wrong mark is worse than none, so only what a session touches is marked (R-376).
|
||||||
|
> **An unmarked statement therefore means "not yet classified", never "observed".** That ambiguity is
|
||||||
|
> exactly what cost this project three sessions in August 2026: the hot/bulk placement decision sat
|
||||||
|
> unmarked beside a marked `[FACT]`, and was read as an observation and reported as a defect.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
> Status: design draft (decision content), grounded on `docs/tests/phase4-signing-findings.md`.
|
> Status: design draft (decision content), grounded on `docs/tests/phase4-signing-findings.md`.
|
||||||
> To be reviewed by Claude Code against that spike + `03` §4, then placed at
|
> To be reviewed by Claude Code against that spike + `03` §4, then placed at
|
||||||
> `docs/architecture/04-control-plane-authorization.md`.
|
> `docs/architecture/04-control-plane-authorization.md`.
|
||||||
|
|||||||
@@ -1,5 +1,20 @@
|
|||||||
# Architecture Part 5 — The Hub
|
# Architecture Part 5 — The Hub
|
||||||
|
|
||||||
|
> **How to read this document.** Two kinds of statement appear, and where this document marks them it
|
||||||
|
> marks them like this — the same wording as `07-backup-architecture.md:11-17`, carried here on
|
||||||
|
> 2026-08-22 (R-376) so a reader meets one convention and not eight:
|
||||||
|
>
|
||||||
|
> - **[DESIGN]** — a decision taken. Not derived from code; the code may not implement it yet.
|
||||||
|
> - **[FACT]** — an observed property, carrying a `file:line`, a live command output or a citation.
|
||||||
|
>
|
||||||
|
> **Statements in this document are NOT yet all marked.** Marking them wholesale is a large judgement
|
||||||
|
> exercise and a wrong mark is worse than none, so only what a session touches is marked (R-376).
|
||||||
|
> **An unmarked statement therefore means "not yet classified", never "observed".** That ambiguity is
|
||||||
|
> exactly what cost this project three sessions in August 2026: the hot/bulk placement decision sat
|
||||||
|
> unmarked beside a marked `[FACT]`, and was read as an observation and reported as a defect.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
> Status: design draft (decision content). To be validated by Claude Code against the **actual
|
> Status: design draft (decision content). To be validated by Claude Code against the **actual
|
||||||
> felhom-hub source** (`felhom.eu` repo, `hub/`) + Parts 01–04, then placed at
|
> felhom-hub source** (`felhom.eu` repo, `hub/`) + Parts 01–04, then placed at
|
||||||
> `docs/architecture/05-hub-architecture.md`.
|
> `docs/architecture/05-hub-architecture.md`.
|
||||||
|
|||||||
@@ -1,5 +1,20 @@
|
|||||||
# Architecture Part 6 — Offsite Connectivity (the backup transport)
|
# Architecture Part 6 — Offsite Connectivity (the backup transport)
|
||||||
|
|
||||||
|
> **How to read this document.** Two kinds of statement appear, and where this document marks them it
|
||||||
|
> marks them like this — the same wording as `07-backup-architecture.md:11-17`, carried here on
|
||||||
|
> 2026-08-22 (R-376) so a reader meets one convention and not eight:
|
||||||
|
>
|
||||||
|
> - **[DESIGN]** — a decision taken. Not derived from code; the code may not implement it yet.
|
||||||
|
> - **[FACT]** — an observed property, carrying a `file:line`, a live command output or a citation.
|
||||||
|
>
|
||||||
|
> **Statements in this document are NOT yet all marked.** Marking them wholesale is a large judgement
|
||||||
|
> exercise and a wrong mark is worse than none, so only what a session touches is marked (R-376).
|
||||||
|
> **An unmarked statement therefore means "not yet classified", never "observed".** That ambiguity is
|
||||||
|
> exactly what cost this project three sessions in August 2026: the hot/bulk placement decision sat
|
||||||
|
> unmarked beside a marked `[FACT]`, and was read as an observation and reported as a defect.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
> Status: **design-of-record** (2026-07-03). Records the settled offsite-backup-transport
|
> Status: **design-of-record** (2026-07-03). Records the settled offsite-backup-transport
|
||||||
> decisions; grounded against felhom.eu @ `bf099f6` and felhom-agent @ `4ba1b14` (v0.63.0).
|
> decisions; grounded against felhom.eu @ `bf099f6` and felhom-agent @ `4ba1b14` (v0.63.0).
|
||||||
> Evidence base: `documentation/audits/SPIKE-connectivity-wireguard-2026-07-03.md` (all
|
> Evidence base: `documentation/audits/SPIKE-connectivity-wireguard-2026-07-03.md` (all
|
||||||
|
|||||||
@@ -0,0 +1,308 @@
|
|||||||
|
# REPORT — correcting what we mis-called a defect, and finding what we wrote down and never filed (2026-08-22)
|
||||||
|
|
||||||
|
**Documentation and survey only. No code, no version bump, no bake, no deploy, no machine contacted.**
|
||||||
|
The previous report is preserved at `documentation/audits/REPORT-v0.218.0-r354-r355-2026-08-22.md`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. §2's four claims — ALL FOUR HELD. No halt.
|
||||||
|
|
||||||
|
| # | claim | verdict |
|
||||||
|
|---|---|---|
|
||||||
|
| 1 | `07-backup-architecture.md:297` — 53 templates, 52 named volumes, exactly 13 with a `backup:` block, and those 13 are exactly the ones that bind a configurable path | **HOLDS**, at `:296-299`. The heading above it reads *"Coverage per app class — and an unresolved count"*, which the sweep then followed |
|
||||||
|
| 2 | `_recovery-inventory-2026-07-28.md` Tier-3 row — *"…unpacked by no offsite action… no single action does it and no UI routes it. This is my own enumeration; it is not currently filed as a finding."* | **HOLDS**, verbatim, at `:373` |
|
||||||
|
| 3 | `:392-400` — `rootfs`, `mp0 /var/lib/docker`, `mp1 /mnt/sys_drive` as separate volumes | **HOLDS**, at `:392-396` — **but it describes a layout no current box has.** See below |
|
||||||
|
| 4 | `00-capability-map.md:94` — one data volume; a local backup bounded by free space | **HOLDS**, at `:94` |
|
||||||
|
|
||||||
|
**Claims 3 and 4 describe different layouts, six days apart, and 4 is the live one.** The inventory
|
||||||
|
(2026-07-28) records the pre-R-165 split. `felhom-agent/configs/build-golden.sh:29-40` — live source —
|
||||||
|
bakes **ONE** data volume at `/var/lib/felhom`, with `/var/lib/docker` and `/mnt/sys_drive` as two
|
||||||
|
**binds of that same volume**, and states at `:99` that *"There is deliberately no mp1"*.
|
||||||
|
|
||||||
|
**So the prompt's own §1.2 instruction is out of date:** it asked me to say that named volumes and
|
||||||
|
first-tier backups are *"on two different guest volumes on one physical disk"*. They are on **one**
|
||||||
|
guest volume, by two binds. The disk claim is stated precisely in §4 below.
|
||||||
|
|
||||||
|
**A correction to my own §2 working.** In verifying claim 2 I reported that a literal grep matched
|
||||||
|
`:373`. It did not — it returned nothing, and what printed was the second grep in the same cell.
|
||||||
|
`**not** currently filed` does not match `not currently filed`, because of the markdown bold. The
|
||||||
|
claim still holds (I read the line), but the check I quoted was wrong — and that false zero turned out
|
||||||
|
to matter, see §3.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. The sweep — three counts, and the control that earned them
|
||||||
|
|
||||||
|
**Method** (`scripts` not committed; it is a throwaway, and the terms below are the durable part):
|
||||||
|
enumerate every **survey-class** document — inventory / audit / spike / review / diagnosis / recon /
|
||||||
|
campaign / report / validation / rehearsal / walk / finding / spec / triage — under
|
||||||
|
`documentation/{audits,architecture,pilot,backlog}`, excluding runbooks, templates, READMEs,
|
||||||
|
CHANGELOGs and ROADMAP, because those instruct rather than conclude. For each, ask whether any
|
||||||
|
`OPEN-ITEMS.md` row cites it by filename, then search every line for the **shapes** an unfiled gap
|
||||||
|
takes.
|
||||||
|
|
||||||
|
| count | value |
|
||||||
|
|---|---|
|
||||||
|
| **documents examined** | **113** (survey-class; 114 after this session added one) |
|
||||||
|
| **gaps found** (documents saying, in their own words, that something was not filed) | **14** statements |
|
||||||
|
| **of those, already filed** | **2** — and the other 12 break down in §3 |
|
||||||
|
|
||||||
|
**The search terms, so the next person can widen them:**
|
||||||
|
|
||||||
|
```
|
||||||
|
not (currently )?filed never filed no finding not a finding
|
||||||
|
unresolved unfiled disagreement no single action
|
||||||
|
nothing (does|routes|reads|consults) never been should be
|
||||||
|
ought to is not (currently )?(tested|proven|observed|covered|wired)
|
||||||
|
has (never|not) been (seen|observed|walked|proven|done)
|
||||||
|
no (test|row|register) (pins|covers|cites) TODO FIXME ⚠ not covered gap
|
||||||
|
```
|
||||||
|
|
||||||
|
Every word-gap in the first four patterns is `[*_`~ ]*`, i.e. **markdown emphasis is tolerated** —
|
||||||
|
see §3.
|
||||||
|
|
||||||
|
### The positive control — and it convicted the sweep twice before it convicted the corpus
|
||||||
|
|
||||||
|
**Plant → find → remove → fail to find**, on a scratch copy of the whole `documentation/` tree:
|
||||||
|
|
||||||
|
| step | strongest-shape hits |
|
||||||
|
|---|---|
|
||||||
|
| unplanted scratch copy | **14** |
|
||||||
|
| a synthetic gap planted, **bolded and on one line** | **15** — convicted by name and quoted back |
|
||||||
|
| scratch discarded, real corpus re-run | **14** |
|
||||||
|
|
||||||
|
**The control found two defects in my own sweep before it found anything in the corpus, and both were
|
||||||
|
false zeros of exactly the class this session is about:**
|
||||||
|
|
||||||
|
1. **Markdown emphasis broke the strongest pattern.** The single most important line in the corpus is
|
||||||
|
written `it is **not** currently filed as a finding`, and `not (currently )?filed` does not match
|
||||||
|
it. Fixed by tolerating `[*_`~ ]*` between words.
|
||||||
|
2. **The reporter re-searched a truncated copy of the line.** Hits were stored as `line[:200]`; that
|
||||||
|
line is a long table row and the phrase sits past column 200, so the detector caught it and the
|
||||||
|
**report** dropped it. Fixed by matching the full line and truncating only for display. This alone
|
||||||
|
moved the count **12 → 14**.
|
||||||
|
|
||||||
|
A sweep whose first two runs would have under-reported by 2 is exactly why the control is mandatory.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. What the 14 were, judged
|
||||||
|
|
||||||
|
| verdict | n | which |
|
||||||
|
|---|---|---|
|
||||||
|
| **never a gap** — a reasoned "no finding" | 4 | `_design-review.md:9` (nothing deferred), `CAMPAIGN-11:501` (a measurement was honest), and the two **explicit `### Not filed` sections** in `CAMPAIGN-10-two-storage-soak` and `SPIKE-recovery-unit-space` — each item disposed with a reason. **This is good practice, not a failure, and it is what the rest should look like.** |
|
||||||
|
| **already filed** | 2 | `REPORT-DRILL-backup-truth:321` (its Part 5 became R-360 and R-364, filed the same session) and `REPORT-DRILL:569` (the hub's controller-version blindness — the register already covers it, 2 hits; not re-filed) |
|
||||||
|
| **still open → NEWLY FILED** | 5 | R-371, R-372, R-373, R-374, R-375 |
|
||||||
|
| **the headline** | 3 | `_recovery-inventory:373`, `:1043` and `07-backup-architecture.md:337` — all three the same gap, and it turns out it **was** given a number |
|
||||||
|
|
||||||
|
### The headline: it was filed. In the other register.
|
||||||
|
|
||||||
|
The gap the 2026-08-21 drill rediscovered **was already numbered R-107**, with a full write-up:
|
||||||
|
|
||||||
|
> `ROADMAP.md:122` — *"**No offsite action unpacks the named-volume tars Tier-3 captures on every
|
||||||
|
> run.** `ReconstituteFromOffsite` skips the unit outright … `PlaceOffsiteRestore` places it only when
|
||||||
|
> the live unit is ABSENT"* — **M, READY, 2026-07-28**
|
||||||
|
|
||||||
|
and cross-referenced twice in `07-backup-architecture.md` (`:337`, `:902`). **It is absent from
|
||||||
|
`OPEN-ITEMS.md`**, which opens with the words *"the single source of truth for open work"*.
|
||||||
|
|
||||||
|
**The dating makes it a rule violation, not a gap in the rules.** `OPEN-ITEMS.md` was created and the
|
||||||
|
template gained its *"single source of truth"* bullet on **2026-07-27** (`655b69f`). R-107 went into
|
||||||
|
ROADMAP alone on **2026-07-28** (`070b0ce`) — **the day after**.
|
||||||
|
|
||||||
|
**Measured scope: 72 `R-` ids are in ROADMAP and not in OPEN-ITEMS; 29 are not marked shipped, closed
|
||||||
|
or killed.** Most of the 29 are feature *ideas*, which arguably belong only in ROADMAP. A minority are
|
||||||
|
**findings**: R-30, R-31, R-32 (all P2-HIGH), R-35, R-40, R-76, R-79, R-25, R-49, R-10, R-107.
|
||||||
|
|
||||||
|
**The risk is not double-minting** — the two files share ids and OPEN-ITEMS' ceiling (368) is above
|
||||||
|
ROADMAP's (331). **The risk is rediscovery**: a session greps one file, finds nothing, and redoes the
|
||||||
|
work. That is precisely what happened, and it cost an evening and a night. **Filed as R-369, HIGH.**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Part 1 — the record corrected
|
||||||
|
|
||||||
|
### The specification
|
||||||
|
|
||||||
|
`SPEC-app-data-placement-2026-08-21.md` now opens with a marked **⚠ CORRECTED 2026-08-22** block and
|
||||||
|
carries four inline `[CORRECTED 2026-08-22]` marks. **Nothing was deleted; every measurement stands.**
|
||||||
|
|
||||||
|
What it got wrong: it treated the absence of a storage field on 40 templates as a choice being denied.
|
||||||
|
The architecture states the rule and states it as **enforced**:
|
||||||
|
|
||||||
|
> *"**App data placement is per-volume, not per-app:** `.felhom.yml` classifies each volume **hot**
|
||||||
|
> (DB/config/cache → fast storage, **enforced**) vs **bulk** (media/files → may be slow)."*
|
||||||
|
> — `01-topology-and-trust.md:150-152`
|
||||||
|
|
||||||
|
The 40 are all-hot apps; the 13 are the bulk apps. **And the deploy page has been telling the customer
|
||||||
|
exactly this the whole time** — `deploy.html:624-625`: *„A kiválasztott meghajtón az alkalmazás
|
||||||
|
**fájljai** (média, dokumentumok) tárolódnak. Az **adatbázis a gyors belső SSD-n** fut."*
|
||||||
|
|
||||||
|
### The disk claim, stated precisely
|
||||||
|
|
||||||
|
- **REAL:** a **physical-disk failure** loses the app's data and its first-tier copy together. That is
|
||||||
|
what the off-site and whole-machine tiers exist for, and it is **equally true of a drive-resident
|
||||||
|
app**, whose unit sits beside its data on the drive deliberately so a restore needs the drive and
|
||||||
|
nothing else.
|
||||||
|
- **OVERSTATED:** a **full data volume stopping the operating system.** The OS rootfs is a separate
|
||||||
|
volume, and the capture floor refuses per app before exhaustion (`00-capability-map.md:94`).
|
||||||
|
Watched working 2026-08-21 with `/var/lib/felhom` at 99%: the reserve refused one app per run, told
|
||||||
|
the hub, and all 15 containers stayed healthy.
|
||||||
|
- **WITHDRAWN:** the comparison to Tier 2's same-disk refusal (`tier2.go:329`). Tier 2 refuses a
|
||||||
|
**second** copy on the same disk; Tier 1's unit is *meant* to sit beside the data.
|
||||||
|
|
||||||
|
### Rows re-framed
|
||||||
|
|
||||||
|
- **R-352** — measurements (1)–(4) all stand; the conclusions drawn from (1) and (3) are marked
|
||||||
|
re-framed. (2) is now filed on its own as **R-368**; (4) needs no ruling.
|
||||||
|
- **R-356** — **re-checked and it SURVIVES UNCHANGED, strengthened.** Because the 40-class correctly
|
||||||
|
has no `HDD_PATH`, a restore that reads that as *"the app is not installed"* is misreading a correct
|
||||||
|
configuration. One sentence in it that leaned on the old framing is corrected in place.
|
||||||
|
|
||||||
|
### My own errors, named — R-370
|
||||||
|
|
||||||
|
**Four instances, not three.** The prompt said three; the evidenced count is four, all authored
|
||||||
|
2026-08-21: SPEC §1, SPEC §2.3, SPEC §5, and R-352 point (3). Recorded as a **process** failure with
|
||||||
|
its mechanism — the register and live source were read, `documentation/architecture/` was not — and
|
||||||
|
the missing step is now in the template. Also written into `CONTEXT.md`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. What still needs the operator's ruling — **from this specification, almost nothing**
|
||||||
|
|
||||||
|
- **The placement question is ANSWERED and the answer is no.** Points 1, 2 and 3 of the spec's §5 are
|
||||||
|
withdrawn as a live question; hot data belongs where it is.
|
||||||
|
- **Point 4 is the only thing left**, and it is smaller than it looked — see R-368 below.
|
||||||
|
- **Point 5 needs no ruling.** The Drives count is honest; it is a wording question the design system
|
||||||
|
already owns.
|
||||||
|
|
||||||
|
**One new thing does want your ruling, and it is R-369:** whether the two registers become one, or
|
||||||
|
whether a gate enforces that a not-done `ROADMAP` row has an `OPEN-ITEMS` counterpart. Triage the 29
|
||||||
|
first — most are ideas, a minority are findings.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Part 4 — the three answers, and the finding is smaller and different than believed
|
||||||
|
|
||||||
|
**1. Is the default store consulted at deploy time, for the 13 apps that take a path? — YES.**
|
||||||
|
`internal/web/templates/deploy.html:612`:
|
||||||
|
|
||||||
|
```
|
||||||
|
{{else if and .IsDefault (not .NotAllowed)}}selected{{end}}
|
||||||
|
```
|
||||||
|
|
||||||
|
For a new deploy the default drive **is pre-selected**. `DeployStoragePath` embeds
|
||||||
|
`settings.StoragePath` (`web/handlers.go:89-99`), so `.IsDefault` resolves.
|
||||||
|
|
||||||
|
**So `// new apps use this by default` (`settings.go:453`) is IMPRECISE ABOUT THE MECHANISM, NOT
|
||||||
|
FALSE.** The earlier claim — *"the deploy route never reads it"* — is **wrong**, and it is wrong for
|
||||||
|
the same reason as everything else this week: the grep behind it
|
||||||
|
(`grep -nE 'GetDefaultStoragePath|primaryHDDPath|IsDefault' deploy.go manager.go`) **searched Go files
|
||||||
|
and never the templates.**
|
||||||
|
|
||||||
|
**The residual, and it is the whole finding:** the default lives in the **template**, not the server.
|
||||||
|
`POST /api/stacks/<n>/deploy` takes `values` verbatim; omit `HDD_PATH` and `withPathVars`
|
||||||
|
(`stacks/deploy.go:584`) gets `""` and no default applies. **That is why the invariant has no test —
|
||||||
|
there is nothing server-side to test.** Filed **R-368, LOW**.
|
||||||
|
|
||||||
|
**2. What the label promises the customer, quoted:** `storage.html:469` —
|
||||||
|
**„Legyen alapértelmezett új telepítéseknél"** ("Be the default for new installations"), with the
|
||||||
|
badge **„Alapértelmezett"** at `:34`. **The promise is kept**: it is the default for new installs,
|
||||||
|
which is exactly what the pre-selection does. It does not promise that every app's data goes there.
|
||||||
|
|
||||||
|
**3. What the Drives count counts:** `countAppsUsingPath` (`web/handlers.go:2162-2175`) counts
|
||||||
|
deployed apps where `appCfg.Env["HDD_PATH"] == storagePath`. **A named-volume app has no `HDD_PATH`,
|
||||||
|
so it can never be counted — by construction, not by accident.** The number is honest; it means *"apps
|
||||||
|
that place bulk data on this drive"*, not *"apps using storage"*. Worth one sentence of wording, not a
|
||||||
|
ruling.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Part 3 — the template's two new rules
|
||||||
|
|
||||||
|
**It had neither.** `enumerat` → 0 hits, `register row` → 0 hits; `architecture` appeared 9 times but
|
||||||
|
§4 item 4 named only `02-controller-module-map.md`, and the S-1 rule at the end governs *updating* a
|
||||||
|
design doc, not *reading* one first. **No halt.** There is no separate authoring companion.
|
||||||
|
|
||||||
|
**Rule 1, in §4 where files are read** — abridged; the full text carries a file→area table for all
|
||||||
|
eight architecture documents:
|
||||||
|
|
||||||
|
> **THE ARCHITECTURE DOCUMENT FOR THE AREA THIS TASK TOUCHES — NAME IT AND SAY WHAT IT SAYS.** Not
|
||||||
|
> "read the architecture folder": name the file, and state in one line what it rules about this area.
|
||||||
|
> **A prompt that cannot name one says so explicitly, and that absence is itself recorded** — an
|
||||||
|
> undocumented architectural decision is how a deliberate design gets "fixed" by someone who did not
|
||||||
|
> know it was one.
|
||||||
|
>
|
||||||
|
> **Three sources, in this order, before any claim: the architecture folder holds the REASONING, the
|
||||||
|
> register holds the WORK, source holds the TRUTH.**
|
||||||
|
>
|
||||||
|
> **And the test that catches it: _is what I am about to call a defect something we chose?_** If it
|
||||||
|
> was chosen and the choice is wrong, that is **a proposal to change a decision** — it goes to the
|
||||||
|
> operator as a decision, not filed as a bug. **Cost of learning this (R-370):** between 19 and 22
|
||||||
|
> August a documented placement decision was called a defect in four places.
|
||||||
|
|
||||||
|
**Rule 2, on the `OPEN-ITEMS.md` bullet, where a survey session lands:**
|
||||||
|
|
||||||
|
> **AN ENUMERATED GAP BECOMES A ROW, IN THE SAME SESSION. PROSE IS NOT A RECORD.**
|
||||||
|
>
|
||||||
|
> This binds **surveys, inventories, spikes, reviews and diagnoses**, not only implementation
|
||||||
|
> sessions… If a document says a thing is missing, unhandled, unreachable or *"not currently filed"*,
|
||||||
|
> it does not leave the session as prose. It leaves as a row here, with a rank and an owner. Writing
|
||||||
|
> *"not filed"* is not a disposition; it is a note that the work was seen and dropped.
|
||||||
|
>
|
||||||
|
> **A row in `ROADMAP.md` alone does not satisfy this** … invisible to every standing rule that says
|
||||||
|
> *"grep the register before minting"* (**R-369**).
|
||||||
|
>
|
||||||
|
> **The cost, recorded so the rule can be narrowed later rather than becoming permanent by accident:**
|
||||||
|
> R-107 … was enumerated on **2026-07-28** … and never entered here. **It was rediscovered from
|
||||||
|
> scratch 25 days later by an overnight drill**, and shipped as R-354.
|
||||||
|
|
||||||
|
Two rules, one place each, ~40 lines added to a 521-line document.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Rows opened — ceiling R-367 → **R-375**
|
||||||
|
|
||||||
|
| id | rank | what | first written down | age |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| **R-368** | LOW | The storage default **does** apply at deploy time (`deploy.html:612`); the earlier "never reads it" was wrong. Residual: the default lives in the template, not the server, so the API has none and nothing server-side can be tested | 2026-08-21 (as the wrong claim) | 1 d |
|
||||||
|
| **R-369** | **HIGH** | **Two registers.** 72 ids in ROADMAP only, 29 not done, some of them findings. R-107 sat there 25 days and was rediscovered by a drill | 2026-07-28 | **25 d** |
|
||||||
|
| **R-370** | CLOSED | Process: a documented decision called a defect four times; architecture folder never read. Rule now in the template | 2026-08-19 | 3 d |
|
||||||
|
| **R-371** | LOW | The off-site tier is the only tier that announces nothing on success | 2026-08-05 | 17 d |
|
||||||
|
| **R-372** | LOW | A Tier-2 copy that has **never** been produced (source missing) is not surfaced distinctly | **2026-07-15** | **38 d — the oldest** |
|
||||||
|
| **R-373** | LOW | `SysDataGrowGB` is the intended sizing lever, it works, and nothing sets it | 2026-08-02 | 20 d |
|
||||||
|
| **R-374** | LOW | Three C1 refusal cases judged borderline, left unfiled **and never named**, so nobody can re-judge them | 2026-08-08 | 14 d |
|
||||||
|
| **R-375** | LOW | A PBS datastore audit signal noted and explicitly not filed | 2026-08-18 | 4 d |
|
||||||
|
|
||||||
|
**Oldest gap recovered: R-372, written down 2026-07-15, 38 days.**
|
||||||
|
**Most consequential: R-369**, because it explains the other seven.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. What was dropped, and observations
|
||||||
|
|
||||||
|
**Dropped: nothing.** Parts 1, 2, 3 and 4 all completed. Part 4 was droppable-first and was done.
|
||||||
|
|
||||||
|
**Observations — noticed, not acted on:**
|
||||||
|
|
||||||
|
- **The two `### Not filed` sections are the model.** `CAMPAIGN-10-two-storage-soak:383` and
|
||||||
|
`SPIKE-recovery-unit-space:228` list what they decided not to file **and why, item by item**. That
|
||||||
|
is a disposition, not a shrug. If the new rule needs an exemplar, those are it.
|
||||||
|
- **`07-backup-architecture.md:337` already names this gap and points at R-107**, so the architecture
|
||||||
|
doc was right and current while the register was empty. The document was not the weak link.
|
||||||
|
- **The `_recovery-inventory` cites `07-backup-architecture.md:81` for a phrase that is no longer
|
||||||
|
there** (`grep 'missing-only merge'` → only the inventory's own copy). A stale line-number citation;
|
||||||
|
not filed, because the doc it points into has since been rewritten wholesale and the claim it
|
||||||
|
supported is now handled by R-354.
|
||||||
|
- **The sweep's "cited by no row" count (89) is not a defect count.** Many audits are cited by the
|
||||||
|
capability map, by `where-felhom-stands.yaml` or by other reports rather than by a register row. It
|
||||||
|
is a *candidate* filter, and it earned its keep only in combination with the shape search.
|
||||||
|
- **`REPORT-hub-blindness.md` exists at the repo root and is cited by no register row**, but the
|
||||||
|
register does cover its subject (2 hits). Left alone.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. CI, confirmed by ID
|
||||||
|
|
||||||
|
`felhom.eu` **id=382 / run_number=250**, head `091a4b74` — **success**. Commit `091a4b7`, pushed to
|
||||||
|
`main`, all 10 gates OK, tree clean. No other repo was touched.
|
||||||
@@ -0,0 +1,156 @@
|
|||||||
|
# CLOSED-ITEMS — finished work, compressed
|
||||||
|
|
||||||
|
> **What this is.** Every register row that reached a terminal state, compressed to its title, the
|
||||||
|
> version it shipped in, its evidence paths, and any sentence that states a RULE rather than a
|
||||||
|
> narrative. **Nothing was deleted:** each entry names the commit that holds its full original text,
|
||||||
|
> and `git show <commit>:documentation/backlog/OPEN-ITEMS.md` returns it verbatim.
|
||||||
|
>
|
||||||
|
> **Why it exists (operator ruling, 2026-08-22).** `OPEN-ITEMS.md` had grown to 672 KB across 286
|
||||||
|
> entries, over half of it finished work, with one single entry at 16 KB. A file that cannot be read
|
||||||
|
> is a file that cannot be checked — and this project has already 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. The register now holds **open work only**, so its size tracks the work
|
||||||
|
> rather than the project's age.
|
||||||
|
>
|
||||||
|
> **A sibling rather than the bottom of the register**, deliberately: appending to the same file keeps
|
||||||
|
> the byte count and the scroll, which is the thing being fixed.
|
||||||
|
>
|
||||||
|
> **Load-bearing reasoning was NOT compressed away.** Where a closed row states a rule, a fence or a
|
||||||
|
> deliberate refusal, that sentence is carried here verbatim under **Reasoning kept**. Rules that
|
||||||
|
> outlive their work item also live in their proper homes — `workspace-CLAUDE.md` standing rules,
|
||||||
|
> `felhom.eu/CLAUDE.md`, `CONTEXT.md`, and the architecture folder — and this file is not their
|
||||||
|
> primary record.
|
||||||
|
>
|
||||||
|
> **This file is not the register.** Nothing here is open. `OPEN-ITEMS.md` remains the single source
|
||||||
|
> of truth for open work; `scripts/one_register_gate.py` enforces that against `ROADMAP.md`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
| **R-216** | **A correct recovery code was reported to the customer as wrong.** Shipped in 0.120.0, v0.125.0. | **SHIPPED** (controller v0.201.0 + hub v0.97.0/0.97.1) — **but see R-223**: the feature does not work on a NEW box until the manifest vouches agent 0.125.0. Until then such a box is correctly HELD, not lied to | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-218** | **Succeeding at recovery stopped the box asking for what it still needed.** Shipped in v0.203.0. Evidence: `documentation/tests/part4-rewalk-2026-08-06/journal.md`. | **CLOSED 2026-08-06 — controller v0.203.0, proven live.** *(State corrected 2026-08-06: this field read REOPENED while the body below already recorded the fix shipped and proven. The history of the over-claim is kept deliberately — it is why the row is worded as it is.)* **The over-claim, as it stood: the fix covered the DECLARATION half only.** Measured on the R-201 re-walk: the box declared, and **`offsiteheal` re-staged the secret at 11:44:57** saying *"the box re-consumes on its next cycle"* — **the next cycle came and went** (`host-report` 11:55:46, `Received report` 11:55:54, a full cycle **with a positive control that it ran**) **and the credential was still not consumed.** 23 minutes after the re-stage the box's last off-site-apply attempt was still the pre-re-stage one. A census of the customer-reachable actions on `/backups/remote` (`config`, `reset`, `run`, `toggle`) found **none that fetches a staged credential**, and the only lever is `systemctl restart felhom-controller-bootstrap.service` **inside the guest** — which worked in **18 s** (Campaign 11 measured 17), confirming nothing was wrong with the credential, the target or the key: **the only thing missing is anything at all to trigger a retry.** **This is the FIRST of the two dead ends that keep the recovery journey failing** | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-219** | **The listing the screen promises could never render on the shape it exists for.** | **SHIPPED** (controller v0.201.0) — the unlock now places the key, brings the tier up, then lists | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-217** | **An unreadable store reported as "opened, with unattributable content".** | **SHIPPED** (controller v0.201.0) — opened / empty / unreadable are three distinguishable states | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-222** | **Reaching for a RETAINED earlier package read as a wrong code.** | **SHIPPED** (controller v0.201.0 + hub v0.97.0) — the ACK carries `superseded_present`/`superseded_at` and the screen names the situation. **It states what the hub knows and promises nothing** — the read path is still unbuilt (R-199's inventory) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-215** | **`GET /recovery` rendered the recovery story on a box that never had off-site backups.** | **SHIPPED** (controller v0.201.0) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-220** | **After a rebuild the customer's drives cannot be re-enrolled, and the refusal names an impossible action.** | **CLOSED 2026-08-06 — shipped in agent v0.127.0 and PROVEN LIVE on a genuinely rebuilt box.** The fix is **corroborated, not a widened prefix**: a mountpoint outside `/mnt/felhom-drives` is forgiven only when the SAME device is also mounted under the managed path — a pairing only Felhom's own enrolment produces, so a disk another system is using at `/srv/data` or even `/mnt/someone-elses-disk` is still refused (own test + red-proof). Read from `/proc/mounts` deliberately: the lsblk invocation is pinned verbatim in the sudoers file, so switching to plural `MOUNTPOINTS` would have shipped a sudoers change with the binary. Fail-safe: an unreadable mount table corroborates nothing. **Measured on the Part 4 venue after a real guest purge, with both raw mounts still present on the surviving host:** `/disks/candidates` returned both drives in `attach` and `initialize` (before the fix: two empty lists), and both **re-attached through the customer endpoint** (`registered: true`). The customer-facing refusal was corrected in controller v0.203.0. | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-221** | **A rebuilt box cannot run the escrow ceremony at all.** | **CLOSED 2026-08-08 — agent v0.128.0.** `Apply` re-asserts the seed BEFORE the idempotent early return; the return itself is kept and pinned by a zero-Proxmox-calls assertion. **The writer was established at `file:line` rather than assumed** — see the follow-through section below | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-223** | **The Day-0 manifest vouched agent 0.120.0 while the recovery feature needs 0.125.0 — and a reinstall DOWNGRADES a box that was fixed by hand.** Shipped in 0.120.0, 0.125.0, 0.192.0. | **CLOSED 2026-08-05.** Golden **0.201.0** baked in the drill VM (658 165 766 B, sha `e730d7cab343eb35…f007654`, **round-trip verified from Gitea**), then manifest set in one save: `agent=0.125.0 golden=0.201.0 min_agent=0.125.0`. A fresh install now lands on current agent AND current controller | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-224** | **Every non-code failure on the unlock path is reported to the customer as a statement about their code.** **Reasoning kept:** **And R-216's gate cannot catch it**: the box's own ring reads `recovery capability gate: offsite_key_recovery=yes (source=version)` — the gate discriminates the agent's **age**, not its **reachability**, so a dead agent of the right version sails through the guard whose own comment says *"An attemp | **CLOSED 2026-08-06 — controller v0.202.0 + agent v0.126.0.** The discriminator is now a VALUE: `escrow.ErrBundleFetch` → **HTTP 502** at the agent, `agentapi.RecoveryRefusal` carrying the status at the controller, and `ClassifyRecoveryFailure` mapping it to one of five classes **from the value, never the text**. **PROVEN LIVE on the venue**, same wrong code, only the hub's reachability changed: `hub up → 400 "…did not open the sealed bundle"` · `hub REJECTed → 502 "…could not be fetched — the recovery code was NOT used"` · `hub restored → 400`. Red-proof: deleting the agent case reproduces `got 400, want 502` with the wrong-code sentence. **Coupled `MinAgent 0.126.0`** — an older agent answers 400 for both causes, so the reading is withheld and the 400 degrades to NEUTRAL; the gate blocks nothing. **The customer-facing messages were NOT re-driven end-to-end**: `/recovery` correctly redirects since F7 set the old data aside, and restoring that state is the reconfiguration §11 forbids — they are covered by handler tests + red-proofs | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-225** | **The remote store reports `0 pillanatkép · 0 / 50 GB` when the box cannot read it — directly above a card stating the store holds backups.** | **CLOSED 2026-08-06 — controller v0.202.0.** `StatsKnown` is a **named** state (the `OffsiteInventory.Empty` pattern), because zero is what an unread store and an empty one both look like and `omitempty` makes "absent" and "0" the same bytes. The fill bar renders only when the fill is known — a 0 %-wide bar is a picture of emptiness. **PROVEN LIVE both ways**: before a run the venue read „a pillanatképek száma még ismeretlen"; after one, „2 pillanatkép … / 50 GB". A measured zero still says zero | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-226** | **M1 — the only message that tells a customer to check their typing — is unreachable on any box that has re-escrowed.** | **CLOSED 2026-08-06 — controller v0.202.0.** The retained-package message now names **both** possibilities and restores the ten-words prompt, because the two are indistinguishable at the engine and saying so is the honest thing. It still does not promise the earlier package can be opened. Red-proof: removing the clause makes the prompt unreachable again | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-228** | **After „I do not want the old data", the set-aside history becomes invisible — the box records where it is and shows it to nobody.** | **CLOSED 2026-08-06 — controller v0.202.0.** `OrphanedRenamedTo` is surfaced as two facts and stops. **It does not promise the history can be reopened** — it cannot be, by anyone, today (R-199's inventory is unbuilt) — and the set-aside **confirmation copy was corrected** for the same reason: *"a helyreállítási kód nélkül többé nem lesznek megnyithatók"* implied that WITH the code they could be. The field's own comment said "recovery-code-recoverable", the same over-promise in the code. **PROVEN LIVE**: the notice renders on the venue | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-227** | **A controller restart mid-unlock returns a raw English `Bad Gateway`.** | **CLOSED 2026-08-06 — controller v0.202.0, partially and stated as such.** **The layer that answers is traefik**, whose config this repo generates — but traefik v3 serves no static files, so a branded proxy page needs a **new always-up container** for every 502 on the box: **scoped, not built**. Shipped: the unlock posts via `fetch` and answers a gateway failure in Hungarian in-page. **Progressive enhancement — with no JS the plain POST still shows the proxy's error** | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-234** | **An off-site run reports success while silently omitting an app the customer just switched on.** Shipped in v0.205.0. | **CLOSED 2026-08-06** — controller v0.205.0 | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-236** | **After a guest rebuild the hub never re-stages the off-site credential.** | **CLOSED 2026-08-06 — not a defect** | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-237** | **After a successful recovery the customer is shown no backups at all, because the restore surface is keyed on apps that are currently installed and currently marked for future remote backup.** | **CLOSED 2026-08-06** — controller v0.204.0: the list is now built from `OffsiteInventoryList` (the repository's own snapshot tags). Installed-ness became a property OF a row, never a filter; an unreadable store renders as UNKNOWN **and keeps the action offered**; `felhom-offbox` and `_shares` are excluded. 7 new tests incl. a rendered-page test for the rebuilt shape, and a red-proof that keys the list back on installed-and-toggled apps. | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-238** | **„Teljes visszaállítás előkészítése" accepts the click and does nothing.** Shipped in v0.204.0. | **CLOSED 2026-08-06** — controller v0.204.0 | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-239** | **The fixes are written, tested, pushed — and a machine installed tonight gets none of them.** Shipped in 0.127.0, 0.203.0, 0.204.0. Evidence: `tests/finalwalk-r201-2026-08-07/journal.md`, `tests/golden-0.205.0-2026-08-07/`. | **CLOSED 2026-08-07** | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-241** | **The credential self-heal, succeeding, locks the customer out of their own recovery.** Shipped in v0.206.0, v0.98.0. Evidence: `audits/SPIKE-r241-recovery-offer-2026-08-07.md`, `tests/finalwalk-r201-2026-08-07/journal.md`. | **FIXED 2026-08-07 — v0.206.0 / hub v0.98.0** | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-247** | **The box is being told something false, in its own words, and it recommends the destructive act.** Shipped in v0.206.0. | **CLOSED 2026-08-08** — controller v0.209.0. The field is received and the box tells the two conditions apart; see the Campaign-12 follow-through section below. The WRONG FLAG itself is R-246 (operator act, hub-side) and the customer-facing card copy is unchanged — both stated rather than folded in | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-249** | **The retrieval passphrase ships in the customer page's HTML, so any headless read puts it in a transcript.** Shipped in v0.206.0, v0.207.0. **Reasoning kept:** **Severity MEDIUM:** it is a live per-customer secret that fetches the whole config (`GET /api/v1/config/<id>` with `X-Retrieval-Password`), but the exposure is to someone who can already read the operator page — a defence-in-depth failure, not a boundary crossed. | **CLOSED 2026-08-08** | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-252** | **After a rebuild the restore refuses because the data drives are not registered, and nothing on the recovery path says so.** Shipped in v0.207.0. | **CLOSED 2026-08-08** | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-253** | **The restore page promises it will reinstall the app, and the restore then refuses because the app is not installed — in the customer's own language, three lines apart.** Shipped in v0.207.0. | **CLOSED 2026-08-08** | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-212** | **The orphaned-ciphertext deletion HALTED: the stores on the storage box do not match this register's record.** | **CLOSED 2026-08-05 — all three deleted after the operator confirmed the corrected list** | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-94** | **~~A hand-synced version constant drifts, and the gate that would catch it is never run~~** Shipped in 1.22.0, 9.9.9. | **CLOSED — SHIPPED** (hub v0.87.0, 2026-08-02) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-110** | **`main` is the installer's publish channel — there is no staging.** Shipped in 1.22.0, v1.23.0, v4.4.0. **Reasoning kept:** E-2a's `felhom-backup-target-apply` (`:2116`) is installed **0755 to `/usr/local/sbin` and root-fenced in sudoers**, validated only by `bash -n` — a root-executed artifact taken from `main` with no pinned integrity, which is this row's class exactly. Fixing only (i) leaves a tagged installer pulling nine untagged files from `main` at run time — a staging story that is false in the place it matters most, since one of those nine (`felhom-backup-target-apply`) is installed **0755 into `/usr/local/sbin` and root-fenced in sudoers**, validated only b | **CLOSED — SHIPPED** (installer v1.23.0, 2026-08-03) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-111** | **The Day-0 artifact channel is 17 agent releases stale — a box installed today gets agent `0.96.0`, not `0.113.0`.** Shipped in 0.113.0, 0.114.0, 0.161.0. Evidence: `audits/E2D-fresh-vm-2026-07-29.md`. **Reasoning kept:** **The global controller floor was deliberately NOT raised**: the golden now bakes 0.185.1, so a fresh box needs no self-update, and raising it would have been an unnecessary fleet-wide write. | **SHIPPED 2026-07-29 — the channel now serves agent 0.113.0 + golden 0.185.1** | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-115** | **Publishing is a remembered step, and it was forgotten within eight hours of being documented as forgettable.** Shipped in 0.113.0, 0.114.0, 0.119.0. **Reasoning kept:** **Class: → R-29, one layer up** — a control that exists and is never walked; deliberately NOT given its own ID. It calls the existing `publish-agent.sh` rather than reimplementing it, refuses a dirty or unpushed tree, refuses to re-release an existing version (one version name must never mean two binaries), and **deliberately does not vouch** — vouching points machines at a version and stays the operator's ac | **CLOSED — SHIPPED** (`release-agent.sh` + `check-published-versions.py`, 2026-08-03) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-116** | **The drive-absent alarm and its recovery were a MISMATCHED PAIR — absent fired the GENERIC `storage_disconnected`, return the SPECIFIC `backup_target_restored`; `backup_target_absent` never fired at all** Shipped in 0.185.1, v0.115.0, v1.25.0. Evidence: `audits/R116-v0116-2026-07-30.md`, `audits/SPIKE-r117-bind-liveness-2026-07-30.md`. | **SHIPPED + PROVEN-LIVE** (agent v0.116.0, 2026-07-30) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-120** | **The golden baked a controller that predated R-114 + R-112, so a FRESH box showed the customer the WRONG absent-target message** Shipped in 0.113.0, 0.116.0, 0.156.0. Evidence: `audits/R120-golden-rebake-2026-07-30.md`. | **CLOSED — golden rebaked + PROVEN-LIVE, and the class now has an ENFORCED gate** (golden 0.186.0 + hub v0.82.0, 2026-07-30) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-117** | **A drive's guest bind becomes a DEAD MOUNT while every signal reads healthy — and it happens in TWO ways, only one of which the original framing covered.** Shipped in 0.113.0, 0.117.0. Evidence: `audits/R117-v0117-2026-07-30.md`. **Reasoning kept:** **No block I/O proven by strace** (only `/proc/self/mountinfo`, **0** statfs) — the Part 1 `CLAUDE.md` fence applied to its own first consumer. | **SHIPPED + PROVEN-LIVE** (agent **v0.117.0**, 2026-07-30) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-113** | **The drive-absent gate CANNOT FIRE on device loss — E-2b's alarm is wired to an unreachable condition.** Shipped in 0.113.0, 0.114.0, v0.185.0. Evidence: `audits/E2D-fresh-vm-2026-07-29.md`, `audits/SESSION-C-2026-07-29.md`. | **SHIPPED + PROVEN-LIVE** (agent v0.114.0, 2026-07-29) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-112** | **E-2's degraded banner and offer have NO UI CONSUMER — the endpoint is correct and the customer never sees it.** Shipped in v0.185.1. Evidence: `audits/E2D-fresh-vm-2026-07-29.md`, `audits/SESSION-C-2026-07-29.md`. | **SHIPPED + PROVEN-LIVE** (controller v0.186.0, 2026-07-29) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-114** | **On target-drive loss the customer is told the wrong story and offered the drive that just vanished.** Shipped in 0.113.0. Evidence: `audits/E2D-fresh-vm-2026-07-29.md`, `audits/SESSION-C-2026-07-29.md`. | **SHIPPED + PROVEN-LIVE** (controller v0.186.0, 2026-07-29) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-29** | **The green gates are not enforced anywhere — one was RED for 16 releases before anyone ran it.** Shipped in 1.19.0, 1.22.0, v0.129.0. | **CLOSED — both halves shipped** (2026-08-02) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-86** | **Restore-tests are interval-scheduled, not backup-aligned** | **CLOSED — SHIPPED + PROVEN-LIVE 2026-08-03** (agent **v0.121.0**, hub **v0.91.0**) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-87** | **The restic tier is never restore-tested** **Reasoning kept:** **And R-95 still applies:** that credential can delete, so a restic restore-test must never be able to write to the repo CC | **READY — RE-RANKED UP 2026-08-03 (R-86 closed)** | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-185** | **The agent cannot see the host backup tier's archives on demo-felhom — the PVE token has no ACL on `/storage/felhom-backup`, so the content listing returns EMPTY where root sees three archives.** Shipped in v0.123.0. | **CLOSED — SHIPPED + PROVEN-LIVE 2026-08-03** (agent **v0.123.0**, installer **1.24.0**) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-186** | **A released agent binary's sha256 cannot be reproduced from its tag.** Shipped in v0.120.1, v0.121.0, v0.121.2. | **CLOSED — SHIPPED + MEASURED 2026-08-03** (agent **v0.122.0**) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-187** | **R-115's one-command release had never actually run its publish leg — the first real use died there.** Shipped in v0.121.0. | **CLOSED — SHIPPED 2026-08-03** (`felhom-agent`) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-188** | **Every agent release has a ~50 % chance of emailing the operator a CI failure for a release that is correct.** Shipped in 0.121.2, v0.121.0, v0.121.1. | **CLOSED — SHIPPED 2026-08-03** (agent **v0.122.0**) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-189** | **A passing restore-test can be invisible to the hub forever — and R-86 made that window a week instead of a day.** Shipped in v0.121.1. | **CLOSED — SHIPPED + PROVEN-LIVE 2026-08-03** (agent **v0.122.0**) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-195** | **A customer with no machine ever bound e-mailed an `expected_dbdump_missed` ERROR every morning.** Shipped in v0.73.0. **Reasoning kept:** Fail-**open** on a read error (an unreadable binding must never SUPPRESS a real alarm), and the deferral is LOGGED with its own counter (the v0.73.0 Part-7 precedent: a quiet check must not look like a check that did not run). | **SHIPPED** (hub **v0.92.0**, 2026-08-04) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-196** | **`escrow_stale` is wired to the ONE path that does not change the repo password, and absent from the path that does.** Shipped in v0.95.0. Evidence: `audits/DRILL-r201-night-run-2026-08-04.md`, `audits/SPIKE-offsite-credential-recovery-2026-08-04.md`. | **CLOSED 2026-08-05 — hub v0.95.0** | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-197** | **The hub holds both halves of the evidence that a box's offsite DATA key changed, and reads neither.** Shipped in v0.78.0, v0.93.0. Evidence: `audits/SPIKE-offsite-credential-recovery-2026-08-04.md`. **Reasoning kept:** The in-between shapes (a first-ever hash, a hash-less supersession) are LOGGED rather than dropped, so *"we chose not to alarm"* and *"the check did not run"* never look identical. | **SHIPPED** (hub **v0.93.0**, 2026-08-04) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-198** | **The hub's superseded-escrow retention does NOT retain the offsite repository password — and the ceremony the system tells the customer to run is what destroys the last copy.** Shipped in v0.92.0, v0.93.0. Evidence: `audits/RECON-offsite-dr-chain-2026-08-04.md`. | **SHIPPED** (hub **v0.93.0**, 2026-08-04) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-199** | **The hub serves recovery blobs on two endpoints that have no client anywhere in the system.** Evidence: `audits/RECON-offsite-dr-chain-2026-08-04.md`. | **SHIPPED + PROVEN-LIVE 2026-08-04** (hub **v0.94.0**, agent **v0.125.0**) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-203** | **A customer-declared MANDATORY data directory was silently absent from the off-site snapshot while the run reported `ok`.** Evidence: `audits/DRILL-r201-offsite-recovery-2026-08-04.md`. | **SHIPPED + PROVEN-LIVE 2026-08-04** (controller **v0.197.0**) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-204** | **A rebuilt box can recover its off-site key and still cannot use it: the remedy that reconfigures the tier is the thing that blocks the recovery.** Shipped in v0.198.0, v0.199.0, v0.95.0. Evidence: `audits/DRILL-r201-night-run-2026-08-04.md`. **Reasoning kept:** **Live on demo-felhom 9201, nothing restarted (`restarts=0`, container older than both mints): the superseded code returned „Hibás vagy lejárt kód" and the current one was accepted first time.** **Item 2 (a re-issue marks a healthy escrow stale) — CLOSED, → R-196.** Test-proven; deliberately NOT fir **What is deliberately NOT automated: the escrow ceremony.** A credential is replaceable; the recovery code is not. | **ALL FOUR ITEMS CLOSED 2026-08-05** (items 1–3 controller v0.198.0 + hub v0.95.0; item 4 controller v0.199.0 + hub v0.96.0) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-192** | **`offsite_delivery_stuck` tells the operator the opposite of what the detector measured, and the self-heal silently refuses for exactly the reason the message denies.** Shipped in 0.187.0, 0.192.0, v0.199.0. Evidence: `audits/RECON-offsite-dr-chain-2026-08-04.md`, `audits/SPIKE-offsite-credential-recovery-2026-08-04.md`. | **CLOSED 2026-08-05 — the guard's scoping half closed BY REPLACEMENT** (hub v0.96.0) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-193** | **A guest rebuild silently drops the off-site app-data tier, and nothing restages the credential.** Shipped in 0.156.0, 0.187.0, 0.192.0. Evidence: `audits/RECON-offsite-dr-chain-2026-08-04.md`, `audits/SPIKE-offsite-credential-recovery-2026-08-04.md`. | **CLOSED 2026-08-05 — controller v0.200.0** (credential half v0.199.0/v0.96.0; the recovery SCREEN v0.200.0) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-90** | **~~ep0 RAM headroom — 4 GiB swap survived its first reboot 2026-07-27; 3.8 GB RAM unchanged~~** | **CLOSED — the operator rescaled ep0 to a CX33 on 2026-08-03** | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-97** | **~~Whole-guest backup tier had no hub signal; quiesce blamed the apps~~** Shipped in v0.79.0. | **SHIPPED** (controller v0.177.0 + hub v0.78.0/v0.79.0, 2026-07-27) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-100** | **~~A restic offsite tier that fails every night never goes stale on the hub — `isStale` counted from `LastRun`** **Reasoning kept:** The real defect is **defeated defence in depth**: the hub-side *pull* net was anchored on a field the failing controller keeps refreshing, so it could not compensate for a lost *push* (cf. | **SHIPPED + PROVEN-LIVE** (controller v0.181.0 + hub v0.80.0, 2026-07-28) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-101** | **~~Tier-2 `LastRun` is written on failure and rendered to the customer as „Legutóbbi másolat" — including in t** | **SHIPPED + PROVEN-LIVE** (controller v0.182.0, 2026-07-28) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-108** | **~~Network storage can host an app's namespace, and FileBrowser binds a network share at its ROOT~~** Evidence: `audits/R108-network-app-namespace-2026-07-30.md`. | **SHIPPED + PROVEN-LIVE** (controller v0.187.0, 2026-07-30) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-109** | **~~The DR recipe records no backup target~~** Evidence: `audits/R106-R109-recipe-completeness-2026-07-30.md`. | **SHIPPED + PROVEN-LIVE** (agent v0.118.1 + hub v0.83.0, 2026-07-30) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-106** | **~~The DR recipe records the PBS namespace as `"root"` on every box~~** | **SHIPPED + PROVEN-LIVE** (agent v0.118.1, 2026-07-30) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-122** | **~~`AssembleDRRecipe` silently DROPPED `offsite_restic` — the offsite recovery location never reached any reci** | **SHIPPED** (hub v0.83.0, 2026-07-30) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-125** | **A "test through the production path" is only true up to the seam it injects at.** Shipped in v0.118.0. Evidence: `audits/R106-R109-recipe-completeness-2026-07-30.md`. | **FIXED** (agent v0.118.1) — filed for the DOCTRINE point | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-128** | **~~`build-felhom-iso.sh:44` comments that `ISO_VERSION` "aligns with felhom-host-install SCRIPT_VERSION" — a c** | **CLOSED** (iso v1.26.0, 2026-07-31) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-154** | **~~`[first-boot]` is automated-install-only and nothing in the Felhom tree said so~~** Evidence: `audits/SPIKE-universal-iso-3-2026-07-31.md`. | **CLOSED** (iso v1.26.0, 2026-07-31) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-155** | **~~`iso-repack.sh` refuses any ISO without `auto-installer-mode.toml`, blocking the no-`answer.toml` posture~~** | **CLOSED** (iso v1.26.0, 2026-07-31) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-156** | **An app's data is neither persisted nor backed up, and it reports healthy.** Shipped in 26.6.1. **Reasoning kept:** **Provenance, stated because it decides the row:** the observation is `docker ps -a` on demo-hp's **guest 9201** returning empty, supplied with the 2026-08-02 task; **this session did not re-measure** (documentation-only, every box fenced). | **CLOSED — all three apps fixed** (papra template, 2026-08-03) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-157** | **`bootrecon`'s start-ONCE sweep misses the boot orphan it exists to recover — TWO mechanisms.** | **CLOSED — SHIPPED + PROVEN-LIVE** (B: controller v0.189.0; A: v0.190.0, 2026-08-02) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-170** | **The drive-backed boot gate infers a customer's Stop from a container count.** Shipped in v0.190.0. | **CLOSED — SHIPPED + PROVEN-LIVE** (controller v0.190.0, 2026-08-02) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-171** | **The boot sweep started apps whose data drive was ABSENT — a regression introduced by v0.189.0, now FIXED.** Shipped in v0.189.0. Evidence: `audits/DIAG-bootrecon-drive-absent-2026-08-02.md`. | **CLOSED — SHIPPED + PROVEN-LIVE** (controller v0.190.0, 2026-08-02) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-172** | **A false `host_stale` alarm fires when the hub's SQLite refuses two consecutive host reports.** **Reasoning kept:** **Retry options (b) and (c) were deliberately NOT taken** — with readers no longer blocking writers a surviving `SQLITE_BUSY` would be a real signal, and a retry would hide it; revisit only on evidence. | **CLOSED — SHIPPED + PROVEN-LIVE** (hub v0.88.0, 2026-08-02) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-174** | **The app-stop guard's crash recovery started apps onto MISSING drives — a regression in v0.189.0 code.** Shipped in v0.189.0. | **CLOSED — SHIPPED + PROVEN-LIVE** (controller v0.191.0, 2026-08-02) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-175** | **`07-backup-architecture.md` §7.5 states ONE box's size bound as if it were the fleet's.** Shipped in 0.192.0, 7.5.1. Evidence: `audits/SPIKE-r165-mp1-merge-2026-08-02.md`. | **CLOSED — FIXED 2026-08-03** (same pass as R-165) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-183** | **A fresh install fetched the vouched agent BINARY and its sixteen CONFIG files from two different refs, and nothing compared them.** Shipped in v0.120.0. **Reasoning kept:** **Why it is a defect and not only untidiness:** these files are the agent's own operating surface — its systemd unit, its sudoers, its guarded wrappers — and `configs/felhom-backup-target-apply` is installed **0755 into `/usr/local/sbin` and root-fenced in sudoers**, validated only by `bash -n`. | **CLOSED — SHIPPED** (installer v1.23.0, 2026-08-03) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-182** | **A full disk tells the operator about ONE app and silently swallows every other app's refusal for an hour.** Shipped in v0.194.0, v0.90.0, v0.90.1. | **CLOSED — SHIPPED** (controller v0.194.0 + hub v0.90.0/.1, 2026-08-03) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-181** | **The capture floor guards the cheap leg and not the leg that fills the volume — and its refusal message asserts an invariant the code does not provide.** Shipped in v0.192.0, v0.193.0, v0.193.1. | **CLOSED — SHIPPED** (controller v0.193.0 + v0.193.1, 2026-08-03) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-178** | **The merged golden (0.192.0) is built and published but NO BOX HAS BEEN REINSTALLED FROM IT, and it is deliberately UNVOUCHED.** Shipped in 0.119.0, 0.120.0, 0.192.0. | **CLOSED — BOTH BOXES REINSTALLED AND PROVEN (2026-08-03)** | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-158** | **A local Tier-1 app-data backup failure reaches no hub channel.** Shipped in v0.78.0. | **CLOSED BY R-167 — SHIPPED + PROVEN-LIVE** (controller v0.191.0 + hub v0.89.0, 2026-08-02) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-159** | **wishlist's data landed in an ANONYMOUS volume — never backed up, orphaned by a redeploy.** | **SHIPPED** (`templates/wishlist/docker-compose.yml`, 2026-08-02) — filed to record the CLASS | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-160** | **gramps-web persisted three paths and wrote to none of them.** | **SHIPPED** (`templates/gramps-web/docker-compose.yml`, 2026-08-02) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-163** | **`mp1` is RETENTION, not staging — and it is sized as if it were neither.** Shipped in v0.192.0. | **CLOSED by R-165 — the ceiling it describes no longer exists** (golden v3.0.0, 2026-08-03) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-165** | **Merge `mp1` into `mp0` — the dedicated 20 G backup partition stops existing.** Shipped in 0.192.0, v0.192.0. Evidence: `audits/SPIKE-r165-phase0-2026-08-03.md`. | **SHIPPED — golden `build-golden.sh` v3.0.0 + agent v0.120.0 + controller v0.192.0 (B2), 2026-08-03. IMPLEMENTED — the LAYOUT is proven live on both boxes (R-178, 2026-08-03); the BULKHEAD'S REPLACEMENT IS NOT (→ R-181)** | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-166** | **App state gets a desired/observed model with its own store.** Shipped in v0.189.0. | **SHIPPED + PROVEN-LIVE** (controller v0.189.0, 2026-08-02) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-167** | **Storage monitoring and backup alerts.** Shipped in v0.191.1, v0.191.2. | **CLOSED — SHIPPED + PROVEN-LIVE** (controller v0.191.0/.1/.2 + hub v0.89.0, 2026-08-02) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-168** | **~~CI: no runner exists, and with trunk-based pushes CI can DETECT but not BLOCK~~** Shipped in 0.1.0. Evidence: `audits/SPIKE-ci-runner-2026-08-02.md`. | **SHIPPED — and the alarm is DEMONSTRATED** (2026-08-02) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-205** | **`RootFsPressureDespiteHousekeeping` can never fire** Evidence: `audits/SPIKE-dooplex-buildcache-2026-08-05.md`. | **CLOSED — SHIPPED + RED-PROVEN LIVE** (`homelab-manifests` `6808a4b`, 2026-08-05) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-258** | **C3 — the customer's per-app backup tick is green on the PRESENCE of a restore point, and its only red condition is a GLOBAL one.** | **CLOSED 2026-08-08 — controller v0.210.0.** `appDumpVerdict` reads THIS app's own dump result; three states, no icon when nothing is known. **Recency deliberately not added** — see the observation in the follow-through section | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-259** | **C4 — a disk read that FAILS renders as „0.0 GB / 0.0 GB (0%)" in the nominal colour, on the dashboard's most-looked-at meter.** | **CLOSED 2026-08-08 — controller v0.210.0.** `readDiskUsage` reports success; `SystemInfo.DiskKnown`/`HDDKnown`; the template draws no figure, no percentage and no meter fill when unknown. **The hub leg is deliberately NOT fixed and is now R-266** | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-260** | **C5 — the agent reports at least eight decision-bearing facts the hub models NOWHERE, and the sharpest one blinds the check that answers „can the operator get into this box".** | **CLOSED 2026-08-08** — the class is GATED (G-1, `scripts/wire_contract_gate.py`) and the sharpest instance is fixed (hub v0.99.0). The remaining unconsumed facts are **R-264, OPEN** — allowlisted with reasons, which is not the same as decided. See the follow-through section below | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-265** | **A CI run can fail with NO LOG PERSISTED, and the alarm mail then points the operator at a log that does not exist.** | **CLOSED 2026-08-08 — `timeout-minutes: 5` on the gates job, and the alarm mail now states elapsed seconds and qualifies its own "names itself in the run log" sentence.** ⚠ **The unknown is NOT closed and must not be read as closed:** whether the `if: failure()` alarm fires for a REAPED job is still unverified. The timeout makes the reap unreachable in practice; it does not answer what happens inside one | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-267** | **The Configuration page is 2.6× faster and is still ~10 s, and the remaining cost is ONE Gitea call whose latency swings 20× with load.** Shipped in 0.100.2, v0.100.0. | **CLOSED 2026-08-08 — hub v0.101.0 + a registry prune.** **Final: cold 5.4 s, warm 0.14 s** (was 26.2 s). Three serialisation legs took it to 9.85 s mean, the 60 s in-memory memo took the warm path to a quarter-second, and the prune halved what remains of the cold path. **⚠ TWO CORRECTIONS TO THIS ROW'S OWN EARLIER TEXT, because both were wrong and both mattered.** **(1) "Only 50 generic versions exist" WAS NOT A COUNT, IT WAS A PAGE LIMIT.** `?type=generic&limit=1000` returns at most 50; the 50 I measured was exactly the cap, and three older agent versions (0.81.0, 0.80.0, 0.79.0) only became visible after the first 30 deletions moved them onto page one. **An unpaginated listing is not evidence of a total** — this repo's own "an empty listing is not evidence of emptiness" rule, walked into while measuring. **(2) THE OPERATOR'S "REDUCE THE NUMBER OF ARTIFACTS" WAS THE BETTER CALL AND MY MEASUREMENT SAID OTHERWISE.** I reported it helps "sub-linearly" and "is not the lever". Measured after: trimming to 10+10 took the COLD load from 13.4 s to 5.4 s — a 2.5× improvement on the path the memo cannot help, because the fan-out is per-version. Recorded rather than quietly dropped (the R-96 standing rule). **Pruned to the newest 10 per package on the operator's rule**, with the live-vouched golden/agent/floor asserted into the KEEP set before a single DELETE was issued; 33 deletions, all HTTP 204, and golden 0.210.0 / agent 0.128.0 / agent 0.127.0 verified still fetchable afterwards. `drill-r50` runs agent 0.113.0, now deleted — flagged to the operator first; it is a disposable nested drill VM and only its re-download path is gone | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-268** | **A live per-guest local-API token was printed into a session transcript.** Shipped in 169.254.253. | **CLOSED — ROTATED + PROVEN LIVE 2026-08-09** (rehearsal pre-phase, `audits/REHEARSAL-byo-reinstall-2026-08-09.md` §3) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-273** | **RANK 1 — the hub vouched an agent version that was never git-tagged, and every install fleet-wide now fails at step 5/8.** Shipped in 0.127.0, 0.128.0, v0.127.0. | **CLOSED 2026-08-09 — tag pushed, install PROVEN** | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-278** | **demo-felhom's off-site tier has never completed a run and has been stuck for six days.** Shipped in 0.200.0, v0.93.0. | **CLOSED 2026-08-10 — protection RESTORED, and the recovery it waited for could never have worked** | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-280** | **RANK 1 — after a reinstall the data drive cannot be re-attached through ANY dashboard route, and the restore page promises it is "two clicks".** | **CLOSED — controller v0.211.0, delivered via golden 0.211.0 (vouched 2026-08-10)** | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-281** | **The hub said NOTHING through an entire reinstall — and the tripwire for a sealed-backup unseal did not fire on a real unseal.** | **WITHDRAWN 2026-08-09** | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-293** | **CENSUS, 2026-08-10 — no machine that is not ours can be in the state that cost demo-felhom its history, and here is the whole population.** | **CLOSED-INFORMATIONAL 2026-08-10** | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-294** | **The orphan card promises restorability that the box rendering it cannot evaluate — specified, not implemented.** Evidence: `documentation/design/SPEC-orphan-card-copy-2026-08-10.md`. | **CLOSED — controller v0.211.0; see R-299 for the sentence it missed** | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-296** | **The orphan card's OTHER sentence makes the same promise, and the spec says it is fine.** | **CLOSED — shipped in controller v0.212.0 (R-299); verified: the sentence at backups_remote.html:98 was replaced and the stem guard covers it** | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-297** | **An install took whatever golden was lying around.** Shipped in 0.153.0, 0.210.0, 0.213.0. | **CLOSED — observed live + PUBLISHED as `installer-v1.27.0` (both refs bumped)** | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-299** | **The orphan card's OTHER sentence made the same unevaluable promise, and the spec called it accurate.** Shipped in v0.211.0. | **CLOSED — controller v0.212.0** | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-300** | **Our own uninstall left the thing that makes our own reinstall refuse.** Shipped in 0.0.0, 10.0.2, 127.0.0. | **CLOSED — observed live + PUBLISHED as `installer-v1.27.0` (both refs bumped)** | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-301** | **The abandon countdown banner makes the retired promise a third time, and as a flat statement.** **Reasoning kept:** the customer chose to abandon a recovery offer that exists — which is why it was NOT changed (this session was fenced to the orphan card). | **CLOSED — premise CONFIRMED and fixed in controller v0.213.0 (R-302)** | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-302** | **The abandon banner promised retrieval it could not see was still true — fixed by PINNING a fingerprint at the decision.** Shipped in v0.213.0. **Reasoning kept:** Empty is not a match on either side; a countdown started before v0.213.0 carries no pin and takes the cautious branch (deliberately NOT backfilled). | **CLOSED — controller v0.213.0** | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-305** | **The R-300 cleanup fires exactly once per machine, and the second reinstall hits the original wall.** Shipped in 0.0.0, v1.27.0. | **CLOSED — superseded by R-316** | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-307** | **`demo-felhom` carries a LIVE abandon countdown that this drill did not start — and the end state says there should be none.** **Reasoning kept:** The drill's fence forbade starting, shortening or triggering a countdown, and none was; but its required end state was *"no abandon countdown anywhere"*, and one exists. | **CLOSED — countdown cancelled 2026-08-12 on the operator's ruling** | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-308** | **~~The stored controller password no longer opens `demo-felhom`~~ — WITHDRAWN 2026-08-12, this was MY BUG, not a defect.** | **WITHDRAWN — not a defect (my error)** | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-309** | **The day-0 runbook says pushing the installer publishes it. It has not since R-110.** Shipped in 1.25.0, 1.27.0. Evidence: `documentation/runbooks/day0-install.md`. | grep -m1 '^SCRIPT_VERSION'`. **Measured while writing it: served `1.28.0`, `main` `1.28.0`, both pins `installer-v1.28.0` — the three agreeing is the observation; any one alone is not.** **The claim was copied elsewhere and the copy was hunted:** `audits/SPIKE-universal-iso-3-2026-07-31.md:184` said the same thing and **cited `day0-install.md` as its source**, which is how it spread. It was **true on the day it was written** (R-110 shipped 2026-08-03), so the dated finding is kept verbatim and carries a SUPERSEDED note rather than being rewritten — falsifying a dated record to tidy it is its own defect. Two other hits are correct in context: `hostinstall_gates.py:198` states the consequence of the manifest LOSING its tag, and the 2026-08-12 drill record already names the sentence as false | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-311** | **A correct recovery code for a retained package stopped being reported as wrong.** Shipped in 0.126.0, 0.128.0, 0.129.0. | **CLOSED — shipped + delivered: hub v0.103.0 + agent v0.129.0 + controller v0.214.0** | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-316** | **The removal now genuinely reverses the installation — R-305's once-per-machine defect closed.** Shipped in 0.0.0, v1.27.0, v1.28.0. | **CLOSED — shipped + published, observed on the cycle that actually fails** | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-318** | **No honest marker exists that says Felhom installed dnsmasq on a machine already in the field, and none can be invented.** Shipped in v1.27.0. **Reasoning kept:** `/var/log/dpkg.log` does record the install — and is a **timestamp**, which the standing rule refuses as a heuristic dressed as a fact. | **CLOSED — established, no action possible for existing boxes** | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-319** | **The guest-network watchdog finally has a reader — the first of R-264's twenty-one, and it is the repair COUNT that matters, not the state.** Shipped in 0.92.0, v0.92.0. | **CLOSED — shipped hub-side 2026-08-13** | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-320** | **Evidence has been destroyed twice in three days, in the same place, by the same act.** Shipped in v1.28.0. Evidence: `audits/DRILL-retained-key-2026-08-12.md`, `audits/REPORT-r316-installer-v1.28.0-2026-08-13.md`. **Reasoning kept:** **The rule, now standing rule 5 in `workspace-CLAUDE.md` (so it loads in every session) and repeated where a session actually meets it — `runbooks/target-selection.md`, `RUNBOOK-rehearsal-v3.md`, and the `PROMPT-TEMPLATE.md` report section: evidence is copied off the machine at the end of the phase | **CLOSED — rule written, four homes** | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-321** | **A box on which reporting is deliberately switched off still alarms as stale, then down.** Shipped in v0.105.0. | **CLOSED — shipped hub v0.105.0, both doors** | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-322** | **The claim guard has never scanned the hub, and the hub sends the customer's first sentence.** **Reasoning kept:** **Recommended shape, and the reason it is not one line:** the gate is invoked by `controller_gates.py`, so pointing it at a sibling repo makes a controller gate fail on a felhom.eu edit — the cross-repo lesson from G-1 (a gate needing a sibling passes locally and exits INCONCLUSIVE in CI, and must n | **CLOSED 2026-08-13 by R-324** — `scripts/hub_copy_gate.py`, registered in `repo_gates.py`, scanning 95 hub files for retired names and four declared customer surfaces for retrieval stems, with a plant→convict→remove→pass selftest that caught a defect in its own instrument on the first run. The stem list IS shared (`scripts/customer_copy_vocab.py`) and no controller gate was made to depend on a felhom.eu clone; the controller gate's adoption of the shared list is R-325, and until it happens the two are drift-checked rather than left to diverge | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-323** | **The third near-homograph — the five-word phrase is „Tulajdonosi jelmondat” now.** | **CLOSED — shipped hub v0.105.0** | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-324** | **The hub's customer copy is under a guard for the first time — and the guard has been watched catching, ignoring and releasing.** | **CLOSED — shipped, selftest green** | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-326** | **"Which claims are unproven?" is a question a machine can answer now — and the number everyone was repeating answered a different question.** | **CLOSED — shipped** | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-328** | **The disk alert was emailed to nobody, and one word is the whole reason.** Evidence: `audits/DIAG-smart-passed-trap-2026-08-14.md`. | **CLOSED — controller v0.215.0, PROVEN LIVE 2026-08-14.** Now `"warning"`, and `DiskAlertKind.Severity()` is exported so the contract is assertable from any package rather than duplicated as a literal. **The proof is a side-by-side pair pushed through the REAL hub event endpoint** from demo-hp's controller: severity `"warning"` → stored `warning`, `notification_log` **id 689, channel `operator`, status `sent`**; the identical push at `"warn"` → stored **`info`**, and **no `notification_log` row exists at all**. Pinned by `TestNotifyDiskHealthDegraded_SeverityRoutes`, which asserts membership of the hub's accepted set (not just the literal) and names both hub locations; its red-proof — restoring `"warn"` — fails all three assertions | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-334** | **CLOSED 2026-08-18 — golden 0.216.0 baked, published and VOUCHED; CI green by run id.** Shipped in 0.214.0, 0.215.0, 0.216.0. Evidence: `documentation/tests/golden-*`, `documentation/tests/golden-0.214.0-2026-08-12`. | **CLOSED 2026-08-18.** Baked from `RUNBOOK-manual-build.md` §4.0+§4.1 in the DooPlex drill VM and published: **`GOLDEN_VERSION=0.216.0`**, **`GOLDEN_SHA256=ac004dc90d8cefccc5448377892f9cff3a4c3e1e27d0e11129120e38ac31c34b`**, 656,970,239 bytes at `…/generic/felhom-golden/0.216.0/golden.tar.zst`. **The published bytes were verified, not just the script's print** — the artifact was downloaded back out of Gitea and hashed, and it matches. **Vouched by the operator, all THREE fields together**, confirmed by reading the hub's own store rather than the save: `artifact_golden_version=0.216.0`, `artifact_agent_version=0.129.0`, `artifact_min_agent=0.129.0` (2026-08-18 11:00:59–11:01:00), and the hub's recorded sha256 matches the downloaded artifact. The R-216 shape was checked on the machine: `MinAgent` 0.129.0 is **equal to**, not above, the newest **published** agent. **`golden_currency_gate.py` rc=0 and `repo_gates.py --fast` rc=0 — all nine gates — and CI is GREEN BY RUN ID: run **353**, `head_sha 7d81681d6`, conclusion `success`** (the two prior runs 351/352 on this same afternoon were red on exactly this row, which is the contrast). That push needed **no `--no-verify`** — the first of the day that did not. Evidence: `documentation/tests/golden-0.216.0-2026-08-18/`, report `REPORT-golden-0.216.0.md`. **Closed with the run id quoted deliberately**: this row was re-confirmed once and widened once, and closing it on a local green a third time would have left the same ambiguity | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-335** | **One physical disk was walked TWICE per run, and the second walk sustained it against itself.** Shipped in v0.215.0. **Reasoning kept:** **This is the shape standing rule 3 warns about: an absent alarm was not evidence — the two artefacts had to be read AGAINST each other** — — CC | **CLOSED — controller v0.216.0, 2026-08-14.** Each `diskKey` is evaluated once per run; both entries stay marked `seen` so neither looks like a disappeared disk, and the card still renders both storage rows (the dedup is about state and alerts, not display). Pinned by `TestDiskCheck_SameDiskTwiceIsEvaluatedOnce`; companion red-proof run and reverted — deleting the guard makes the first sighting emit `Kind:2` (Hiba-from-sectors) at 8 sectors | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-344** | **`felhom-agent` leaks one TCP connection to PBS per poll cycle, forever, on both sides — and it is the whole of the ep0 descriptor leak.** Shipped in 0.129.0, 0.130.0. Evidence: `audits/SPIKE-ep0-established-connections-2026-08-20.md`. **Reasoning kept:** **The proof obligation is the fd count, not the diff:** per standing rule 3 the positive observable is ep0's ESTAB count going FLAT between proxy restarts, measured over a window long enough to matter — a green test suite proves nothing here, and a 30-minute window proves nothing here either (that e **control 4 cycles -> 4 leaks; fixed 4 cycles -> 0 leaks.** **Positive observable per standing rule 3** (a zero leak is equally consistent with "the agent stopped working"): the fixed box's four poll cycles are in ep0's log, and the boxes' other traffic is near-identical (libwww-perl 924 vs 926, pro | **CLOSED 2026-08-20 — fixed, proven live on both boxes, published and vouched** | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-347** | **The R-344 fix exists on two demo boxes by hand and NOWHERE ELSE — a box installed from the current image still ships the leaking agent.** Shipped in 0.129.0, 0.130.0, 0.216.0. Evidence: `documentation/runbooks/publish-train-rules.md`. | **CLOSED 2026-08-20 — published, vouched, and the fleet reconciled onto the published bytes** | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-351** | **The restore never read back where the backup said the data lived, and a second press started a second restore.** | \.NamespaceRoot\b' --include=*.go` found **no non-test reader anywhere** — the reconstitution opened the manifest (`offbox_reconstitute.go:235`) purely for the coherence stamp and resolved its destination from the LIVE app instead. **A restore into a destination different from the recorded one therefore succeeded silently, under a green message.** **(b) The second press.** All seven restore handlers gated on `backupMgr.IsRunning()` — the CONCURRENCY flag, acquired *inside* the goroutine (`offbox_reconstitute.go:180`) **after** the handler returned. Established with a test before any change: both the reconstitute and place handlers answered „…elindult" and **overwrote the first restore's op/stack**. The wizard had read the correct flag since v0.154.0 and said so in a comment; the handlers were never moved over. **(c)** The banner gated its terminal result on a page-local `sawRunning`, so a restore that finished before the page opened — the 8.666 s OpenGist restore — was shown to nobody. | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-354** | **The off-site full restore has NO named-volume leg — the tar is in the unit, in the snapshot and in the checking folder, and is never replayed.** Shipped in 0.217.0, 0.218.0. | **CLOSED — SHIPPED + PROVEN-LIVE 2026-08-22** (controller **v0.218.0**) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-355** | **`paperless-ngx`'s PostgreSQL is dumped into a directory for a stack that does not exist, so its unit has never contained a database dump — and the destructive restore therefore takes no safety dump and tells the customer the app has no database.** Shipped in 0.217.0, 0.218.0. | **CLOSED — SHIPPED + PROVEN-LIVE 2026-08-22** (controller **v0.218.0**) | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-339** | **The hub was SILENT when it lost sight of the off-site stores — and a 9 h 37 m outage proved it.** **Reasoning kept:** That is **correct for a fill signal** — a missing reading must never be mistaken for 0%, which is why degraded data drives no band transition — but the consequence was that a completely dead off-site endpoint and a healthy one were **indistinguishable on the operator channel**. | **SHIPPED — hub v0.106.0, 2026-08-18.** Reachability is now a second, independent signal: consecutive failed fetch windows counted per checker, `pbsdr_box_unreachable` / `offsite_box_unreachable` (severity `warning`) past a default **3 windows (≈30–45 min)**, with paired `*_recovered` all-clears wired into `recoveredPairedDownTypes` — necessary because both recoveries are severity `info` and `severityNotifies` drops `info`. Threshold tunable via `alerting.box_unreachable_windows`. **The fill logic is untouched**: no threshold, throttle, band or escalate-once behaviour changed. Evidence: `internal/monitor/box_reachability_test.go` (Scenarios A–F) + `internal/notify/dispatcher_box_reachability_test.go` (the cross-package wiring, asserting an actual operator mail), plus three companion red-proofs each seen failing with a message naming the right cause | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-370** | **PROCESS: between 2026-08-19 and 2026-08-22 the reviewing side called a documented architectural decision a defect, in four places, because it read the register and live source and never `documentation/architecture/`.** Evidence: `documentation/architecture/`. **Reasoning kept:** R-352 (re-framed), R-369 The record is corrected in place with the framing marked rather than deleted, per the standing rule that a document which quietly changes its mind teaches nobody. | **CLOSED — corrected 2026-08-22** | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-96** | **Two standing rules were agreed in chat and never committed** Evidence: `documentation/runbooks/workspace-CLAUDE.md:48-70`. **Reasoning kept:** **Two standing rules were agreed in chat and never committed** **MIGRATED FROM `ROADMAP.md` 2026-08-22 (R-369) — originally filed 2026-07-27, size XS, roadmap state `idea — found 2026-07-27`.** Moved verbatim; nothing added or reinterpreted. | **CLOSED — migrated from ROADMAP 2026-08-22** | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
|
| **R-107** | **No offsite action unpacks the named-volume tars Tier-3 captures on every run.** Shipped in v0.218.0. | **CLOSED — migrated from ROADMAP 2026-08-22** | full text: `git show fddfe00ce268:documentation/backlog/OPEN-ITEMS.md` |
|
||||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,116 @@
|
|||||||
|
# ROADMAP-HISTORY — finished and superseded roadmap items, compressed
|
||||||
|
|
||||||
|
> Companion to `ROADMAP.md`, created 2026-08-22 under the same operator ruling that produced
|
||||||
|
> `CLOSED-ITEMS.md`. Each entry keeps its identifier, title, shipped version and final state, and
|
||||||
|
> names the commit holding its full original text. **Nothing was deleted.**
|
||||||
|
>
|
||||||
|
> `ROADMAP.md` now carries only live intentions and the reasoning behind them, which is what its
|
||||||
|
> own first paragraph has always said it is for.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
| **R-115** | **Publishing is a remembered step — forgotten within eight hours of being documented as forgettable** Shipped in 0.113.0, 0.114.0, v0.113.0. | **CLOSED — SHIPPED 2026-08-03** (`release-agent.sh` + `check-published-versions.py`, no version bump). Releasing now builds, tags, publishes and **verifies by an independent download** in one act; a ` | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-111** | **The Day-0 artifact channel is 17 agent releases stale — a box installed today gets agent `0.96.0`, not `0.113.0`** Shipped in 0.110.0, 0.113.0, 0.161.0. | **SHIPPED 2026-07-29** — agent 0.113.0 published (sha `5f3247f7…`, round-trip verified) + golden **0.185.1** baked and published (sha `dba00f3e…`, embeds controller 0.185.1); hub Day-0 manifest moved | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-1** | **Peti convergence** Shipped in v0.57.0. | **rehearsal DONE; Peti half open** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-2** | **~~Resolve ~215 lines of foreign WIP in felhom.eu clone (`hub/internal/notify/`, `store.go`, `hub/internal/cla** Shipped in v0.50.0, v0.54.0, v0.55.0. | **killed** (2026-07-16) | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-4** | **Claim-code deliverability: test-send to gmail.com / freemail.hu; tighten DMARC `p=none` → `p=quarantine`** Shipped in 1.1.1, 8.8.8. | **DONE 2026-07-21 (all three halves)** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-39** | **[P2-HIGH] The PBS DR tier can be `applied` and dead at the same time — and nothing notices.** Shipped in 0.68.1, 0.90.0, 0.90.1. | **CLOSED 2026-07-21 — PROVEN LIVE (hub 0.68.1 + agent 0.91.2)** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-30** | **[P2-HIGH] Liveness presence should come from the wait channel, not the report clock.** | **MOVED 2026-08-22 -> OPEN-ITEMS.md** (was: idea) | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-31** | **[P2-HIGH] Offsite provisioning is synchronous with no status affordance.** Shipped in v0.138.0. | **MOVED 2026-08-22 -> OPEN-ITEMS.md** (was: idea) | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-32** | **[P2-HIGH] RESET must purge the customer base dir; the orphan card must stay honest; unattributed bytes must be visible.** | **MOVED 2026-08-22 -> OPEN-ITEMS.md** (was: idea) | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-33** | **[P2-HIGH] Bootstrap pairing-poll spams the customer-visible console.** Shipped in v1.21.0. | **SHIPPED (scripts v1.21.0, 2026-07-18)** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-5** | **Hub: offsite storage visibility — RESTIC box aggregate (v0.64.0) + PBS DR datastore (v0.65.0), each with fill** Shipped in v0.64.0, v0.65.0, v1.2.0. | **SHIPPED (hub v0.64.0 + v0.65.0 + tenantsync v1.2.0, 2026-07-17)** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-7** | **server** Shipped in 1.0.0, v0.144.0. | **SHIPPED slice 1 (controller v0.144.0 + `felhom-samba:1.0.0`, 2026-07-18)** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-10** | **T-6E-1: DB-dump dir-fsync asymmetry (LOW, confirmed in 6E)** | **MOVED 2026-08-22 -> OPEN-ITEMS.md** (was: idea) | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-11** | **Tester-facing one-pager: what the box does, known limitations, how to report** | **RULED 2026-07-21 (channel); doc is the architect's** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-16** | **Operator hygiene: campaign6 autofs orphan (clears on host reboot) + tied-CreatedAt flash duplicates (audioboo** Shipped in v1.17.0. | open (doc-drift bit CLOSED) | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-22** | **PBS-DR pre-check self-grant (F4).** Shipped in v0.89.0. | **SHIPPED + PROVEN-LIVE agent v0.89.0** (2026-07-17) | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-17** | **Old-box archive (u629193-sub1) retirement — 9/9 byte-identical restores verified** | **CLOSED 2026-07-22 — archive deleted (operator console)** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-198** | **The superseded-escrow retention kept the K-escrow and dropped the identity blob** Shipped in v0.60.0, v0.93.0. | **SHIPPED (hub v0.93.0, 2026-08-04)** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-197** | **The hub stored both halves of "did this box's offsite data key change" and compared them nowhere** Shipped in v0.93.0. | **SHIPPED (hub v0.93.0, 2026-08-04)** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-196** | **Five comments claimed `ReissueCredentials` rotates the restic repo password** Shipped in v0.93.0, v0.95.0. | **CLOSED — behaviour shipped hub v0.95.0 (2026-08-05)** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-199** | **The hub served recovery blobs on endpoints with no client anywhere** Shipped in v0.125.0, v0.94.0. | **SHIPPED + PROVEN-LIVE (hub v0.94.0 + agent v0.125.0, 2026-08-04)** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-200** | **The password-injection seam had a handler and no form** Shipped in v0.195.0. | **plumbing SHIPPED (controller v0.195.0); the form is NOT built** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-203** | **A mandatory customer data directory was silently absent from the off-site snapshot while the run reported `ok`** Shipped in v0.197.0. | **SUPERSEDED 2026-08-22 — the register records SHIPPED + PROVEN-LIVE (controller v0.197.0, 2026-08-04); this row was never updated when it shipped** (was:**OPEN — halted the R-201 drill 2026-08-04**) | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-203** | **The app and its backup looked in different directories, and a run that skipped a mandatory folder still said `ok`** Shipped in v0.197.0. | **SHIPPED + PROVEN-LIVE (controller v0.197.0, 2026-08-04)** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-204** | **The recovered key cannot be used: the Re-issue that reconfigures a rebuilt box's off-site tier marks the escrow stale, which gates every run, and the only way to clear it destroys the key** Shipped in v0.199.0, v0.96.0. | **ALL FOUR ITEMS SHIPPED (controller v0.199.0 + hub v0.96.0, 2026-08-05)** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-193** | **A rebuilt box's off-site history was unreachable, and later reachable only from a command line** Shipped in v0.199.0, v0.200.0, v0.96.0. | **CLOSED (2026-08-05)** — credential half controller v0.199.0 + hub v0.96.0; the customer-facing SCREEN controller v0.200.0 | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-201** | **The wipe-and-recover drill** | **PASSED + PROVEN-LIVE (2026-08-04 night)** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-20** | **~~Verify operator-key pinning is fully in the day-0 install flow~~** | **closed** (2026-07-16) | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-23** | **Immediate-sync Direction-2 follow-ups** Shipped in 0.153.0, 0.154.0, 0.155.0. | **(a) BANKED in full; only (b) cosmetic remains** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-35** | **Config-apply should not end the customer's session.** | **MOVED 2026-08-22 -> OPEN-ITEMS.md** (was: idea) | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-36** | **Post-RESET re-enroll leaves offsite "enabled but unprovisioned" — silently.** Shipped in v0.67.0. | **SHIPPED (hub v0.67.0, 2026-07-18)** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-50** | **[P2-HIGH] Island-bridge control plane — make controller↔agent independent of the LAN.** Shipped in 0.96.0, 169.254.253, 192.168.0. | **SHIPPED 2026-07-25 — fleet-migrated (agent v0.96.0 + host-install v1.19.0)** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-51** | **Dead-primary alerting — a multi-container app whose MAIN container is dead must alert.** Shipped in v0.156.0. | **SHIPPED 2026-07-21 — controller v0.156.0** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-52** | **Boot desired-state reconciliation — a `deployed: true` app should be running after boot.** Shipped in v0.156.0. | **SHIPPED 2026-07-21 — controller v0.156.0** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-54** | **[P2-HIGH] The guest's DHCP client is unsupervised — its death takes the box off the internet 1-2 hours later, invisibly.** Shipped in 0.92.0, 0.92.1, 192.168.0. | **SHIPPED 2026-07-21 — agent v0.92.1** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-55** | **[P2] A customer's deliberate Stop does NOT survive a guest reboot for any drive-backed app** Shipped in v0.157.0. | **SHIPPED 2026-07-21 — controller v0.157.0** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-57** | **An app can be withdrawn from the catalog without orphaning the customers already running it** Shipped in v0.158.0, v0.158.1. | **SHIPPED 2026-07-21 — controller v0.158.0 (+ v0.158.1 fix), LIVE-PROVEN** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-59** | **[P1] A no-DHCP install must HARD-ABORT — instead it bakes the installer's fallback address as a STATIC config and completes, producing a box that can never call home.** Shipped in 192.168.100, v1.24.0. | **SHIPPED v1.24.0 (2026-07-22) — as a FIRST-BOOT refuse-loudly gate, with a RECORDED DEVIATION: the install-time abort is out of scope (the 192.168.100.2 fallback is baked inside the Proxmox auto-inst | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-60** | **[P2] First-boot NIC sweep self-heal: if the hub is unreachable, try DHCP across every carrier-bearing NIC before settling.** Shipped in v1.24.0. | **SHIPPED v1.24.0 (2026-07-22) — spike + nested drill proven (SPIKE-firstboot-nic-sweep-2026-07-22.md): cable move → sweep → heal + hub registration unaided in <1 min; sweep is structurally first-boot | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-61** | **[P1] The baked root password must be knowable by the operator — the recurring console lockout.** Shipped in v1.24.0. | **slice 1 SHIPPED v1.24.0 (2026-07-22): the build emits the plaintext into a 0600 sibling `<iso>.rootpw.txt` (single record of truth — never logged/manifested/committed); drill-verified against the in | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-63** | **The install console learns ő/ű** Shipped in 0.153.0, 0.156.0, 0.161.0. | **SHIPPED (scripts v1.25.0, 2026-07-23)** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-66** | **The box's own address becomes visible — „Hálózat" card, Debug network dump, NetBIOS hint.** Shipped in v0.159.0. | **SHIPPED (controller v0.159.0, 2026-07-22)** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-67** | **The NAS share appears in FileBrowser — browse what you mounted.** Shipped in v0.160.0. | **SHIPPED (controller v0.160.0, 2026-07-22)** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-68** | **Notification train: paired recovery mails + prefs seeding at claim + priority headers (power-outage audit F11+F12+F14-light).** Shipped in 0.160.0, v0.71.0. | **SHIPPED (hub v0.71.0, 2026-07-22)** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-70** | **[P2-HIGH] The offsite last mile is invisible on BOTH surfaces — the hub cannot tell "staged" from "delivered" from "applied".** Shipped in v0.161.0, v0.72.0. | **SHIPPED (hub v0.72.0 + controller v0.161.0, 2026-07-23)** — detector `offsite.DeliveryStateFor` (one impl, all consumers), customer-card state line with age (static "delivered once" copy GONE), `off | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-71** | **[P1] Day-0 race: the managed floor-update kills the offsite apply-bridge between password-consume and persist — the one-shot credential is burned and the box lands in the silent consume-404 dead-end forever.** Shipped in 0.153.0, 0.156.0, v0.162.0. | **SHIPPED — (a)+(c); (b) rejected-by-design.** **(a) SHIPPED (controller v0.162.0, 2026-07-24): the apply-bridge settle-gate.** `offsiteapply.SettleProvider.SettleState()` + `SettleFunc` adapter over | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-53** | **`app_export.html` substituted the CSRF token where the customer domain belongs** Shipped in v0.150.0. | **SHIPPED (controller v0.150.0, 2026-07-20)** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-102** | **Tier-2 writes a full `recovery-unit/` mirror on every run and no code path reads it.** | **MOVED 2026-08-22 -> OPEN-ITEMS.md** (was: READY — 2026-07-28) | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-103** | **The Tier-2 no-coverage refusal names the working action but does not route to it.** Shipped in v0.183.0. | **MOVED 2026-08-22 -> OPEN-ITEMS.md** (was: READY — 2026-07-28) | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-104** | **An interrupted offsite run leaves an exclusive restic lock the existing self-heal cannot reach.** | **MOVED 2026-08-22 -> OPEN-ITEMS.md** (was: READY — 2026-07-28) | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-105** | **Three hub-held DR records are empty on the entire live fleet.** | **MOVED 2026-08-22 -> OPEN-ITEMS.md** (was: READY — 2026-07-28) | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-106** | **~~The DR recipe records the PBS namespace as `"root"` on every box~~** Shipped in v0.118.1. | **SHIPPED** — agent v0.118.1, 2026-07-30 | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-107** | **No offsite action unpacks the named-volume tars Tier-3 captures on every run.** | **MOVED 2026-08-22 -> OPEN-ITEMS.md** (was: READY — 2026-07-28) | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-108** | **~~Network storage can host an app's namespace, and FileBrowser binds a network share at its ROOT — this BLOCK** Shipped in v0.187.0. | **SHIPPED** — controller v0.187.0, 2026-07-30 | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-109** | **~~The DR recipe records no backup target~~** Shipped in v0.118.1, v0.83.0. | **SHIPPED** — agent v0.118.1 + hub v0.83.0, 2026-07-30 | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-27** | **Customer-facing self-bind page (R-21 slice C follow-on).** Shipped in v0.62.0, v0.66.0. | **SHIPPED (slice 1, hub v0.66.0, 2026-07-17)** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-25** | **Device-node TOCTOU hardening (drive init).** Shipped in v0.141.0. | **MOVED 2026-08-22 -> OPEN-ITEMS.md** (was: idea) | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-24** | **Guest RAM resize (live) — SHIPPED (agent v0.90.0 + controller v0.143.0, 2026-07-17).** Shipped in v0.143.0, v0.90.0. | **SHIPPED + PROVEN-LIVE** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-13** | **OOB management arc: dual-use existing WireGuard + hub desired-state channel as mutual-repair** Shipped in 10.77.0, v0.59.0, v0.89.0. | **first slice PROVEN-LIVE (poke channel)** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-28** | **Agent fast-tick-until-first-convergence — SHIPPED (agent v0.90.0, 2026-07-17).** Shipped in v0.90.0. | **SHIPPED** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-73** | **SMART history / trending (hub-side) — SUPERSEDED by the phased disk-health arc; see R-330 (Phase 2) and R-331 (Phase 3)** Shipped in v0.215.0. | **partly SHIPPED** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-75** | **Catalog-derived userdata skeleton + import surfaces** Shipped in v0.172.0. | **SHIPPED (controller v0.172.0 + catalog, 2026-07-26)** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-77** | **Endpoint-drift detection, samba protected-set gate, channel log honesty** Shipped in 169.254.253, v0.173.0, v0.74.0. | **SHIPPED (controller v0.173.0 + hub v0.74.0, 2026-07-26)** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-78** | **`local_api` authority ruling — auto-reconcile vs detect-only** | **MOVED 2026-08-22 -> OPEN-ITEMS.md** (was: idea (deferred OUT of R-77 on purpose)) | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-79** | **`report.Issues` / `report.Warnings` are English on customer-facing surfaces** | **MOVED 2026-08-22 -> OPEN-ITEMS.md** (was: idea) | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-80** | **`expected_backup_missed` false alarm — diagnosed + class-fixed** Shipped in v0.75.0. | **SHIPPED (hub v0.75.0, 2026-07-26)** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-81** | **"No signal" is not "bad signal" — anchor the backup deadline check** Shipped in v0.12.0, v0.73.0, v0.75.0. | **SHIPPED (hub v0.75.0, 2026-07-26)** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-82** | **The backup target split — local daily + offsite weekly** Shipped in 1.20.0, v0.100.0, v0.101.0. | **SHIPPED (agent v0.102.0 + controller v0.175.0 + hub v0.76.0 + host-install 1.20.0, 2026-07-26)** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-83** | **Ratify or retire `07-backup-architecture.md`** | **DISCHARGED (2026-07-26) — brought current, NOT ratified** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-84** | **The agent's cold backup `Store` no longer causes a redundant backup** Shipped in v0.103.0. | **SHIPPED (agent v0.103.0, 2026-07-26)** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-85** | **The DR tier must be restore-tested UNATTENDED, and its failure must be HEARD** Shipped in v0.104.0, v0.77.0. | **Code SHIPPED (agent v0.104.0 + hub v0.77.0, 2026-07-27); rotation NOT YET OBSERVED LIVE** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-86** | **Backup-ALIGNED restore-test scheduling — test a tier ~1 day after ITS OWN backup** Shipped in v0.121.0, v0.91.0. | **CLOSED — SHIPPED + PROVEN-LIVE 2026-08-03 (agent v0.121.0 + hub v0.91.0)** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-88** | **An UNREACHABLE backup target reads as "no backup exists" → the agent fires a doomed backup at it** Shipped in 10.77.0, v0.176.0. | **Part 1 SHIPPED (controller v0.176.0, 2026-07-27); Part 2 OPEN (agent wire change)** — **Part 1** added the failure breaker: consecutive failures tracked per TARGET, backoff `15m→30m→1h→2h→4h` capped | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-89** | **Retention is a COMMERCIAL attribute — it belongs to the hub, not to ep0 or a box** | idea — operator ruling 2026-07-27, first increment SHIPPED same day | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-97** | **The whole-guest backup tier has NO failure signal to the hub — `internal/quiesce` never notifies** Shipped in v0.164.0, v0.177.0, v0.78.0. | **SHIPPED (controller v0.177.0 + hub v0.78.0, 2026-07-27)** — **R-97a:** `quiesce.TierNotifier`, a seam (not an import) wired by an init-only setter, edge-triggered on the R-88 breaker ARMING so a fai | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-94** | **~~A hand-synced version constant drifts, and the gate that would catch it is never run~~** Shipped in 9.9.9, v0.87.0. | **CLOSED — SHIPPED hub v0.87.0, 2026-08-02** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-110** | **`main` is the installer's publish channel — there is no staging** Shipped in 1.22.0, v1.23.0. | **CLOSED — SHIPPED 2026-08-03** (installer v1.23.0). `/scripts/` syncs `installer-v1.23.0`; the website still tracks `main`. Proven by HTTP: a push to `main` left the served bytes byte-identical, movi | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-128** | **`ISO_VERSION` "aligns with SCRIPT_VERSION" was a comment nothing evaluated** Shipped in v1.26.0. | **CLOSED — iso v1.26.0, 2026-07-31** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-154** | **`[first-boot]` is automated-install-only, and nothing in the tree said so** Shipped in v1.26.0. | **CLOSED — iso v1.26.0, 2026-07-31** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-155** | **`iso-repack.sh` refused any ISO without `auto-installer-mode.toml`** Shipped in v1.26.0. | **CLOSED — iso v1.26.0, 2026-07-31** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-156** | **An app's data is neither persisted nor backed up, and it reports healthy** | **CLOSED — all three apps fixed, 2026-08-03.** papra's template now mounts `papra_data:/app/app-data` (the app's own data ROOT, chosen over reconfiguring three env vars so a future upstream path canno | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-157** | **`bootrecon`'s start-ONCE sweep misses the boot orphan it exists to recover** Shipped in v0.189.0, v0.190.0. | **CLOSED — SHIPPED + PROVEN-LIVE (v0.189.0 + v0.190.0, 2026-08-02)** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-170** | **The drive-backed boot gate infers a Stop from a container count** Shipped in v0.190.0. | **CLOSED — SHIPPED + PROVEN-LIVE (v0.190.0, 2026-08-02)** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-171** | **The boot sweep started apps whose data drive was ABSENT** Shipped in v0.189.0, v0.190.0. | **CLOSED — SHIPPED + PROVEN-LIVE (v0.190.0, 2026-08-02)** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-166** | **App state gets a desired/observed model with its own store** Shipped in v0.189.0. | **SHIPPED + PROVEN-LIVE — controller v0.189.0, 2026-08-02** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-158** | **~~A local Tier-1 backup failure reaches no hub channel~~** Shipped in v0.191.0, v0.89.0. | **SHIPPED — controller v0.191.0 + hub v0.89.0, 2026-08-02** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-167** | **~~Storage monitoring and backup alerts (decision D-c)~~** Shipped in v0.191.0, v0.89.0. | **SHIPPED — controller v0.191.0/.1/.2 + hub v0.89.0, 2026-08-02** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-165** | **~~Merge `mp1` into `mp0` — the dedicated backup partition stops existing (decision D-a)~~** Shipped in v0.120.0, v0.192.0, v0.193.0. | **CLOSED — PROVEN-LIVE 2026-08-03.** Variant V-c shipped (golden v3.0.0 + agent v0.120.0 + controller v0.192.0); both demo boxes reinstalled and proven end to end (R-178). **Its stated replacement for | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-159** | **wishlist's data landed in an ANONYMOUS volume — never backed up, orphaned by a redeploy** | **SHIPPED** (`templates/wishlist/`, 2026-08-02) — filed for the CLASS | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-160** | **gramps-web persisted three paths and wrote to none of them** | **SHIPPED** (`templates/gramps-web/`, 2026-08-02) | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-161** | **The volume-persistence gate is enforced by convention, not automatically** | **RULED + SHIPPED at reduced scope** (operator, 2026-08-02; `app-catalog` `fd7747d`) | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-163** | **`mp1` is RETENTION, not staging — and it is sized as if it were neither** Shipped in v3.0.0. | **SUPERSEDED 2026-08-22 — the register records CLOSED by R-165 (golden v3.0.0, 2026-08-03): the ceiling this row describes no longer exists** (was:**WAITING-ON-OPERATOR** — the ratio is a tier-sizing | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-90** | **ep0 has 3.8 GB and NO swap — it OOMs under a restore-test, and that gates R-86** | **CLOSED 2026-08-03 — the operator rescaled ep0 to a CX33.** MEASURED on the box, not read from an invoice: `Mem: 7757` MB (**8 GB**, was 3.8), `nproc` **4**, and the 4 GiB swapfile added 2026-07-27 * | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-96** | **Two standing rules were agreed in chat and never committed** | **MOVED 2026-08-22 -> OPEN-ITEMS.md** (was: idea — found 2026-07-27) | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-76** | **FileBrowser-created folders break the setgid chain, and a drop-zone's mode is not stable** Shipped in 1.3.3. | **MOVED 2026-08-22 -> OPEN-ITEMS.md** (was: idea (surfaced by the R-75 spike, 2026-07-26)) | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-21** | **Bare-metal Felhom ISO** Shipped in 0.92.1, v0.62.0, v1.18.0. | **SHIPPED + PHYSICALLY CLOSED (slices A+B+C; rehearsal executed 2026-07-18)** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-168** | **~~CI: no runner exists, and with trunk-based pushes CI can DETECT but not BLOCK~~** | **CLOSED — SHIPPED 2026-08-02** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-37** | **Post-RESET health card shows stale pre-RESET warnings.** Shipped in v0.67.0. | **SHIPPED (hub v0.67.0, 2026-07-18)** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-38** | **Installer GRUB slice.** Shipped in v1.21.0, v1.22.0. | **SHIPPED (scripts v1.22.0, 2026-07-19)** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-40** | **[P2-HIGH] The update path cannot express a MULTI-HOP major upgrade.** | **MOVED 2026-08-22 -> OPEN-ITEMS.md** (was: idea) | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-42** | **RULED: sidecar majors follow the APP, never the newest tag.** | **RULED 2026-07-21 — option (a)** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-43** | **[P2-HIGH] No offsite restore path can restore a database — offsite restore cannot reconstitute a DB-indexed app.** Shipped in v0.148.0. | **SHIPPED controller v0.148.0 (2026-07-19) — live acceptance PENDING** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-44** | **[P2-HIGH] A manual offsite push ships an unrefreshed DB dump — "backed up now" is false for the DB half.** Shipped in v0.148.0. | **SHIPPED controller v0.148.0 (2026-07-19)** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-47** | **[P2-HIGH] The DB replay races the application's own schema repair.** Shipped in 0.153.0, 0.90.0, v0.153.0. | **SHIPPED — controller v0.153.0, 2026-07-20** | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
|
| **R-49** | **[P2] The offsite capture set is ~90% cache and duplication — 1.1 GB of a 1.2 GB immich "photo backup".** | **MOVED 2026-08-22 -> OPEN-ITEMS.md** (was: idea) | full text: `git show fddfe00ce268:documentation/backlog/ROADMAP.md` |
|
||||||
File diff suppressed because one or more lines are too long
@@ -1,3 +1,25 @@
|
|||||||
|
## one_register_gate.py v1.0.0 + repo_gates registration — one register, enforced (2026-08-22)
|
||||||
|
|
||||||
|
**Operator ruling: one register.** `OPEN-ITEMS.md` calls itself the single source of truth for open
|
||||||
|
work; `ROADMAP.md` also held it. R-107 — a READY finding filed 2026-07-28 — sat in the roadmap alone
|
||||||
|
and was invisible to every rule that says *"grep the register"*, until an overnight drill rediscovered
|
||||||
|
it from scratch **25 days later**.
|
||||||
|
|
||||||
|
The gate fails when a roadmap row is **neither an idea nor done** and has no counterpart row in the
|
||||||
|
register. **The predicate is the roadmap's own state column**, so it reads data that already exists.
|
||||||
|
|
||||||
|
**Two things it taught while being built, both worth keeping:**
|
||||||
|
|
||||||
|
1. **Match the LEADING verdict, not the whole field.** A first cut matched the state words anywhere in
|
||||||
|
the row — and a finding's body routinely contains "shipped". The same bug appeared independently in
|
||||||
|
this session's compressor, where `PARTLY CLOSED` and `OPEN — NOT FIXED` both read as closed and
|
||||||
|
moved six still-open rows out of the register (R-378).
|
||||||
|
2. **`BANKED` and `PROVEN-LIVE` are this project's own done-words**, found by running the gate rather
|
||||||
|
than by reading the vocabulary.
|
||||||
|
|
||||||
|
**Residual holes are in the docstring, not implied:** a finding filed as `idea` escapes, a finding with
|
||||||
|
no `R-` id escapes entirely, and the gate checks that a counterpart exists — never that the two agree.
|
||||||
|
|
||||||
## render_stands.py — the page stopped disagreeing with its own source (2026-08-22)
|
## render_stands.py — the page stopped disagreeing with its own source (2026-08-22)
|
||||||
|
|
||||||
**The header's commit shas were hardcoded in the renderer, not read from the YAML.** `verified_on`
|
**The header's commit shas were hardcoded in the renderer, not read from the YAML.** `verified_on`
|
||||||
|
|||||||
@@ -0,0 +1,104 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""one_register_gate.py — ONE register. Operator ruling, 2026-08-22 (R-369).
|
||||||
|
|
||||||
|
WHAT IT CONVICTS ON (a FAIL, exit 1): a row in `ROADMAP.md` that is
|
||||||
|
* neither an IDEA (a proposal for something that does not exist yet), nor
|
||||||
|
* DONE (shipped / closed / killed / ruled / moved),
|
||||||
|
and that has **no counterpart row in `OPEN-ITEMS.md`**.
|
||||||
|
|
||||||
|
WHY IT EXISTS, and the cost that bought it. Two files held open work and only one of them called
|
||||||
|
itself the source of truth. R-107 — "no offsite action unpacks the named-volume tars Tier-3
|
||||||
|
captures" — was written up properly on 2026-07-28, marked READY, and put here. Every standing rule
|
||||||
|
says "grep the register before minting", and every one of them greps the other file. So it was
|
||||||
|
invisible, and on 2026-08-21 an overnight drill rediscovered it from scratch by planting files and
|
||||||
|
watching them not come back. **25 days.**
|
||||||
|
|
||||||
|
THE PREDICATE IS THE ROADMAP'S OWN STATE COLUMN, deliberately — it already distinguishes `idea` from
|
||||||
|
`READY`, so this gate reads data that exists rather than asking anyone to maintain a new marker.
|
||||||
|
|
||||||
|
WHAT THIS GATE CANNOT SEE — the residual holes, named rather than implied:
|
||||||
|
|
||||||
|
1. **A finding filed with the state `idea` escapes.** The state column is a human judgement, and a
|
||||||
|
defect written under `idea` looks exactly like a proposal to this gate. It is the same shape as
|
||||||
|
the problem it fixes, one level up.
|
||||||
|
2. **A finding written in prose with no `R-` identifier escapes entirely.** This gate matches ids.
|
||||||
|
A survey that enumerates a gap and never numbers it is invisible here — that is the previous
|
||||||
|
session's sweep territory and the PROMPT-TEMPLATE rule "an enumerated gap becomes a row".
|
||||||
|
3. **A finding that never reaches the roadmap at all escapes.** Nothing here reads audits, spikes
|
||||||
|
or inventories.
|
||||||
|
4. It checks that a counterpart EXISTS, never that the two say the same thing. A stale register row
|
||||||
|
beside a live roadmap row passes.
|
||||||
|
|
||||||
|
Run: python3 scripts/one_register_gate.py [--fast]
|
||||||
|
"""
|
||||||
|
import io
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
ROADMAP = os.path.join(ROOT, "documentation", "backlog", "ROADMAP.md")
|
||||||
|
REGISTER = os.path.join(ROOT, "documentation", "backlog", "OPEN-ITEMS.md")
|
||||||
|
|
||||||
|
ROW = re.compile(r"^\|\s*\*{0,2}R-(\d+)\*{0,2}\s*\|")
|
||||||
|
# DONE and IDEA are matched against the row's STATE cell only, never the whole row: the body of a
|
||||||
|
# finding routinely contains the word "shipped" while describing something else.
|
||||||
|
# BANKED and PROVEN-LIVE are this project's own done-words and were found by running the gate: a row
|
||||||
|
# reading "BANKED in full" or "first slice PROVEN-LIVE" is shipped work, not an open finding.
|
||||||
|
DONE = re.compile(r"\b(SHIPPED|CLOSED|KILLED|DONE|SUPERSEDED|OBSOLETE|WITHDRAWN|MERGED|DISCHARGED|"
|
||||||
|
r"RULED|MOVED|BANKED|PROVEN-LIVE)\b", re.I)
|
||||||
|
IDEA = re.compile(r"\b(IDEA|SPIKED|PARKED|DEFERRED|BACKLOG|PROPOSAL)\b", re.I)
|
||||||
|
|
||||||
|
|
||||||
|
def rows(path):
|
||||||
|
"""Yield (id, state_cell, whole_line) for every R- row in a pipe table."""
|
||||||
|
for line in io.open(path, encoding="utf-8"):
|
||||||
|
line = line.rstrip("\n")
|
||||||
|
m = ROW.match(line)
|
||||||
|
if not m:
|
||||||
|
continue
|
||||||
|
cells = line.split("|")
|
||||||
|
state = cells[4].strip() if len(cells) > 4 else ""
|
||||||
|
yield m.group(1), state, line
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if not os.path.exists(ROADMAP) or not os.path.exists(REGISTER):
|
||||||
|
print("one-register gate: a backlog file is missing — FAILURE, never a skip")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
have = set(rid for rid, _, _ in rows(REGISTER))
|
||||||
|
offenders, ideas, done, ok = [], 0, 0, 0
|
||||||
|
for rid, state, line in rows(ROADMAP):
|
||||||
|
if DONE.search(state):
|
||||||
|
done += 1
|
||||||
|
continue
|
||||||
|
if IDEA.search(state):
|
||||||
|
ideas += 1
|
||||||
|
continue
|
||||||
|
if rid in have:
|
||||||
|
ok += 1
|
||||||
|
continue
|
||||||
|
title = re.sub(r"\s+", " ", line.split("|")[2] if len(line.split("|")) > 2 else line)
|
||||||
|
offenders.append((rid, state, title.strip()[:120]))
|
||||||
|
|
||||||
|
print("one-register gate — ROADMAP rows: %d done, %d ideas, %d open-with-a-register-row, "
|
||||||
|
"%d WITHOUT" % (done, ideas, ok, len(offenders)))
|
||||||
|
|
||||||
|
if offenders:
|
||||||
|
print()
|
||||||
|
print("CONVICTED — open work in ROADMAP.md with no row in OPEN-ITEMS.md:")
|
||||||
|
for rid, state, title in offenders:
|
||||||
|
print(" R-%-5s state=%-24s %s" % (rid, state[:24], title))
|
||||||
|
print()
|
||||||
|
print("OPEN-ITEMS.md is the single source of truth for open work (its own first line).")
|
||||||
|
print("Either move the row there keeping its id and filing date, or — if it is a proposal for")
|
||||||
|
print("something that does not exist yet — mark its state `idea`, which is what it is.")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
print("one-register gate OK — every open ROADMAP row has a register counterpart.")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
sys.exit(main())
|
||||||
@@ -89,6 +89,10 @@ GATES = [
|
|||||||
("hub-copy", os.path.join(SCRIPTS, "hub_copy_gate.py"), [], True),
|
("hub-copy", os.path.join(SCRIPTS, "hub_copy_gate.py"), [], True),
|
||||||
# R-341 — dated checks in the register were prose that nothing read. Fast: stdlib file read.
|
# R-341 — dated checks in the register were prose that nothing read. Fast: stdlib file read.
|
||||||
("due-checks", os.path.join(SCRIPTS, "due_checks_gate.py"), [], True),
|
("due-checks", os.path.join(SCRIPTS, "due_checks_gate.py"), [], True),
|
||||||
|
# R-369 — two files held open work and only one called itself the source of truth, so a READY
|
||||||
|
# finding sat in ROADMAP.md for 25 days invisible to every "grep the register" rule and was
|
||||||
|
# rediscovered by an overnight drill. Fast: two file reads.
|
||||||
|
("one-register", os.path.join(SCRIPTS, "one_register_gate.py"), [], True),
|
||||||
]
|
]
|
||||||
|
|
||||||
VERDICT = {0: "OK", 1: "FAILED", 2: "INCONCLUSIVE"}
|
VERDICT = {0: "OK", 1: "FAILED", 2: "INCONCLUSIVE"}
|
||||||
|
|||||||
Reference in New Issue
Block a user