Slice 6 parts 0 and A: the guide in English, golden 0.258.0, and the CI finding
gates / gates (push) Successful in 30s

The volunteer guide has an English twin. It is a TRANSLATION, not a rewrite: 16
sections in the same order, identical step counts, table rows and warning blocks per
section (measured, 0 sections differing in structure). Word counts are NOT a twin —
English runs 19 % longer overall and up to 42 % on the short sections, because
Hungarian is agglutinative; the +-15 % criterion the task asked for does not survive
contact with this language pair, so structure is the measure reported instead.

Golden 0.258.0 baked, published and vouched, with its record. One run, no aborted
attempts: the 0.246.0 bake's two traps were both avoided by following its own record.
Token proven not to leak with a planted control before the zero was believed.

The waiver is NOT retired, and the record says why in one line: it is the mechanism of
operator ruling R-468, not a note about this golden, and deleting it would turn the
next release without a bake red immediately. It is also not load-bearing today.

R-595: the catalog's copy gate could not run in CI at all — six pushes red, six alarm
mails, while the local hook was green. Found by reading the operator's inbox, not by
anything in the session that caused it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
2026-09-20 18:29:33 +02:00
parent 8e9401c6bf
commit cf09c78743
13 changed files with 10887 additions and 0 deletions
+1
View File
@@ -770,6 +770,7 @@ class (an image `VOLUME` at an unmounted path) is still live — `immich-server`
| **R-592** | **[P3-LOW] Two defects in the new catalog copy gate, each found by its own decoy rather than by reading it.** FOUND AND FIXED 2026-09-20 building `app-catalog-felhom.eu/scripts/check-copy-i18n.py` (R-560 slice 5). (1) **Coverage was counted only for the apps NAMED on the command line**, so `check-copy-i18n.py privatebin` reported 47 more untranslated strings than the same tree unscoped — a ratchet anybody could loosen by naming one app, and either number could have been made to "pass". (2) **The credential check searched for its token as a bare substring**, so a `default_creds` of „admin / adminadmin" translated to "administrator / hunter2" PASSED: "admin" is inside "administrator". (3) **The ASCII-Hungarian stems matched as bare substrings too**, so „ird be" convicted "the third best" and „angol" convicted "Angola". All three now have their own case in `scripts/test_gate_decoys.py` (33 cases, every one seen to convict or pass as intended). **Recorded rather than left in a commit message** because the general form is worth the row: every one of the three was a check that matched a LABEL where the FACT was a word, a login or a whole catalog — the R-421 shapes, inside the gate written to enforce R-421. | **CLOSED 2026-09-20 — fixed in the same session, decoys added** |
| **R-593** | **[P3-LOW] papra's session-signing key is described as „the app's subdomain".** FOUND 2026-09-20 translating the catalog (R-560 slice 5, batch 1). `templates/papra/.felhom.yml` `deploy_fields[AUTH_SECRET].description` reads **„Az alkalmazás aldomainje"** — the sentence that belongs on `SUBDOMAIN`, on a field that signs sessions. `SUBDOMAIN` itself has NO description at all in that file, so this is a copy-paste that landed one field too low and took the original with it. A customer opening papra's install page reads a wrong explanation under a key they must not regenerate. **A localisation release may not change Hungarian bytes (10 §1)**, so it was not fixed there; and translating a wrong sentence faithfully would have shipped the error in a second language, so **that one field was left untranslated** — it falls back to the Hungarian exactly as today, and it is the ONE string keeping papra at 13/14 and the catalog ceiling off zero. **Fix shape:** move the sentence to `SUBDOMAIN` and give `AUTH_SECRET` its own („A munkamenetek aláírásához használt kulcs" or similar), re-capture that app's two entries in `scripts/copy_freeze/hu.json` in the same commit with the reason, then add the English. Owned by R-516 as a Hungarian-words change. | **READY - rank P3-LOW; owner: CC (catalog)** |
| **R-594** | **[P3-LOW] The catalog copy gate can CONVICT a retrieval promise but has no way to REGISTER a true one.** FOUND 2026-09-20 translating batch 3 (R-560 slice 5). Vaultwarden's invite step and its sign-up setting both ended „…can open an account", and the English retrieval-promise pattern reads `can … open` as the claim that sealed backups can be opened. **The conviction was a FALSE POSITIVE** — opening an account is not opening a backup — and the two sentences were reworded to „can sign up", which is also the better copy, so nothing is blocked today. **The gap is structural.** The shared vocabulary this gate copies (`scripts/customer_copy_vocab.py`) states the design explicitly: these stems are NOT banned, because each carries a claim that is sometimes TRUE, and *"an occurrence must be REGISTERED with a reason in the consuming gate's allowlist"*. The hub gate has `ALLOWLIST_EN`; `app-catalog-felhom.eu/scripts/check-copy-i18n.py` has none, so the only ways past it are to reword or to bypass the gate — and a catalog app whose English genuinely says a file can be restored (a backup app, a versioned document store) has no honest third option. **Fix shape:** an `ALLOWLIST_EN` of `(app, path, reason)` in the gate, a decoy proving a REGISTERED occurrence passes and an unregistered one still convicts, and a check that every entry still matches something (a stale allowlist entry was R-299's shape, and the retrieval gate has gone red on stale entries before). | **READY - rank P3-LOW; owner: CC (catalog)** |
| **R-595** | **[P2-MEDIUM] The catalog's new copy gate could not RUN in CI at all — six pushes red, six alarm mails, while the local hook was green.** FOUND 2026-09-20 by reading the operator's inbox at the start of localisation slice 6 — **not** by anything in the session that caused it, which is the point worth keeping. Every one of slice 5's six catalog pushes (`e81d41e`, `d9aa02a`, `0695d8e`, `5fe70d1`, `1f80650`, `de392cd`) produced *[felhom CI] gates FAILED*, and the alarm's own text says what that means: *"If the local pre-push hook was GREEN for this commit, then CI and the hook disagree — that is a finding about the gates themselves, not about CI, and it outranks whatever the push was for."* **Cause, found by CONTRAST:** `check-copy-i18n.py` imports PyYAML and is the only gate in that repo importing anything outside the standard library; `.gitea/workflows/gates.yml` states in its own header that the runner is *"a host-mode container with python3 and git and nothing else"*. The gate raised `ImportError` before checking anything, so the runner exited non-zero on every push, clean ones included. **FIXED the same session (catalog `18a6d2d`, CI job 791 SUCCESS, verified by id):** a DEGRADED MODE rather than a skip — without PyYAML the gate runs the check that needs no parser and matters most (every frozen Hungarian string must still occur verbatim in its app's bytes) and prints in full what it did NOT check. Measured first: 1 030 of 1 032 frozen strings appear byte-for-byte in the raw files; the two that do not are romm help_texts whose YAML escapes an inner double quote, so the escaped spelling is accepted too — 1 032 of 1 032 found, so the degraded check convicts nothing honest. Five new decoy cases run with PyYAML shadowed by a module that refuses to import, i.e. what CI actually executes. **The general form, and the reason this is P2 rather than P3:** a new gate is written and tested on the machine that has every library, and the runner deliberately has none. **Nothing in the pre-push hook can catch that** — the hook runs on the same rich machine. The only detector is the alarm mail, and it worked; what failed is that six of them went unread for two hours inside the session that caused them. | **CLOSED 2026-09-20 — degraded mode; CI job 791 green** |
| **R-537** | **[P1-HIGH] The app-backup page labels the tier-1 backup „DB + Konfig + Adatok" and prints the app's data-drive size next to it — but the tier-1 unit contains NO drive-side app data at all.** MEASURED 2026-09-16 on the drill box (fresh install, controller 0.243.0, one drive, tier 2 and tier 3 both „Nincs beállítva"): five photos (3 000 000 B) were uploaded into Nextcloud through its own WebDAV interface, then the customer-visible „Mentés most" was pressed (`POST /api/backup/run` → 200, the unit grew 25 337 B → 978 MB). The resulting unit's `manifest.json` lists `db-dumps` + three **docker volume** dumps and nothing else; listing the 781 MB `nextcloud_nextcloud_html.tar` (29 346 entries, positive control `version.php` = 3 hits) gives **`Fotok` = 0 and `nyaralas` = 0**, and `./data/` is the empty bind-mount point. A `find` over the whole `backups/` tree for `*appdata*` / `*Fotok*` returns nothing. The page nevertheless renders „1. mentés … DB + Konfig + Adatok" and „Nextcloud Adatlemez 65.1 MB" — a size measured on exactly the data it does not copy (`internal/web/handlers.go:1176-1178`, `BackupContents`). **This is a truth defect, not a design defect:** `07-backup-architecture.md` §6.2 places nextcloud's file leg at **Tier 2 and Tier 3 only**, and its „[FACT] What the whole-guest tiers do NOT carry" says `mp8 /mnt/felhom-drives` is out of vzdump scope (confirmed live: „excluding bind mount point mp8 … (not a volume)"). So on a one-drive box with no off-site tier — the state every fresh install starts in — the household's files are in **no backup**, while the page says „Adatok". Same family as R-517/R-518. **Fix shape:** render tier-1 contents from the capture set actually written (`ComputeCaptureSet`), so a unit with no file leg reads „DB + Konfig" and the drive size is not shown beside it; and say on the page that the app's files need tier 2 or tier 3. Evidence: `audits/evidence-drill-0243-2026-09-16/phase2-f10.txt`. **CLOSED 2026-09-16 — controller v0.244.0, proven live.** The contents label is computed PER TIER from what that tier captures: Tier 1 says „Adatok" only when the app's data really is in the volumes the unit captured, and a class-A app carries one sentence saying where its files ARE protected. Proven on demo-hp through the page the customer opens: Paperless-ngx reads „1. mentés … DB + Konfig" with „Az alkalmazás fájljait a távoli másolat (és a második meghajtó) védi …", while its „2. mentés" row still reads „DB + Konfig + Adatok". Red-proof: restoring the old app-shaped label fails `TestAppBackupRows_Tier1LabelDoesNotClaimFilesItCannotHold`. **RE-PROVEN 2026-09-16 on a FRESH box** (installed from the built ISO 1.28.0, controller 0.244.0, off-site on by default): the Nextcloud row read „1. mentés … DB + Konfig" with the new sentence, „2. mentés … Nincs 2. (off-drive) másolat", „3. mentés Sikeres restic → …your-storagebox.de"; „DB + Konfig + Adatok" appeared ZERO times while the local unit held no file leg. | **CLOSED 2026-09-16 — controller v0.244.0 (proven live on demo-hp)** |
| **R-538** | **[P1-HIGH] A tier-1 app restore reports plain success and leaves Nextcloud listing files whose bytes were never in the backup — and it destroys the app's own trash, the customer's last copy.** MEASURED 2026-09-16 on the drill box, F10 („a child deletes the photo folder"): the five photos were deleted through Nextcloud (DELETE 204, PROPFIND 404), then restored through the page exactly as a customer would (`POST /backup/restore` `stack_name=nextcloud` `snapshot_id=helyi` → 302, finished in **35 s**, „A(z) nextcloud: 3 adatkötet és az adatbázis visszaállítva — az alkalmazás újraindult."). Afterwards the folder is back and **lists all five photos**, and **none of them opens**: `GET nyaralas-1..5` = 404 / 503×4 with `Sabre\DAV\Exception\NotFound`, while the positive controls at the same moment pass (`status.php` 200, WebDAV PUT 201, GET 200). Cause: the replayed MariaDB dump (11:01:45Z) knows the photos, the bytes live on `mp8` and were never captured (R-537). **Worse:** the bytes were still on the drive in Nextcloud's own trash (`appdata/nextcloud/admin/files_trashbin/files/Fotok.d1789556707/nyaralas-1..5.jpg`, all five present) and the restored database no longer references them — the trash listing comes back **empty**, so „restore from trash", the one route that would have worked, is gone. The customer is left with five unopenable photos, a success message, and no warning. **Fix shape:** before replaying a database whose app has an uncaptured file leg, refuse or warn („ennek az alkalmazásnak a fájljai nincsenek ebben a mentésben — a visszaállítás után a fájlok hiányozni fognak"); and never present a DB-only restore of a class-A app as a complete one. Evidence: `audits/evidence-drill-0243-2026-09-16/phase2-f10.txt`. **CLOSED 2026-09-16 — controller v0.244.0, proven live.** A unit restore refuses before anything is touched when the unit cannot return the app's drive-side files, and names the route that can. Fired live on demo-hp: `POST /backup/restore` for paperless-ngx → 302 with „Ez a mentés nem tartalmazza az alkalmazás fájljait, ezért nem állítjuk vissza az adatbázist föléjük — a fájlok így a helyükön maradnak. A fájlok a távoli másolatból állíthatók vissza …", and the app read `running` before AND after, so nothing was stopped and no trash was made unreachable. The database-and-settings-only path exists as a separately worded second step. Red-proof: disabling the guard fails `TestUnitRestore_RefusesWhenTheUnitCannotHoldTheFiles`. **RE-PROVEN 2026-09-16 on a FRESH box, and this time the refusal had somewhere to point:** after five photos were deleted, `POST /backup/restore` was refused with „…a fájlok így a helyükön maradnak. A fájlok a távoli másolatból állíthatók vissza: … „Teljes visszaállítás (fájlok + adatbázis)"", the app read `running` before AND after, and the wastebasket was untouched. The off-site route then returned all five photos — 200 with the exact uploaded sizes and sha256 IDENTICAL to the originals, 5/5, with a negative control. Evidence: `audits/evidence-backup-promise-2026-09-16/phaseE-photos.txt`. | **CLOSED 2026-09-16 — controller v0.244.0 (proven live on demo-hp)** |
| **R-525** | **[P3-LOW] FileBrowser has its own login; putting it behind the dashboard session (traefik forwardAuth or Quantum proxy auth) is a new mechanism nobody has measured.** Filed 2026-09-15 by the P1-fixes task (B.5). R-513 closed the default-password hole with a generated password; a household still has two logins. **What it needs:** a spike on a scratch guest — forwardAuth to the controller session, and what FileBrowser Quantum does with a trusted header. | **READY — rank P3-LOW; owner: CC (spike)** |