burn-down: R-423 site page walk (exemption dropped); STATUS Part C list (45 rows for the operator); REPORT; register 336 -> 291 (0 opened, 45 closed)
gates / gates (push) Failing after 13m1s

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
2026-10-05 17:15:02 +02:00
parent b26d292a64
commit 8012ce4640
10 changed files with 221 additions and 8 deletions
+10
View File
@@ -16,6 +16,16 @@
> and holds nothing of its own; this file does hold its own content, namely the standing rulings below.
> **2026-10-05 (night) — the burn-down (no release; DooPlex/ep0 untouched).** Register 336 → 291 (0 opened, 45
> closed): 24 fixed by later work + 2 duplicates (each re-checked; `audits/burndown-2026-10-05/partA-table.md` holds all
> 317 P3/P4 verdicts), 19 small fixes with tests/red-proofs (catalog `29ac711`, agent `d833163`, controller `114ff27`,
> felhom.eu `ab2b304`…). New gate `script-tests` (every `scripts/**/test_*.py` per push, R-885); `closed_register_gate`
> RULE 4; `site_gates` page walk (R-423, exemption dropped); hub build no longer pushes `:latest`. **The size rule**
> (`OPEN-ITEMS.md` „How a row is filed", the four `unprompted-work.md` copies, `PROMPT-TEMPLATE.md` §9.2/N.7.3): a
> small finding is fixed in-session, not filed; reports state four numbers. Controller/agent/hub carry an
> `## unreleased` CHANGELOG head for comment/test-only changes — the next release folds it in. Part C: 45 rows await
> the operator's „close as accepted" (STATUS). Report: `REPORT-burndown-2026-10-05.md`.
> **2026-10-05 (evening) — the hub database off DooPlex (hub v0.136.0; operator rulings `09` 125–127).** R-173 option A
> IN FORCE: hub `internal/dbsnap` writes `VACUUM INTO /data/snapshots/hub-<UTC>.db` at 02:00 Budapest (keep 2, `ErrBusy`
> on overlap, start-up catch-up when >24 h; `05` §16.3); DooPlex `scripts/hub-db-backup/` (installed by `install.sh` to
+107
View File
@@ -0,0 +1,107 @@
# REPORT — the burn-down: the open-items list gets shorter — 2026-10-05 (night)
| Part | Result |
|---|---|
| **A** — stale sweep, every P4 then every P3 row, oldest first | **done** — 317 rows checked against `main`; 24 closed as fixed by later work, 2 closed as duplicates (facts merged); table `documentation/audits/burndown-2026-10-05/partA-table.md` |
| **B** — small fixes, batched | **done** — 19 rows fixed and closed in four repos, each with a test (and a red-proof where a check changed); **no release** (see below) |
| **C** — the „not worth doing" list | **done** — 45 rows in `STATUS.md`, one line each with my pick; none closed; 2 more (leaked tokens) listed as actions for the operator |
| **D** — stop the growth | **done** — the size rule and the four-numbers rule, in the register, the rules file (4 copies) and the report template |
| Rows before | Rows after | Opened | Closed |
|---|---|---|---|
| **336** | **291** | **0** | **45** |
Counted by `register_shape_gate.py`'s method (`| **R-n** |` lines in `OPEN-ITEMS.md`). Target was ≥ 40 fewer: 45.
## Baselines (re-verified at the start)
felhom.eu `53d8131b20` (hub v0.136.0) · controller `7690c27f86` (v0.296.0) · agent `e06ed97fa8` (v0.146.1) · catalog
`917a779cca`. Register 336: P2 19, P3 139, P4 178.
## Why no release
The brief allowed one release per repo, but also said „DooPlex: no change". The hub runs on DooPlex, so a hub release
could not be deployed; delivering a controller or agent release needs a floor raise or signed jobs through the hub. So
Part B fixed only what needs no release: documents, comments, tests, gates, catalog tooling. Controller, agent and hub
each carry an `## unreleased` head in `CHANGELOG.md` for these; the next release folds it into its own entry. Code
rows that need a release stay open (they are in the Part A table as STILL-TRUE-SMALL with their fix described).
## Part A — the sweep
Eight read-only checker agents took 40 rows each (P4 then P3, oldest id first). No machine was reached. Groups over all
317: FIXED-BY-LATER-WORK 29, DUPLICATE 2, STILL-TRUE-SMALL 91, STILL-TRUE-NOT-SMALL 129, NOT-WORTH-IT 43, UNCHECKED 23.
**Every FIXED and DUPLICATE verdict was re-checked before closing**: 22 cited proof lines re-grepped (all present; one
first missed by my own shell quoting). Of the 29 „fixed": **24 closed**; **R-274 kept** (only one of its two halves was
checked); **R-700, R-704, R-706, R-723 moved to Part C** — their code fix and tests exist, but each row waits for a live
observation, and closing them would silently drop that. R-766 was additionally checked in the live hub image (read
only): the new app logos are in `/usr/share/felhom/assets-seed/`.
Closed as fixed by later work: R-184, R-207, R-287, R-289, R-373, R-390, R-427, R-437, R-464, R-501, R-602, R-617, R-705,
R-766, R-50b, R-121, R-200, R-450, R-489, R-573, R-622, R-635, R-235, R-282. Duplicates: R-755 → R-762, R-446 → R-440
(the unique fact of each moved into the survivor). Each closed row's evidence is in `CLOSED-ITEMS.md`.
## Part B — the fixes, by repo
| Repo (commit) | Row | Fix | Test / red-proof |
|---|---|---|---|
| felhom.eu `ab2b304` + `58ce696` | R-885 | gate `script-tests` runs every `scripts/**/test_*.py` per push (13 → 15 suites, ~20 s); a Python-sqlite3 stand-in when CI has no `sqlite3` | 5 decoys; 2 red-proofs convict |
| felhom.eu `f5a0aeb` | R-376 | marker legend in `08`, `09`, `11` (all 11 numbered docs now) | — (docs) |
| felhom.eu `f5a0aeb` | R-817 | dated clarification under `09` decision 56 — same image, seen before and after a swap (`controllerswap.go:236-240`, `:289`) | — (docs) |
| felhom.eu `f5a0aeb`, controller `e563733` | R-818 | correction notes under hub v0.109.0, controller v0.224.0/v0.225.0 — those findings never had rows | — (docs) |
| catalog `29ac711` | R-799 | MeTube `POST /add` sends `download_type` | test + red-proof |
| catalog `29ac711` | R-761 | logo name `.svg` then `.png` in template comment, REUSE, checklist | — (comment) |
| catalog `29ac711` | R-391 | CLAUDE.md: no observations section by convention | — (docs) |
| agent `d833163` | R-291 | retention record names its source (R-267 prune, R-287); dead reader dropped | reader still reads 10 |
| agent `d833163` | R-348 | restart comment says what a restart blanks | — (comment) |
| controller `114ff27` | R-263 | „only writer that GRANTS"; AST scan of internal/ + cmd/ | 2 red-proofs convict |
| controller `114ff27` | R-368 | `IsDefault` comment names the form as the one that applies it | — (comment) |
| felhom.eu `b26d292` | R-418 | gate list in the docstring = `GATES` | test + red-proof |
| felhom.eu `b26d292` | R-345 | no `:latest` in `hub/Makefile` **and in the real release script `build-hub.sh`** (found during the fix; nothing pulls it) | test; 2 red-proofs |
| felhom.eu `b26d292` | R-416 | `closed_register_gate` RULE 4 — duplicate id in CLOSED-ITEMS | decoy + red-proof |
| felhom.eu `b26d292` | R-261 | `CountSelfBindTokens` documented as a test accessor | — (comment) |
| felhom.eu `b26d292` | R-262 | hostRestoreTest is a deliberate subset; the test found a **third** unmodelled agent field, `skipped` (by design) | test; 2 red-proofs |
| felhom.eu `b26d292` | R-286 | standing rule 3: a control from a DIFFERENT channel (both CLAUDE.md copies) | instructions gate |
| felhom.eu `b26d292` | R-588 | one home for ISO release records; 1.28.0 pointer | — (docs) |
| felhom.eu (final commit) | R-423 | `site_gates` fails on a page `PAGES` does not list; exemption dropped | decoy + red-proof |
Red-proof records: `documentation/audits/burndown-2026-10-05/` (`r885-`, `r263-`, `r262-`, `r423-red-proof.txt`). The
red-proofs of R-418, R-345, R-416 and R-799 were run in the session and each convicted, but their output was **not
saved to a file** — re-run them by mutating as described in each CHANGELOG entry. Suites: hub `go test ./...` green; controller
`go build/vet/test ./...` green; agent build + vet green; all four repos' gates green at each push.
**Fixed without a row** (the new rule, used once): the `:latest` push in `build-hub.sh` — folded into R-345's fix.
**One slip, said plainly:** R-885's gate commit (`ab2b304`) went out WITHOUT closing the row — my closing file had a
JSON error. It was closed one commit later (`58ce696`). „Close in the same commit" was broken once.
## Part C — in `STATUS.md`
45 rows, one line each: what it is, what fixing costs, what happens if never, my pick (close for all 45). Two more —
R-831 and R-870, leaked tokens — are listed as actions for the operator (pick: keep until rotated).
## Part D — the rule, where it lives, quoted
`documentation/backlog/OPEN-ITEMS.md`, „How a row is filed":
> **Fix small, do not file (the size rule — operator brief 2026-10-05, the burn-down).** The register grew because every
> session closed a few rows and filed a few small new ones. So: **a finding that is cosmetic or small — fixable in the
> session in about 30 minutes, in a repo the session may change — is FIXED in that session, with a test where it changes
> behaviour, and NOT filed.** It is recorded in that repo's `CHANGELOG.md` and in the session report under „fixed without
> a row". **Only a finding that needs a decision, a design, a larger build, or a change the session may not make** (a
> protected machine, a repo out of scope, a release budget already spent) **becomes a row.** This narrows — it does not
> repeal — „an enumerated gap becomes a row": a small gap leaves the session fixed, which is a record too.
>
> **Every session report states four numbers:** rows before, rows after, rows opened, rows closed — counted the way
> `register_shape_gate.py` counts (`| **R-n** |` lines in this file).
Carried, so no instruction contradicts it: `.claude/rules/unprompted-work.md` §1 and §3.7 (all four identical copies:
workspace root, felhom.eu, controller, catalog — commits `b018ca9`, `df85a07`, `7a19491`) and
`documentation/PROMPT-TEMPLATE.md` §9.2 (the exception) and N.7.3 (four numbers). The morning-note line in the rules file
already asked for opened/closed/before/after. No gate was weakened or bypassed.
## Teardown
Provisioned nothing. No machine changed: the only reads were the hub pod's asset directory (R-766) and none on any box.
Checker agents were read-only. Scratch: the batch files and results in the session scratchpad; the results are copied to
the audit folder.
+80 -3
View File
@@ -3,9 +3,86 @@
**Ready for the first real tester (Tester-2): yes. Tester 2 (a laptop, off at night) was offline again; nothing was
sent to it.**
**Updated 2026-10-05 (evening, the hub-database session): every box of ours healthy. The hub database now leaves
DooPlex every night, locked, to ep0, is test-restored every Sunday, and an alarm mails you if either stops. Report:
`REPORT-hub-db-offsite-2026-10-05.md`.**
**Updated 2026-10-05 (night, the burn-down session): every box of ours healthy (nothing was changed on any box). The
open-items list went from 336 to 291. Report: `REPORT-burndown-2026-10-05.md`.**
## Tonight, later (2026-10-05, night): the list got shorter
**Decisions:** none of mine.
**What happened:**
- **Every lower-priority row was checked against today's code** (317 rows). 24 described problems a later change had
already fixed; 2 were duplicates. Those are closed, each with the change that fixed it.
- **19 small rows were fixed and closed** in four repositories — wrong comments and documents, missing tests, gates that
checked less than they claimed. No release was needed: nothing that runs on a box or on the hub changed.
- **One real fix found on the way:** the hub's build script pushed a `latest` image tag on every release. Nothing used
it; it is gone, and a test keeps it gone.
- **A new rule, so the list stops growing:** a small problem found during work (about 30 minutes) is fixed in that
session and never added to the list. Every report now states four numbers: rows before, after, opened, closed.
**The numbers:** 336 before → **291 after**; 0 opened; 45 closed.
**Needs you:**
1. **The list below: 45 rows I would close as „accepted".** Answer per row, or „all as picked". If you do nothing,
they stay open and the list stays at 291.
2. **Two leaked tokens to rotate** (R-831, R-870, at the end of the list). If you do nothing, whoever has those
transcripts keeps that access.
3. Earlier tonight's items are unchanged (pin the other „latest" apps on DooPlex; Alertmanager's file permissions).
## Your list: rows I would close as 'accepted' (burn-down 2026-10-05) - answer per row, or 'all as picked'
Each line: what it is. What fixing costs. What happens if we never fix it. **My pick.** I closed none of these.
- **R-93** - A choice between two test fixtures that no longer exist. Fix: a new fixture (a design job). If never: nothing breaks. **Pick: close.**
- **R-124** - The disaster-recovery recipe writes the backup-server namespace as the word 'root'; the server wants an empty name, so one pasted command fails. Fix: a change across hub and agent. If never: an operator doing a manual restore drops one option once; no data risk. **Pick: close.**
- **R-161** - The check that app data lands where backups see it runs by hand, not on every push. Fix: CI starting ~53 apps per push. If never: a bad template can ship until the next periodic check. **Pick: close.**
- **R-162** - If Docker ran on an unusual storage driver, one catalog check would blame the wrong thing. Fix: ~1 h for a driver nobody runs. If never: nothing; the check still fails safely. **Pick: close.**
- **R-169** - CI reports after a push lands instead of blocking it (no pull requests here). Fix: a pull-request workflow for every change. If never: a forbidden --no-verify push could land broken code until the CI mail. **Pick: close.**
- **R-194** - A removed storage permission can still look present for minutes (Proxmox cache). Fix: a new probe in the agent. If never: the self-repair notices up to ~16 min late, then repairs. **Pick: close.**
- **R-210** - 193 old controller/hub images sit only on DooPlex (~27 GB). Fix: a careful delete on DooPlex. If never: 27 GB stays used; ~199 GB is free. **Pick: close.**
- **R-284** - An 'almost full' warning reported on an empty disk was a misreading; the page never shows it there. Fix: nothing to fix. If never: nothing. **Pick: close.**
- **R-346** - A warning about a mistake nobody has made (the audit found 0 cases). Fix: nothing left. If never: nothing today. **Pick: close.**
- **R-367** - One old 312 KB database dump sits on the demo-hp test box under an old folder name. Fix: a hand delete on a demo box. If never: a small file stays on a test box. **Pick: close.**
- **R-371** - The weekly off-site backup sends no 'done' message (failures and staleness already alarm). Fix: a new event in two repos. If never: success stays silent, as now. **Pick: close.**
- **R-372** - An idea to show 'second copy never made' apart from 'second copy failed' to the operator. Fix: a design and a new state end to end. If never: the existing loud warning stays. **Pick: close.**
- **R-374** - A July audit says three borderline cases were left out but never named them. Fix: hours to guess which three. If never: nobody can re-judge them; later sweeps exist. **Pick: close.**
- **R-393** - A proposed tool to log every small decision an unattended run makes. Fix: a small project. If never: the bigger decisions are already recorded under the rules file. **Pick: close.**
- **R-420** - The felhom.eu gate runner cannot mark a gate as advisory only. Fix: add it when one is needed. If never: nothing today. **Pick: close.**
- **R-424** - The roadmap gate cannot tell a real defect filed as an idea from an idea. Fix: no mechanical fix exists. If never: a person must read the roadmap. **Pick: close.**
- **R-445** - The hub's memory suggestion for an app can use samples from a short test install. Fix: a hub change (~1-2 h). If never: a misleading suggestion for up to 7 days after a test install. **Pick: close.**
- **R-460** - BookStack's uploaded files cannot be checked automatically after an upgrade. Fix: an upstream change or a browser step. If never: upgrades stay half-checked automatically. **Pick: close.**
- **R-503** - Letting the installer pick the disk when there is only one (you ruled no). Fix: reversing your rulings. If never: a person keeps choosing the disk. **Pick: close.**
- **R-527** - A catalog 'locked after install' flag does nothing visible (all settings are read-only anyway). Fix: delete it everywhere, or build an edit page. If never: nothing a household meets. **Pick: close.**
- **R-532** - Vaultwarden shows a sign-up form although sign-up is off; the server refuses it. Fix: an upstream fix. If never: a stranger sees a form that fails. **Pick: close.**
- **R-551** - The escrow 'waiting for the agent' screens are tested but never seen on a real box. Fix: a token setup on the scratch box. If never: small chance the live page differs; the state lasts ~17 min. **Pick: close.**
- **R-610** - A power cut inside a sub-second startup phase was never measured (same code as the measured phase). Fix: a new fault injector and a drill. If never: nothing new would be learned. **Pick: close.**
- **R-654** - Old opengist /login bookmarks answer 404 after an upstream move. Fix: one help-text line. If never: a household re-bookmarks once. **Pick: close.**
- **R-687** - Three live proofs a scratch box cannot give, and one log line 20 minutes off. Fix: special test venues. If never: the tests stay the proof. **Pick: close.**
- **R-688** - Deleting a customer does not remove their Cloudflare tunnel and DNS; the dialog says so. Fix: a new Cloudflare step in the delete. If never: you remove them by hand, guided by the dialog. **Pick: close.**
- **R-768** - Grimoire is not offered (upstream rules out public use); the row only watches upstream. Fix: a re-read each catalog campaign. If never: nothing; Karakeep covers bookmarks. **Pick: close.**
- **R-793** - Four apps contain paid-edition code that is off; the row says never turn it on. Fix: nothing (the rule lives in the licence audit). If never: nothing unless someone enables it. **Pick: close.**
- **R-796** - MeTube's browser 'send' helpers cannot pass the family gate. Fix: a new token design. If never: households paste links in the page. **Pick: close.**
- **R-797** - Catalog CI cannot run one gate rule; it says 'not checked' and the push hook checks it. Fix: a CI checkout of a second repo. If never: only a forbidden bypass could skip it. **Pick: close.**
- **R-804** - plant-it's image no longer exists; the template is already marked not installable. Fix: hide the template (small). If never: nothing. **Pick: close.**
- **R-190** - A storage permission vanished once in August; the agent now restores it and mails you. Fix: hours of live experiments. If never: one mail per recurrence; it self-repairs. **Pick: close.**
- **R-412** - A rare race can push one hollow off-site copy of one app; the next night repairs it. Fix: a change at the backup/push boundary plus a drill. If never: rarely, one app's off-site copy is hollow for a day; the second-drive copy stays good. **Pick: close.**
- **R-458** - A pinned (frozen) app can get a newer health check, which can only cause a false alarm. Fix: a fiddly change on the sync path. If never: maybe one false alarm one day. **Pick: close.**
- **R-584** - Helper scripts with the shared demo password were left in a demo box's /tmp. Fix: a wrapper tool. If never: demo-password litter on throwaway boxes. **Pick: close.**
- **R-586** - The ISO bootstrap harness runs at each ISO release, not on every push. Fix: Docker-capable CI. If never: a break is caught at the next ISO release. **Pick: close.**
- **R-698** - A backup records an app's image name, not the image; restoring a version deleted upstream fails. Fix: a mirror or much more backup space. If never: that restore fails; the household uses another copy or version. **Pick: close.**
- **R-738** - The update's health check sees only the front page, so data broken behind it passes. Fix: a per-app data check in every template. If never: catalog tests keep catching it before release. **Pick: close.**
- **R-778** - A box that rolls back below controller 0.286 trusts a forged address in the login counter. Fix: a patch release or a rollback rule. If never: a short window until the next update. **Pick: close.**
- **R-783** - Three wrong SparkyFitness logins block sign-in for everyone for ~10 s. Fix: an upstream setting. If never: a persistent stranger can annoy the household. **Pick: close.**
- **R-853** - After a boot, the box's versions reach the hub up to ~15 min late. Fix: a new agent mode and release. If never: one report late; nothing lost. **Pick: close.**
- **R-700** - A drive move keeps the app's records: fixed in controller v0.276.0 with tests; never seen live on a two-drive box. Fix: a live two-drive move. If never: the tests stay the proof. **Pick: close.**
- **R-704** - A fresh install drops an old update hold: fixed in v0.278.0 with tests; not seen live. Fix: a live install with a leftover hold. If never: the tests stay the proof. **Pick: close.**
- **R-706** - Removing an app with its backups also deletes its off-site test copy: fixed in v0.279.0 with tests; not seen live. Fix: a live remove with an off-site copy. If never: the tests stay the proof. **Pick: close.**
- **R-723** - No 'box recovered' alarm in a new box's first hour: fixed in hub v0.126.0 with tests; not seen at a real first install. Fix: watch the next real install. If never: the tests stay the proof. **Pick: close.**
**Not 'won't do' - these need YOU (leaked tokens; my pick is KEEP until you rotate):**
- **R-831** - the Hetzner storage API token was printed into one session transcript. Rotate it (~10 min). If never: whoever gets that transcript can manage Storage Box sub-accounts.
- **R-870** - Tester 1's two Cloudflare tokens were printed into one transcript. Rotate them (~15 min), or close when Tester 1 is retired. If never: someone could change that test zone's DNS.
## Tonight (2026-10-05, evening): the hub database off DooPlex; the cut-backup check on the scratch box
@@ -0,0 +1 @@
FAIL: site/unlisted-page: decoy PASSED - LIVE HOLE (rc=0)
+1
View File
@@ -76,6 +76,7 @@ The full text of every row below: `git show ab2b3049:documentation/backlog/OPEN-
| **R-262** | **C7 — a comment claims a cross-repo contract is mirrored „field-for-field" and „the key-set tests guard drift"; it is two fields short, AND THE FIXTURE THE TEST READS OMITS THE SAME TWO FIELDS.** (P3) | CLOSED 2026-10-05 — FIXED (burn-down Part B) | felhom.eu this commit: the hub comment says hostRestoreTest is a deliberate SUBSET; `hub/internal/api/r262_restoretest_subset_test.go` pins the hub fields and the known-unmodelled agent fields and cross-checks the agent source beside it — which found a THIRD unmodelled field, `skipped` (agent v0.133.0, R-672; by design a skipped test reads as failed with its reason). Red-proofs: drop `skipped` from the list / stop decoding source_tier → FAIL. |
| **R-286** | **A control drawn from the same channel as the measurement cannot detect a defect in that channel — and this one passed while the measurement was wrong.** (P4) | CLOSED 2026-10-05 — FIXED (burn-down Part B) | felhom.eu this commit: workspace standing rule 3 (both `CLAUDE.md` copies, identical) adds: a control must come from a DIFFERENT channel than the measurement; a hub-state check copies `hub.db-wal` or asks the running pod. |
| **R-588** | **[P3-LOW] ISO release records live in two different places, so "was the gate run for this image?" cannot be answered by looking.** (P4) | CLOSED 2026-10-05 — FIXED (burn-down Part B) | felhom.eu this commit: `runbooks/iso-release-gate.md` names `documentation/tests/iso-release-<ver>-<date>/` as the one home; `tests/iso-release-1.28.0-2026-09-16/README.md` points at the 1.28.0 record inside the 2026-09-16 audit. |
| **R-423** | **`site_gates.py` checks a hardcoded `PAGES` list of seven files; a new page is not scanned at all.** (P4) | CLOSED 2026-10-05 — FIXED (burn-down Part B) | felhom.eu this commit: `site_gates.py` walks website/ and fails on any *.html not in PAGES (all 9 listed today); decoy `site/unlisted-page` in `test_gate_decoys.py`; the `site` exemption is gone from `decoy_coverage_gate.py` (33 covered, 18 exempt). Red-proof: the walk switched off → LIVE HOLE (`audits/burndown-2026-10-05/r423-red-proof.txt`). |
---
+1 -2
View File
@@ -391,7 +391,7 @@ stopping line that lies.
| **R-793** | Business & legal | P4 | **[P3-LOW] Enterprise / BUSL code ships inside four open images — Cal.com and Docmost (EE folders, off without a key), Outline (BUSL-1.1: no commercial "Document Service"), meilisearch v1.36 in Wanderer (EE modules).** READ 2026-10-02 (`audits/licences-2026-10-02/TABLE.md`). Each is fine as the catalog runs them: no EE key, the household's own Outline is not a Document Service, Wanderer uses plain search. **Watch:** never turn on an EE feature, never switch Karakeep's/Wanderer's meilisearch to the `-enterprise` image, and re-read on each major. | **WATCHING — rank P3-LOW; owner: CC** **Re-ranked 2026-10-03: P3→P4: a watch item; nothing is wrong as the catalog runs them.** | — | — | CC |
| **R-794** | Business & legal | P4 | **[P3-LOW] redis 7.4 (RSALv2 / SSPL, not OSI) runs as a private cache in seven apps: dawarich, docmost, immich, nextcloud, outline, paperless-ngx, romm.** READ 2026-10-02 (`audits/licences-2026-10-02/TABLE.md`). Read as permitted (a private cache only its app uses is not Redis offered as a service — inferred). Valkey (BSD-3) or redis 8 (AGPL option) removes the question. **Needs:** a ladder step per app to valkey or redis 8, through the harness — no hurry. | **READY — rank P3-LOW; owner: CC** **Re-ranked 2026-10-03: P3→P4: the row itself says no hurry; usage read as permitted.** | — | — | CC |
## Process & tooling — 65 rows (P3 4, P4 61)
## Process & tooling — 64 rows (P3 4, P4 60)
| ID | Category | Sev | What | State | Blocked on | Next action | Owner |
|---|---|---|---|---|---|---|---|
@@ -424,7 +424,6 @@ stopping line that lies.
| **R-420** | Process & tooling | P4 | **`controller_gates.py` could not express a NON-BLOCKING gate before 2026-09-01** — every registered gate's non-zero exit failed the run, so the only way to add a check was to give it the power to refuse a push. That is the wrong trade for a notice that must fire at the moment a release is committed, when the golden legitimately cannot exist yet. **The capability was added rather than the notice compromised** (a fifth `blocking` field, False for exactly one gate; the felhom.eu runner already had the shape from its `--scope` work). Recorded because the ABSENCE was invisible: nobody had wanted a non-blocking gate before, so nothing said it was impossible. `felhom.eu/scripts/repo_gates.py` still has no `blocking` field — it has `exemptible`, which is a different idea (scope-dependent, not permanent). If a permanently-advisory gate is ever wanted there, it needs the same addition. | **OPEN — noted, not needed yet** | — | — | CC |
| **R-421** | Process & tooling | P4 | **THE CLASS: an instrument that matches a LABEL rather than the fact it names — five instances, every one found by accident.** R-410 (a `mkdir` turned the release gate green), R-400 (seven debug controls answering nothing), R-378 (a status word inside a sentence), R-419 (a phrase inside prose, including prose saying the marker was ABSENT), R-94 (a test comparing a constant to itself). **The gates are the machinery that enforces everything else in this project, and they were the one part nothing had ever checked.** The 2026-09-01 decoy sweep read all 29 scripts and fooled **16**. Ten were fixed the same day; four remain with rows (R-422..R-425); six could not be given a plausible decoy and are named. **The shapes, so the next one is cheap to recognise:** (1) name-for-fact — it matches a path or directory NAME while the fact lives inside the file; (2) substring-for-field — it matches a token anywhere in a body instead of in the field that carries it; (3) declaration-for-reachability — it checks a thing is declared, not that it RESOLVES; (4) constant-for-measurement — it compares a value against itself. **The single largest cause was mundane:** eight gates set their SCOPE with `os.listdir` (one level), so every one was green and correct today and would have gone blind the moment anyone added a subdirectory. `decoy_coverage_gate.py` now refuses a new gate that ships without a decoy. | **OPEN — the class row; it stays open as the place the next instance is recorded** | — | — | CC |
| **R-422** | Process & tooling | P4 | **`reuse_refs_check.py` only checks citations whose extension is one of `go py html css yml yaml sh`.** A cited `.md` path that does not exist is invisible — MEASURED 2026-09-01: `documentation/architecture/99-does-not-exist.md` added to `REUSE.md` passed, while the `.go` control was correctly convicted. REUSE.md and the CLAUDE.md files cite `.md` paths routinely, so this is the common case, not an exotic one. Fix: widen `PATH_RE`, then walk the false positives it produces across all four repos — that pass is the work, not the regex. The decoy is kept in `scripts/test_gate_decoys.py` asserting TODAY's behaviour, so the day this is fixed the test fails and is updated deliberately. | **OPEN** | — | — | CC |
| **R-423** | Process & tooling | P4 | **`site_gates.py` checks a hardcoded `PAGES` list of seven files; a new page is not scanned at all.** MEASURED 2026-09-01: a new `website/decoy-page.html` carrying an emoji and no nav or analytics passed. `felhom.eu/CLAUDE.md` already tells the author to add new pages to the list by hand — which is the R-410 shape written down as a procedure. Fix: glob `website/*.html` and rethink the per-page exemptions (`ANALYTICS_EXEMPT` and friends) so the list becomes a list of EXCEPTIONS rather than a list of what is checked. | **OPEN** | — | — | CC |
| **R-424** | Process & tooling | P4 | **`one_register_gate.py`: a real defect parked under the roadmap state `idea` is invisible to it.** MEASURED 2026-09-01 with a correctly-shaped 5-column row. This is **declared** in the gate's own docstring as residual hole 1 — *"the state column is a human judgement, and a defect written under `idea` looks exactly like a proposal to this gate"* — so it is honest, not hidden. Recorded here because a hole declared only in a docstring is not in the register, which is this project's own standing rule. No cheap fix: distinguishing a defect from a proposal mechanically is the thing the gate cannot do. | **OPEN — declared, not hidden; recorded so it is not re-derived** | — | — | CC |
| **R-425** | Process & tooling | P4 | **`offbox_rename_gate.py` scans a fixed three-entry `FILES` list.** MEASURED 2026-09-01: `NAS-mentés` in a new `backups_offbox_extra.html` passed. The scope was correct when written and silently narrows every time the feature grows a file. Fix: scan the offbox feature's files by pattern, or assert the FILES list against a discovered set so a new file fails until it is classified. | **OPEN** | — | — | CC |
| **R-426** | Process & tooling | P4 | **The decoy-coverage exemption list — 20 registered gates that ship WITHOUT a decoy test, each named.** `scripts/decoy_coverage_gate.py`'s `EXEMPT` map is debt, and this row owns it so it lives in the register and not only in a Python literal. **Four kinds:** (a) genuinely covered in the 2026-09-01 sweep but not yet moved into a suite — `hub-copy`, `instructions`, `docker-v`, `image-pins`; (b) blocked by an open hole and therefore un-assertable as rejecting — `site` (R-423), `one-register` (R-424), `offbox-rename` (R-425); (c) shared scripts whose decoy lives in `felhom.eu` and is counted there — `reuse-refs`, `instructions`, `observations` in the controller and agent runners; (d) **no plausible decoy constructed yet** — `hostinstall`, `wire-contract`, `due-checks`, `published`, `image-resolvable`, `volume-persistence`. Group (d) is the honest unknown: six gates whose soundness is UNTESTED, not established. **The list is green today and shrinks; a NEW gate with no decoy fails immediately.** | **OPEN — 20 names; group (d) is six untested gates** | — | — | CC |
+2
View File
@@ -4,6 +4,8 @@
pulls it. `test_no_latest_push.py` walks `hub/` and `scripts/` build files (red-proofs: the old Makefile, the old
build script).
- **R-418:** `repo_gates.py`'s docstring lists all 17 gates; `test_repo_gates_docstring.py` keeps list == `GATES`.
- **R-423:** `site_gates.py` fails on a website page that `PAGES` does not list (a walk); decoy added, the gate's
decoy-coverage exemption removed.
- **R-416:** `closed_register_gate.py` RULE 4 — an id twice in `CLOSED-ITEMS.md`; decoy in `test_gate_decoys.py`.
## gates — `script-tests`: every Python test suite under scripts/ runs on every push (R-885) (2026-10-05)
-3
View File
@@ -33,9 +33,6 @@ import sys
# Dated 2026-09-01. This list is DEBT, not a settled state: R-426 owns it and names every entry.
EXEMPT = {
# felhom.eu
("felhom.eu", "site"):
"R-423 — PAGES is a hardcoded list of 7 files; a new page is unscanned. The decoy passes "
"TODAY, so a test asserting rejection would be a lie. Fix is to glob website/*.html.",
("felhom.eu", "one-register"):
"R-424 — a real defect parked under state `idea` is invisible. Declared in the gate's own "
"docstring as residual hole 1; the decoy passes today.",
+12
View File
@@ -65,6 +65,18 @@ def norm_block(b):
return b
# R-423 (2026-10-05): PAGES is still the list the checks run on, but it may no longer be SHORTER than the site. Every
# *.html under website/ (a walk, any depth) must be in it — a page added without being added here used to go
# unscanned, which is how a gate checks seven files of nine and prints OK.
_on_disk = set()
for _dp, _dns, _fns in os.walk(W):
_dns[:] = [d for d in _dns if not d.startswith(".")]
for _f in _fns:
if _f.endswith(".html"):
_on_disk.add(os.path.relpath(os.path.join(_dp, _f), W))
for _missing in sorted(_on_disk - set(PAGES)):
fail("%s: an HTML page the site gates do not scan — add it to PAGES in scripts/site_gates.py (R-423)" % _missing)
pages = {}
for p in PAGES:
path = os.path.join(W, p)
+7
View File
@@ -53,6 +53,7 @@ COVERS = {
"(2026-10-03) an old-shape row under the new header, a near-miss category, an "
"old rank tag as Sev, an undefined state word, and a pipe outside backticks"),
"decoy-coverage": "a gate registered in a runner with no decoy and no exemption (its red-proof)",
"site": "R-423: a NEW page under website/ that PAGES does not list (the gate used to scan 7 of 9)",
"script-tests": ("R-885: FIVE cases in scripts/test_script_tests_gate.py, run from here: a suite that PRINTS "
"OK and exits 1 (label without fact), a failing suite three levels deep (scope is a walk), "
"an empty scripts/ tree (checked nothing), the genuine article (must pass), the nested mark"),
@@ -371,6 +372,12 @@ else:
_n = _gq.stdout.strip().splitlines()[-1] if _gq.stdout.strip() else "?"
print(" ok %-20s %s" % ("guide-quote", _n))
# ── site (R-423) ─────────────────────────────────────────────────────────────────────────────────
# A page that exists but is not in PAGES. The content is a perfectly valid page, so only the scope rule can convict.
decoy("site/unlisted-page", "site_gates.py",
plant_file(os.path.join(ROOT, "website", "zz-r423-decoy.html"),
u"\ufeff<!DOCTYPE html><html><body><nav></nav><footer></footer></body></html>\n"))
# ── script-tests (R-885) ─────────────────────────────────────────────────────────────────────────
#
# Its decoys are whole fake repos, so they live in their own file and are RUN from here (guide-quote's shape).