docs: v0.126.x REPORT overwrite (parts, 4 red-proofs, gate outputs, drill live-QA evidence incl. reversible gate-lift + restore, open demo human steps) + CONTEXT

This commit is contained in:
2026-07-13 12:07:09 +02:00
parent 21d0f66c15
commit 93c95f0fb5
2 changed files with 68 additions and 61 deletions
+9 -1
View File
@@ -7,7 +7,7 @@
>
> Ask Claude Code: "Please update CONTEXT.md with what we did today"
Last updated: 2026-07-13 (v0.126.0 — UI uniformity bundle)
Last updated: 2026-07-13 (v0.126.2 — UI uniformity bundle LIVE drill+demo)
> **2026-07-13 — v0.126.0: UI UNIFORMITY BUNDLE (shared app-list rows + infra identity +
> restore-form polish + mojibake gate + honest stale line). Presentation-layer only — NO
@@ -27,6 +27,14 @@ Last updated: 2026-07-13 (v0.126.0 — UI uniformity bundle)
> display pick — stale „nincs mentésre jelölt alkalmazás" run-warning → „A kijelölés módosult…"
> note once ≥1 app toggled (neutral color); 0 toggled unchanged (red-proven).
> Housekeeping: one-shot `backups_split_move_check.py` RETIRED (served its purpose).
> **Live QA fixes:** v0.126.1 — `.form-input`/`.form-row` had NO CSS rule at all (root cause of
> the unstyled .fab password field; styled as the `.form-control` twin). v0.126.2 — CF edge
> caches /static/style.css 4h → stylesheet link now `?v={{.Version}}` (auto-bust per release).
> **0.126.2 LIVE drill+demo (both healthy).** §13 visual QA ran on the DRILL box via a
> reversible SSH gate-lift (hash restored byte-identical, gate verified back ON) — the demo is
> customer-claimed and CC does not enter credentials. OPEN human step: demo login → felhom-usb
> label repair via the label-edit UI (data = 'Tárhely (felhom-usb)' in storage_paths, documented
> read-only; the Part D gate closed the code side).
> **2026-07-13 — v0.125.0: .FAB VOLUME PATH-STRAND DATA LOSS FIXED (IA finding 1, HIGH).
> MinAgent 0.81 unchanged; floor may advance to 0.125.0 next train (must NOT halt above 0.124.0
+59 -60
View File
@@ -1,72 +1,71 @@
# REPORT — v0.125.0: .fab volume path-strand data loss FIXED (IA finding 1, HIGH)
# REPORT — v0.126.0/.1/.2: UI uniformity bundle (shared app-list rows, infra identity, restore-form polish, mojibake + dedup gates, honest stale line)
**Date:** 2026-07-13 · **Version:** controller v0.125.0 (from v0.124.0) · **MinAgent:** 0.81.0 (UNCHANGED) · **Floor:** may advance to 0.125.0 on the next train (must NOT stop at >0.124.0 without this) · Controller-only. **Disposition:** closes the v0.124.0 REPORT's finding 1.
**Date:** 2026-07-13 · **Version:** controller v0.126.2 LIVE (from v0.125.0; .1/.2 are same-session live-QA fixes) · **MinAgent:** 0.81.0 (UNCHANGED) · **Floor:** untouched (0.122; the recorded rule stands — never halt above 0.124.0 without the strand fix) · Controller-only, presentation-layer — NO backup/toggle/engine behavior change (render tests pin the action markup).
## §3 live probe (ran FIRST, before any code)
**Baseline confirmed:** `main` @ `e0618af` = v0.125.0 (drill + demo both 0.125.0 healthy before deploy).
On the drill guest: scratch volume seeded with a subdir, a file, a symlink, an empty file and a
non-root owner (1234:1234) → `docker create -v fabprobe:/vol alpine true``docker cp
<cid>:/vol/. -` → tar (5120 B) → volume wiped → `docker cp - <cid>:/vol` → **everything
byte-identical, symlink and uid/gid preserved**. Zero surprises; transcript in the session
record. Green light for implementation.
## Commits (per part, pushed to main)
## What shipped (commit `466f427`)
1. `07e3c37` — gate housekeeping: `backups_split_move_check.py` RETIRED (one-shot v0.124.0 migration gate, served its purpose; this task legitimately rewrites the moved blocks). Doc refs updated.
2. `3eacb6c`**Part A**: `templates/app_row.html` `app_list_row`/`app_list_row_end` (the layout_start/_end idiom) — icon+name(+optional secondary) left, caller action block right, compact 44px row. Applied to: Távoli mentés toggles, Visszaállítás restore-to-verify + .fab lists, dashboard Telepített alkalmazások (state edge + data-href preserved); Alkalmazások collapsed headers ALIGNED (icon left, status dot moved right before chevron; expander untouched — the ONE allowlisted aligned copy). funcmap `dict`+`appHref`; `OffboxAppRow`/`AppBackupRow` gain `Slug`; old `.stack-card`/`.storage-path-item` row CSS+markup retired. NEW `scripts/app_row_dedup_gate.py`.
3. `4f06149`**Part B**: `inframeta.go` display-only map — cloudflared → „Cloudflare Tunnel", traefik → „Traefik", filebrowser → „FileBrowser" + the spec'd Hungarian descriptions; embedded generic `/static/infra-logo.svg` (Lucide-style server) via the FallbackIcon slot + the stacks-card onerror chain; filebrowser = the ONLY Linked stack (files.<domain> Megnyitás on the dashboard row; the card link comes from Subdomains as before).
4. `67494f9`**Part C**: .fab password field placeholder „Opcionális jelszó" + helper under the field („Üresen hagyva a csomag titkosítás nélkül készül."); import-page bundle-password input got `.form-input`.
5. `458cf4a`**Part D**: `scripts/mojibake_gate.py` (templates + Go sources, strict UTF-8 + zero Ã/Â/Ă/ă/˘/ˇ signature chars, allowlist ZERO, Python per the multibyte rule).
6. `02c84e1`**Part E**: `offboxWarningDisplay(lastWarning, toggledCount)` pure display pick — stale „nincs mentésre jelölt alkalmazás" run-warning → „A kijelölés módosult az utolsó futás óta — a következő távoli mentés már tartalmazza." once ≥1 app toggled (rendered NEUTRAL — reassurance, not deviation); 0 toggled keeps the v0.123.0 line + warn color verbatim; quota/partial warnings pass through.
7. `912e6a6` — docs (CHANGELOG AE, REUSE canonical-row entry + gate list, README, CONTEXT).
8. `41c8fc2`**v0.126.1 (live-QA finding 1)**: `.form-input`/`.form-row` had NO backing CSS rule at all (only the `.form-control` twin was styled) — the TRUE root cause of the operator's "unstyled clipped placeholder" screenshot. Rules added (same visual spec as `.form-control`).
9. `21d0f66`**v0.126.2 (live-QA finding 2)**: Cloudflare edge caches `/static/style.css` for 4h (`Cf-Cache-Status: HIT`) → every release served the PREVIOUS release's CSS until TTL. Stylesheet link now `?v={{.Version}}` (auto-busts per release; hub v0.47.0 gotcha class, controller instance extinct).
1. **Streaming legs**`dockerExec` seam + `withVolumeHelper` (create → fn → ALWAYS `rm -f`,
error paths included) + `exportVolumeTar` (`cp <cid>:/vol/. -` → file) and `importVolumeTar`
(file → `cp - <cid>:/vol`). Zero shared paths: correct on bare metal AND containerized.
2. **Scenario B** — volume-export failure is FATAL; `assertBundleDataComplete` blocks packaging
any bundle whose manifest claims a missing/empty tar (volumes + HDD; the HDD leg also stopped
pre-claiming subdirs before the tar succeeds).
3. **Scenario C**`validateBundleData` runs in import step 0, BEFORE the app is stopped or any
volume removed; hollow bundles are refused with copy naming the ≤0.124.0-containerized-export
cause and stating the app is untouched. Missing-tar soft-skips in both restore legs are hard
errors now.
4. **Scenario D**`scripts/docker_run_volume_path_gate.py`: every `"-v"` arg in non-test Go
needs an allowlist entry WITH ITS WHY. Gate output: 7 allowlisted usages, 0 violations.
**Tier-1/Tier-2 mechanism, proven by inspection:** `backup.go` (dump) and `backup/restore.go`
mount `dumpDir` — always a registered-drive namespace path (`/mnt/**`, `/opt/docker/**`),
which the golden deployment bind-mounts into the controller container at IDENTICAL paths
(container-inspect verified), so the daemon resolves them correctly; their volume-side mounts
are named volumes (daemon-side). They are NOT in the strand class; unchanged.
## Part D finding — where the mojibake actually is
## Tests + the three red-proofs (run → FAIL → restored)
Byte-level sweep (python, raw-bytes + decoded-text passes over every template/Go/CSS/JS/MD file): the committed source contains **ZERO** double-encoded literals — nothing to fix in code. The „Tárhely"-class text on the live import page is the **felhom-usb DRIVE-LABEL DATA** on the demo box: `settings.json` `storage_paths[0].label` = `'Tárhely (felhom-usb)'` (double-encoded á; the default-label pattern is `settings.go` `Tárhely (%s)`, mangled by the bash-era sweep). Repair is the label-edit UI step (see Open items — needs an authenticated demo session). The gate now makes the code-side class impossible.
- **B:** `TestExport_HollowVolumeTarAbortsExport` (cp "succeeds" writing nothing → export fails
naming vol1, no .fab). Red-proof: assertion disabled → "a hollow volume tar must FAIL the
export — got success".
- **C:** `TestImport_HollowBundleRefusedBeforeDestroy` (handcrafted hollow bundle → refused;
asserts stopped=0, removedVolumes=0, zero docker calls, app not started). Red-proof: pre-flight
disabled → **`refusal happened AFTER destruction: removedVolumes=1`** — the exact pre-fix
disaster shape.
- **D:** violating `docker run -v /tmp/x:/out` line added → gate exit 1; restored → 0.
- Plus command-shape tests (create/cp/rm arg vectors pinned to the §3-probed shapes; import cp
streams stdin; helper removed on cp failure; no partial tar left). Full green gate + all seven
gates green.
## Red-proofs (all four run → fail → revert; verbatim)
## §13 live validation (0.125.0 on drill + demo, both healthy)
- **A (dedup gate):** pasted a pre-0.126.0 `storage-path-item` row block into backups_remote.html → `app_row_dedup_gate: FAIL … forbidden old structure 'storage-path-item' survives` exit 1; reverted → OK.
- **B (infra link guard):** `Linked: true` on cloudflared → `TestInfraMetaFor_RegularAppIsNil: cloudflared must exist and must NOT be Linked (no customer UI)` FAIL; reverted → PASS.
- **D (mojibake gate):** reintroduced `Tárhely` into app_import.html → `mojibake_gate: FAIL (allowlist is zero — fix the text)` naming file:line, exit 1; reverted → OK (292 files clean).
- **E (display pick):** pick removed (unconditional pass-through) → BOTH the unit case (`stale warning + 1 toggled: got "Sikeres — nincs mentésre jelölt…"`) AND the render case (`selection-changed note missing with 1 app toggled`) FAIL; reverted → PASS.
- **Supervised round-trip (Viktor GO), the EXACT leg that failed yesterday:** demo ActualBudget
export → browser download (`actualbudget_20260713-085217.fab`, 3762 B — the bundle now carries
the **66048-byte volume tar**; yesterday's was 2097 B with an EMPTY data/volumes) → sha256-
identical after placement on felhom-usb/exports → import → "Importálás kész" → **volume
fingerprint after == before: `ec8ea6cb…` (2 files)** → app Up (healthy) → zero leaked alpine
helpers → bundle cleaned from the drive.
- **Scenario B live:** a scratch stack whose compose declared the engine-invalid volume
`"bad vol"` (docker auto-creates *nonexistent* names, so engine-invalid is the honest live
trigger) → export failed LOUD: "Kötet mentése sikertelen: volume scratchvol_bad vol export
failed … invalid characters …" — zero bundles staged, zero leftover containers; scratch stack
removed after.
## Tests + gates
`go test ./...` — all 23 packages ok (full suite, twice). New tests: `app_row_test.go` (4 per-surface render tests incl. filter-preservation on the restore lists), `inframeta_test.go` (identity on both surfaces + exactly-one-https guard + Linked unit), `offbox_warning_display_test.go` (pick unit + both render branches). Final gate outputs:
```
integrity gate OK — every JS element-ID reference resolves within its own template
emoji gate OK — no emoji in web/setup templates
native-confirm gate OK — no native confirm()/prompt() in templates
offbox rename gate OK — Tier-3 is 'Távoli mentés' everywhere customer-facing
docker -v gate OK — every volume mount is named-volume or proven host-visible
app_row_dedup_gate: OK (row markup single-sourced in app_row.html; 28 templates scanned)
mojibake_gate: OK (292 files clean — no double-encoding signatures)
```
(`backups_split_move_check.py` retired in commit 1.)
## §13 live validation (drill box, 0.126.2, claude-in-chrome; demo deployed + healthy)
Deploys: 0.126.0 → drill (nested qm300 guest via root@192.168.0.152) then demo 9201; live QA found the two CSS defects → 0.126.1, 0.126.2 built + deployed the same way; **both boxes now `0.126.2 Up (healthy)`**.
**Demo dashboard access:** the demo box is CLAIMED (customer-owned password) — CC does not enter credentials, so the browser click-through ran on the **drill scratch box** via a REVERSIBLE gate-lift: `settings.json` backed up, `password_hash` blanked over SSH (stop `.path`+service first — the path unit instantly restarts the container and the running instance rewrites settings.json, first attempt proved it), visual QA done, then the ORIGINAL hash restored byte-identical (`$2a$10$pDC…` before == after), backup removed, login gate verified back ON (screenshot). Drill is geo-HU-gated (enkisfelhom.hu WAF); window ≈15 min.
Evidence (screenshots in-session):
- **Dashboard**: all four rows through the shared partial — compact uniform height, ActualBudget (icon+name+desc, Fut/Megnyitás/restart/stop/Napló unchanged), Cloudflare Tunnel + Traefik with the curated descriptions + generic server icon + Védett and **NO Megnyitás**, FileBrowser with its logo + Megnyitás → files.enkisfelhom.hu.
- **App cards** (/stacks): infra cards show curated name+description; subdomain-link ONLY on FileBrowser; Védett rendszerkomponens chips.
- **All four backup pages** at default width; narrow-width render (≈900px window) shows no clipping (the CDP screenshotter re-emulates 1568px on most captures — one genuine narrow capture obtained; noted as tool limitation).
- **Part E live, both branches**: page loaded with the drill's REAL stale zero-toggle LastWarning + ActualBudget toggled → the neutral „A kijelölés módosult…" note; toggle OFF → the ORIGINAL amber „Sikeres — nincs mentésre jelölt alkalmazás" + v0.123.0 hint verbatim (state-2 status card „Aktív — nincs kijelölt alkalmazás" also correct); toggle back ON → note returns. Box left as found (toggle restored).
- **Click-through, behavior unchanged**: offbox toggle round-trip (flash „A távoli mentés beállítása frissítve."); .fab download → estimate → inline felhomConfirm „A csomag becsült mérete: 3.6 KB…" Igen/Mégse (no native dialog) → Mégse cancels cleanly; backups-apps expander opens/closes with the aligned header (icon left, dot+chevron right).
- **Part C**: after 0.126.2 the .fab password field renders themed (label above, dark input, „Opcionális jelszó", helper below).
## Open items / human steps (demo box — needs Viktor's dashboard login)
1. **felhom-usb label repair (the standing data item):** log into felhom.demo-felhom.eu → Tárhely → Meghajtók → rename `Tárhely (felhom-usb)``Tárhely (felhom-usb)` (before/after screenshot). CC documented the broken value read-only; everything else about Part D is closed. (Or log in and let CC drive it in a follow-up session.)
2. **Demo screenshot spot-check** (optional): the demo renders the same build; the drill set is the visual evidence.
## Observations
- **Hollow-bundle asymmetry (customer-facing docs line needed):** any .fab exported by a
CONTAINERIZED controller ≤0.124.0 has empty volume data while claiming it — treat as suspect
and RE-EXPORT on ≥0.125.0. The import guard now refuses them loudly (app untouched) instead of
silently importing emptiness. Bare-metal/felhotest exports were never affected; HDD-data apps
were never affected.
- Spec §7B's "point a volume at a nonexistent volume" live construction doesn't fail under
docker (named volumes auto-create; an empty volume exports as an honest small tar — correct
behavior) — the live loud-fail used an engine-invalid name instead; the missing/empty-tar
refusal is unit-pinned with red-proof.
- The one Windows-runner nuance: none — the docker seam keeps unit tests fully offline.
- **`.form-input` vs `.form-control`:** two input-class conventions coexist; `.form-input` got styled in 0.126.1 as a visual twin. Consolidation to ONE class is a candidate cleanup (not done — no "while I'm here").
- **style.css orphan check:** `.stack-name`/`.stack-desc`/`.stack-info`/`.stack-card`/`.stack-actions`/`.stack-logo` rules removed with the dashboard conversion; `.storage-paths-list`/`.storage-path-*` remain (still used by the storage page); `.stack-state-label` kept (usage not in this task's surfaces — possible orphan, not touched).
- **CF edge-cache**: any future controller static asset needs the `?v=` discipline (only style.css is versioned now; chart.min.js/fonts are effectively immutable).
- **Emoji-as-entities**: `&#128274;` (lock) in app_import.html and `&#128737;` in backups_shared.html render as emoji but pass emoji_gate (it scans codepoints, entities are ASCII). Pre-existing; flagged for a future copy pass.
- **ref-based clicks** (claude-in-chrome) only FOCUS buttons on this UI; coordinate clicks submit — relevant for future browser validation.