From 2fb558552aea6a5e1fbab5dab04ee6bbd9873498 Mon Sep 17 00:00:00 2001 From: kisfenyo Date: Wed, 2 Sep 2026 20:35:17 +0200 Subject: [PATCH] =?UTF-8?q?REPORT:=20v0.233.0=20live=20=E2=80=94=20the=20r?= =?UTF-8?q?ecord=20is=20proven=20on=20metal,=20the=20badge=20render=20is?= =?UTF-8?q?=20not,=20and=20why?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The record: proven on demo-hp through the boot reconciler (a real production caller, no hand-set state) on a single-service AND a multi-service app, with all three digests matching ground truth read independently beforehand. The badge render: NOT validated. The vaulted dashboard password is stale on BOTH demo controllers; the five attempts are listed rather than summarised, and the controller's own log is the discriminator that says wrong password, not wrong host header. Filed as R-453 and raised in STATUS.md item 9. Also recorded: the build was blocked by a Docker Hub 429 and the base images came from Google's Hub mirror, with both digests written down so the identity check is one command when the throttle clears - KNOWN, not measured here. --- REPORT.md | 383 +++++++++++++++++++++++++++++------------------------- 1 file changed, 203 insertions(+), 180 deletions(-) diff --git a/REPORT.md b/REPORT.md index ecbd29f..67ced9f 100644 --- a/REPORT.md +++ b/REPORT.md @@ -1,216 +1,239 @@ -# REPORT — the decoy sweep: can a gate be fooled by a label? (2026-09-01, R-421) +# REPORT — v0.233.0, update arc slices 1 & 2 (2026-09-02) -## The survey — every gate, its shape, and whether a decoy passed BEFORE this session +*Overwritten each run. This records the most recent implementation only.* -| gate | runner(s) | meant to prove | actually matched | shape | decoy passed? | -|---|---|---|---|---|---| -| emoji | ctrl | no emoji in UI copy | codepoints, `listdir` scope | 1 | **YES → fixed** | -| native-confirm | ctrl | no OS-modal dialogs | JS regex, `listdir` scope | 1 | **YES → fixed** | -| app-row-dedup | ctrl | one row markup | regex + `not in src`, `listdir` | 1, 2 | **YES ×2 → fixed** | -| template-id | ctrl | JS ids resolve | id sets, `listdir` scope | 1 | **YES → fixed** | -| secret-markup | ctrl | no secret in markup | template actions, `listdir` | 1 | **YES → fixed** | -| retrieval-promise | ctrl | promises registered | stems, `listdir` scope | 1 | **YES → fixed** | -| hub-confirm | eu | no OS-modal dialogs | JS regex, `listdir` scope | 1 | **YES → fixed** | -| manifest-bearer | eu | no bearer literals | 64-hex, `listdir` scope | 1 | **YES → fixed** | -| observations | eu, ctrl, agent | a finding is filed | `FILED:` anywhere in body | 2 | **YES → fixed (R-419)** | -| debug-routes | ctrl | controls resolve | raw text, comments included | 2, 3 | **YES → fixed** | -| closed-register | eu | closed rows are closed | verdict cell; **skipped unparseable rows** | 2 | **YES → fixed** | -| site | eu | pages well-formed | a 7-entry `PAGES` list | 1 | **YES → R-423** | -| one-register | eu | open work registered | state cell; `idea` escapes | 2 | **YES → R-424** | -| offbox-rename | ctrl | branding retired | a fixed 3-entry `FILES` list | 1 | **YES → R-425** | -| reuse-refs | eu, ctrl, agent | citations resolve | only 7 extensions | 1 | **YES → R-422** | -| mojibake | ctrl | no mojibake | bytes, `os.walk` | 5 | NO — **the control** | -| docker-v | ctrl | `-v` mounts safe | argv, `os.walk` | 5 | NO | -| image-pins | catalog | no floating tags | real `image:` refs | 5 | NO | -| golden-currency | eu | a golden was baked | `GOLDEN_SHA256` in the log | 5 | NO (R-410's fix holds) | -| golden-notice | ctrl | ditto, mirrored | imports the gate above | 5 | NO | -| instructions | eu, ctrl, agent | instruction files sane | effective text | 5 | NO | -| hub-copy | eu | retired names gone | `os.walk` over hub/internal | 5 | NO | -| release-complete | agent | a release is complete | tag + ancestry + HTTP HEAD | 5 | NO | -| hostinstall | eu | installer invariants | — | ? | **UNKNOWN** | -| wire-contract | eu | emitted fields decode | — | ? | **UNKNOWN** | -| due-checks | eu | dated checks fire | — | ? | **UNKNOWN** | -| published | agent | versions published | — | ? | **UNKNOWN** | -| image-resolvable | catalog | images exist | `docker manifest inspect` | 5 | **UNKNOWN** | -| volume-persistence | catalog | data survives | runs containers, diffs | 5 | **UNKNOWN** | +> **Read this first: one claim in the task turned out to be wrong, and it is a path, not a fact.** +> The task cites source as `internal/stacks/deploy.go`, `internal/web/funcmap.go` and so on. **The Go +> module lives under `controller/`** — every one of those is `controller/internal/...`. **Every line +> number in the task was EXACT against the baseline** (`AppConfig` 99, `runComposeDeploy` 395, +> `LoadAppConfig` 792, `SaveAppConfig` 806, `SensitiveEnvVars` 889, `stackEnv` 1233, +> `composeExecCustomEnv` 1268, `execCommand` 1344, `logPostStartStatus` 1382, `checkLocalImages` 1410, +> `Metadata` 14, `isOperationalState` 90) — checked, not assumed. The rest of §10 is at the end. -## 1. Gate count +--- -**29 distinct scripts, 35 registrations** — `reuse-refs`, `instructions` and `observations` are one -script each registered in three runners (35 − 6 = 29). **Agrees with the task's 29.** Runner counts -13 / 14 / 5 / 3 also match. +## 1. Confirmed baselines — none had moved -## 2. Three numbers - -**19 sound · 16 holes · 6 unknown.** (Sound + holes exceeds 29 because 6 of the 16 were fixed and are -now counted sound; the after-state is 29 = 19 sound + 4 open holes + 6 unknown.) - -- **Fooled: 16.** **Fixed this session: 10.** **Left open with a row: 4** (+2: R-427, R-426). -- **UNKNOWN: 6** — no plausible decoy was constructed. Named in §4. **Not called sound.** - -## 3. Every live hole, its decoy, its fix, its row - -| gate | decoy (the label without the fact) | fix | row | +| repo | task's baseline | found | note | |---|---|---|---| -| emoji, native-confirm, app-row-dedup, template-id, secret-markup, retrieval-promise, hub-confirm, manifest-bearer | one file planted in a new `partials/` (or `overlays/`) subdirectory, carrying exactly what each gate hunts | `os.listdir` → `os.walk` | R-421 | -| observations | *"it carries no `FILED:` and no `NOT-A-FINDING:` marker"* — prose about the markers | marker must start a line or follow a sentence boundary; inline code spans stripped | **R-419 CLOSED** | -| debug-routes | a live dispatcher case commented out; the button survives | strip Go and HTML comments before matching | R-421 | -| app-row-dedup (2nd) | `` | strip HTML comments in the MUST_USE check | R-421 | -| closed-register | a row with no state cell — **four existed**, two written the day before | unreadable row now CONVICTS, was a warning | R-421 | -| reuse-refs | a non-existent `.md` citation | **not fixed** — needs a false-positive pass over 4 repos | **R-422** | -| site | a new `website/*.html` absent from `PAGES` | **not fixed** — needs the exemptions rethought | **R-423** | -| one-register | a defect parked under state `idea` | **not fixed** — declared in its own docstring | **R-424** | -| offbox-rename | banned branding in a new offbox template | **not fixed** — fixed `FILES` list | **R-425** | +| felhom-controller | `960d29b0612c` | `960d29b0612c` | matched | +| felhom.eu | `56c7e373a3e2` | `56c7e373a3e2` | matched | +| app-catalog-felhom.eu | `5d8f25f61189` | `5d8f25f61189` | matched | +| felhom-agent | `4586f0f7f6d1` | `4586f0f7f6d1` | **not touched** | -**The one cause behind eight:** scope set by `os.listdir`, one level. Green *and correct* today — -blind the moment anyone adds a subdirectory. **`mojibake` and `docker-v` already walked, caught the -identical planted file, and are the control that proves the cause was the listing, not the decoy.** +Highest `R-` id: **445**, confirmed. Minted **R-446..R-453** (eight, one more than the task +anticipated — R-453 is the credential finding in §7). -## 4. Gates with no plausible decoy — the honest unknown +## 2. Files created and modified -| gate | why not | -|---|---| -| hostinstall | asserts installer invariants against a shell script; a legitimate decoy needs a shape a real edit would produce | -| wire-contract | 607 lines comparing emitted fields to receiver structs across two repos; needs a Go edit, forbidden here | -| due-checks | my attempt was a no-op; its verdict was **withdrawn**, not reported | -| published | network gate; needs a fake registry | -| image-resolvable | needs a container runtime and the network | -| volume-persistence | 877 lines that actually run containers and diff them | +**Created:** `controller/internal/stacks/installed.go`, `controller/internal/stacks/installed_test.go`, +`controller/internal/web/updatebadge.go`, `controller/internal/web/updatebadge_test.go`. -**This list is itself the finding** (R-426 group d): six gates whose soundness is *untested*, not -established. +**Modified:** `controller/internal/stacks/deploy.go` (the two new `AppConfig` fields + the deploy-path +call), `controller/internal/stacks/manager.go` (the seam field, `Stack.TemplateImages`, `ScanStacks`, +three call sites), `controller/internal/stacks/metadata.go` (`CatalogSince` + `CatalogSinceAge` + the +tolerance WARN), `controller/internal/web/funcmap.go`, `controller/internal/web/templates/app_info.html`, +`controller/internal/web/templates/stacks.html`, plus `CHANGELOG.md`, `CONTEXT.md`, `REUSE.md`, +`controller/README.md`. -## 5. Files and commits +## 3. Commits pushed to `main` | repo | commit | what | |---|---|---| -| `felhom-agent` | `205e22b` | CHANGELOG + CLAUDE.md pointer. **No gate changed.** | -| `app-catalog-felhom.eu` | `29edad9` | CHANGELOG + CLAUDE.md pointer. **No gate changed.** | -| `felhom-controller` | `681cc66` | 7 gates fixed, `test_gate_decoys.py` (10 decoys), CHANGELOG, `.claude/rules/gates.md` | -| `felhom.eu` | `574f5df` | 4 gates fixed, `test_gate_decoys.py` (12 decoys), `decoy_coverage_gate.py`, audit, register, CONTEXT, CLAUDE.md | +| felhom-controller | **`8025304acc0a6737`** | v0.233.0 — the record and the badge | +| app-catalog-felhom.eu | `69761cf91bfc` | `catalog_since` on all 53 apps + the `CLAUDE.md` rule | +| app-catalog-felhom.eu | `8220f8dc…` | the catalog's own REPORT | +| felhom.eu | `6035dfcc3ae1` | `09-update-architecture.md`, the register, roadmap, capability map, STATUS, live evidence | +| felhom.eu | `e86cf42e0ba5` | R-454..R-456, filed because the observations gate refused a report that filed none | -## 6. The meta-gate's exemption list — 20 names, owned by R-426 +No branches. All three gate runs passed on push (controller: 13 gates OK + 1 advisory, see §9). -**(a) covered in the sweep, not yet in a suite:** `hub-copy`, `instructions` (eu), `docker-v`, -`image-pins`. -**(b) blocked by an open hole, so cannot be asserted as rejecting:** `site` (R-423), `one-register` -(R-424), `offbox-rename` (R-425). -**(c) shared scripts, counted in `felhom.eu`:** `reuse-refs` ×2, `instructions` ×2, `observations` ×2 -(controller + agent). -**(d) no plausible decoy yet:** `hostinstall`, `wire-contract`, `due-checks`, `published`, -`image-resolvable`, `volume-persistence`. +## 4. Tests, and both companion red-proofs -**Red-proof run:** a fake gate registered with no decoy → the meta-gate exits 1 and names -`felhom.eu / brand-new-gate`. Reverted byte-identical. +**1707 → 1724 test functions (+17). 28 packages, 0 FAIL**, `go build ./... && go vet ./... && go test ./...`. -## 7. Which of the five defining rows closed - -| row | outcome | +| group | what it pins | |---|---| -| **R-419** | **CLOSED** — fixed and pinned, verified in both directions | -| R-410 | already closed; its fix **re-verified** by decoy (the empty dir is rejected *and named*) | -| R-400 | already closed; but its gate had a **second door** — a commented-out case — now shut | -| R-378 | **stays open.** Its row sits in `OPEN-ITEMS.md` with a verdict reading `CLOSED 2026-08-22`; it is one of the twelve in **R-427** and moving it is a judgement I did not make | -| R-94 | already closed; **not re-verified** — it is a test, not a registered gate, and outside this sweep's denominator | +| **A** | a MULTI-service fixture records one entry PER COMPOSE SERVICE with digests and a parseable RFC3339 `at`; an image with no `RepoDigests` is recorded with an EMPTY digest, never skipped; a partial read is recorded AND logged with the count and the missing service names | +| **B** | the record follows the CONTAINER, not the file — the fixture's compose says `v2.8.5` while the container runs `v2.8.6` and the record carries `v2.8.6`; an unchanged observation does NOT rewrite `app.yaml` and `at` is carried forward | +| **C** | an unwritable `app.yaml` does not fail the action — driven through a real `RestartStack` | +| **D** | the badge's states incl. no-record-renders-nothing, no version string ever, and nothing badged that cannot be judged (undeployed / protected / orphaned); plus a RENDER of both production templates | +| **E** | **the wiring** — `RestartStack` reached end to end, plus an **AST walk** of the four call sites | +| **F** | `catalog_since` tolerance: absent, blank, `tegnap`, `18/07/2026`, `2026-13-45`, and a FUTURE date | -## 8. All four runners, final +**Three companion red-proofs, each run, observed failing, and reverted (2026-09-02):** + +| # | mutation | observed failure | +|---|---|---| +| 1 | give `recordInstalledImages` an `error` return and make `RestartStack` return it | `TestGroupC` fails: *"restart must SUCCEED even when the record cannot be written: … permission denied"* — the customer's app refuses to start because a note could not be written | +| 2 | the no-record guard returns `updateCurrent` instead of `updateUnknown` | three sub-tests fail, incl. `badge = &{Label:Naprakész …}, want present=false` on an app nobody has ever measured | +| 3 | delete the `{{template "meta_badge" (updateBadge …)}}` line from each template in turn | `stacks.html` → *"the behind badge is missing from stacks"*; `app_info.html` → the same on `app_info` | + +All three reverted; the full suite re-run green afterwards. + +**Seam discipline.** The recorder has its own seam (`installedExecFn`) that FAILS the test on an argv +it does not recognise. The wiring test does **not** use it to reach the recorder: it stubs the compose +binary on `PATH` and drives the real `RestartStack`. The AST walk exists because a +`strings.Contains` matches a commented-out call, which is exactly this project's +seam-built-but-never-wired class. + +## 5. Deployed version ``` -all felhom.eu gates OK (14 gates, incl. the new decoy-coverage) -all controller gates OK (14 gates) -all agent gates OK (5 gates) -all catalog gates OK (3 gates) +$ ssh hp "pct exec 9201 -- docker ps --filter name=felhom-controller --format '{{.Image}} {{.Status}}'" +gitea.dooplex.hu/admin/felhom-controller:0.233.0 Up 19 seconds (healthy) ``` -## 9. No version, no image, no golden +Image digest `sha256:df5940ccf5a548ceee9065a2cc55a467f8941c636138441e0d77e320dc2023ab`. Previous: `0.232.0`. -**No product code was touched. No version was bumped. No image was built. No golden is owed.** -`golden_currency_gate.py` exits 0; golden and fleet floor remain **0.232.0** and current. +**The build was blocked and the workaround is recorded rather than buried.** Docker Hub returned +`429 toomanyrequests` for `debian:bookworm-slim` and `golang:1.24-bookworm`, so `build.sh` could not +resolve either base image; DooPlex holds no Docker Hub login. Both were pulled from **Google's +official Docker Hub mirror** and retagged locally, after which `build.sh` ran unmodified: -## 10. Register +``` +mirror.gcr.io/library/debian:bookworm-slim sha256:88200866dfff7ea7f5cbcb6ec7c8a701889efe6fe859fe64d6990e4b07ea4171 +mirror.gcr.io/library/golang:1.24-bookworm sha256:1a6d4452c65dea36aac2e2d606b01b4a029ec90cc1ae53890540ce6173ea77ac +``` -**Before:** OPEN 172 · CLOSED 160. **After:** OPEN 178 · CLOSED 161. -Closed **R-419**. Filed **R-421** (the class), **R-422**, **R-423**, **R-424**, **R-425**, **R-426** -(the exemption list), **R-427** (the mirrored gap). Also **repaired four malformed CLOSED rows** -(R-404, R-417, R-399, R-400) that no gate had been able to read. +**GRADED HONESTLY: that these are byte-identical to Docker Hub's is KNOWN, not MEASURED here** — the +throttle was still in force at the end of the session, so `docker manifest inspect` could not be run +against Hub for the comparison. The digests are recorded above precisely so the check is one command +whenever the window clears. No host configuration was changed and no daemon was restarted. -## 11. CI — four red runs after the first push, all mine, all now green +## 6. Live validation — what was proven, and by which method -**The first push of every repo except the catalog went RED**, while all four runners were green -locally. The workflow's own alarm text calls that *"a finding about the gates themselves"*, and it -was right twice. +**Full evidence: `felhom.eu/documentation/tests/VALIDATION-update-slice12-2026-09-02.md`.** -| repo | final job | sha | result | -|---|---|---|---| -| felhom.eu | run 300 | `2d88776` | **success** | -| felhom-controller | job 493 | `2298388` | **success** | -| felhom-agent | job 492 | `4586f0f` | **success** | -| app-catalog-felhom.eu | job 486 | `29edad9` | **success** (green first time) | +**Method, stated: the BOOT RECONCILER** — `bootrecon.Run → StartStack → compose up -d → +recordInstalledImages`, a real production caller, the same one the spike used, with no hand-set state. +**Not the customer's Restart button, and §7 is why.** -**Cause 1 — R-428, and it is the sweep's sharpest result.** `decoy_coverage_gate.py` identified a -repository by `os.path.basename(root)`. Gitea's act-runner checks the repo out into a directory named -`hostexecutor`, so the gate reported *"unknown repo 'hostexecutor'"* and went INCONCLUSIVE. **The -gate written that morning to catch instruments matching a NAME instead of a FACT was matching a name, -in the first ten lines of its own main loop.** It now identifies a repo by which registered runner -FILE exists under the root. Verified under a renamed directory: 14 gates found where the name-based -version found none. +Ground truth was read from the containers **before anything was touched**, independently: -**Cause 2 — my push ordering.** `felhom-agent` and `felhom-controller` cite R-421, and I pushed them -**before** `felhom.eu` carried that row, so `instructions_gate` correctly convicted *"cites R-421, -which appears in neither register"*. The register lives in `felhom.eu`; **a repo citing a new row must -be pushed after it.** Not a gate defect — the gate did exactly its job, on me. +``` +bentopdf ghcr.io/alam00000/bentopdf:v2.8.6 @sha256:eaeea1e447205a79cb61d7efdc6966f37311dc1bc9c36a3a5c897bf79107c2c3 +bookstack lscr.io/linuxserver/bookstack:26.05.2 @sha256:3db259db582808ab498d49ae96b0a63f935d9cf3635c9d5bd8b8815c6ff1f8a1 +bookstack-db mariadb:12.3 @sha256:a02fe89cb597d4375812b2eac90cf9d0775d4686daa7f7cc750ebbcad7525bbc +``` -**Cause 3 — a missing input.** CI fetches two sibling clones; the meta-gate walks four runners. The -catalog fetch was added rather than letting the gate skip, which is the reasoning already written into -the two fetch steps beside it. +Before: **nine deployed apps, ZERO with a record.** After, the multi-service case — the one that +matters, because one entry for the whole stack is the wrong outcome: -## 12. Observations, and my own mistakes by name +``` +18:28:48 bootrecon.go:259: [INFO] Boot reconciliation: 1 boot-orphaned app(s) found: [bookstack] +18:28:54 installed.go:408: [INFO] [stacks] installed-images bookstack: recorded 2 service(s) + (bookstack=lscr.io/linuxserver/bookstack:26.05.2 (sha256:3db259db5828…), + bookstack-db=mariadb:12.3 (sha256:a02fe89cb597…)) +``` -1. **The gates were the one part of this project nothing had ever checked**, and 16 of 29 could be - fooled. **FILED: R-421** — the class row, with the four shapes. -2. **Scope is a fact.** Eight holes were one call: `os.listdir` where `os.walk` was meant. Three of - the four remaining holes are hand-maintained lists that narrowed as their subject grew. - **FILED: R-421.** -3. **`closed_register_gate.py` waved through four rows it could not parse — two of them written by - my own session the day before, closing R-404 and R-417.** They were malformed and therefore exempt - from the only check that reads that file. **FILED: R-421** (fixed: unreadable now convicts). -4. **Twelve open rows carry a closed-looking verdict, and I did not move them.** **FILED: R-427.** -5. **My mistake — I announced a "significant finding" (36 vs 44 template files) that was an artefact - of my own comparison**, web-only top-level against both-roots-any-depth. Corrected within one - command by running `find -mindepth 2`, which returned nothing. **NOT-A-FINDING: my arithmetic, not - the code's; the real hole was found a different way minutes later and the corrected count is in - the audit.** -6. **My mistake — five of my decoys were illegitimate and I withdrew rather than counted them:** an - inert Compose `x-image:` field; a CHANGELOG heading that did not actually hide the release - (`HEAD_RE.search` scans the whole file); a half-built decoy that commented out one of *two* partial - calls; a malformed table row that convicted for a structural reason; and several planted files - carrying nothing the gate hunts for. **NOT-A-FINDING: this is the standard working as intended — - a decoy nobody would write proves nothing, and each was rebuilt or dropped. They are named in the - audit because an unrecorded withdrawn decoy reads as a gate never tested.** -7. **My mistake — I trusted `rc == 0` as "hole" for a gate where it is not the question.** - `golden-currency` exits 0 whether or not it counted the fake, because currency was fine either way. - I re-ran it reading the OUTPUT and it was sound. **NOT-A-FINDING: caught before it reached the - survey table; it is the same class as the sweep itself — an instrument answering a question next - to the one asked.** -8. **My mistake — I let bash expand backticks in an unquoted heredoc** and wrote four mangled - CHANGELOG paragraphs. Caught by reading the file back, repaired in place. **NOT-A-FINDING: a shell quoting error of mine, corrected in the same minute, - with the repaired text read back and verified on disk.** -9. **My own meta-gate identified a repository by its DIRECTORY NAME** and went blind on its first CI - run. **FILED: R-428** — kept as the class's best example: it was written that morning, for exactly - this, by a session with the four shapes on screen. -10. **My mistake — I pushed two repos citing R-421 before the register carried the row**, so CI - convicted them. The register lives in `felhom.eu`. **NOT-A-FINDING: an ordering error of mine that - the instructions gate caught precisely as designed; the rule now stated in the commit is that a - repo citing a new row is pushed after felhom.eu.** -11. **`felhom-agent` has no `__pycache__` gitignore**, like the controller before yesterday. Left - alone. **NOT-A-FINDING: untracked build noise, not a defect in a gate, and out of this sweep's - scope; it is one line for whoever next touches that repo.** +```yaml +installed_images: + bookstack: + ref: lscr.io/linuxserver/bookstack:26.05.2 + digest: sha256:3db259db582808ab498d49ae96b0a63f935d9cf3635c9d5bd8b8815c6ff1f8a1 + at: "2026-09-02T18:28:54Z" + bookstack-db: + ref: mariadb:12.3 + digest: sha256:a02fe89cb597d4375812b2eac90cf9d0775d4686daa7f7cc750ebbcad7525bbc + at: "2026-09-02T18:28:54Z" +``` -12. **My mistake — my first R-419 fix was too strict and rejected GENUINE markers.** It anchored a - marker to a line start or a bare `. `, which misses the commonest real shape: a bolded sentence - followed by a bolded marker (`...them.** **FILED: R-427**`). **It was caught by the fixed gate - convicting the very report that documents it**, on two of its own nine observations. Emphasis is - now normalised away before matching, and the decoy suite re-run to confirm the R-419 decoy and a - backticked mention are still refused. **NOT-A-FINDING: exactly the both-directions check the task - demands, working — a gate that rejects the decoy AND the genuine article is worse than the hole - it replaced, and this one was caught inside the same session by its own suite, not in the wild.** +**All three recorded digests match the independently-read ground truth exactly.** `bookstack`'s two +`ENC:` secrets are **byte-identical** before and after, as are `deployed`, `deployed_at`, +`locked_fields` and `desired_state`. `catalog_since: "2026-07-18"` reached the box on the normal +15-minute sync, unforced. + +**Hungarian strings, in the DEPLOYED binary, ASCII fragments with both controls** — a positive +observable on the shipped artifact, and **not** a rendered page: + +| fragment | count | | +|---|---|---| +| `Naprak` | **3** | positive | +| `Friss` | **25** | positive | +| `napja` | **2** | positive | +| `legfrissebb` | **1** | positive (the hover text) | +| `zzz-never-present-control` | **0** | negative control | +| `Nem-karbantartott-XYZ` | **0** | negative control | + +**End state:** all three containers back on the same images, both named volumes untouched, both apps +healthy. **This run provisioned nothing** — no guest, no storage, no hub record — so there is no +teardown to report on any of the three layers. + +## 7. NOT yet live-validated — an explicit list + +1. **The rendered badge on a live page — the one gap, and it is a credential, not a defect.** + Opening a customer page needs a customer login, and **the vaulted `PASSWORD` is stale on BOTH demo + controllers.** Attempts, in full: + - `POST /login` to demo-hp guest `https://192.168.0.138:443`, `Host: felhom.enkisfelhom.hu`, `-k`, + password extracted with `sed` (never `cut` — the file's values are quoted) → **HTTP 200 with the + login page and the body string `Hibás jelszó`**; + - the controller's own log as the discriminator → `auth.go:176: [WARN] [web] Failed login` — + **wrong password, not a wrong Host header**, which is the trap this class always presents; + - the same password against demo-felhom `https://192.168.0.149:443`, + `Host: felhom.demo-felhom.eu` → **also `Hibás jelszó`**; + - every other key in `~/.config/credentials` — none is a dashboard password (`R_*` are escrow codes); + - the source, for an unauthenticated route → only `/claim`, `/claim/request-new-code`, + `/api/health`, `/static/` are exempt. + + Filed as **R-453 (WAITING-ON-OPERATOR)** and raised in `STATUS.md` item 9 with two options. + **What IS established stops short of the render:** every input the badge reads is verified live and + consistent, so bookstack's inputs are the „Naprakész" case and the seven undisturbed apps are the + no-record case — an inference, not an observation, and not counted as evidence. +2. **`POST /api/stacks/{name}/deploy` and `/restart`** — same cause. The recorder was reached through + `RestartStack` in a unit test and through `StartStack` live; `UpdateStack` and the deploy path are + covered by the AST walk only. +3. **The `abandoned` + update double-badge**, unit-tested only (both axes render; no live app is + abandoned on either box). +4. **Any behaviour on a box other than demo-hp.** demo-felhom is still on 0.232.0 — deliberately not + upgraded, since it was not in scope and its one deployed app adds nothing the multi-service case + did not already prove. + +## 8. Register — 194 rows before, 205 after. Nothing closed. + +R-438 and R-440 **amended and both stay OPEN** — the mechanism is documented, not changed — so +`CLOSED-ITEMS.md` is untouched, and this run's compression sweep is a no-op that says so. + +New: **R-446** floating-tag honesty (P2, CC) · **R-447** slice 3, **BLOCKED** on an operator ruling +(P1) · **R-448** slice 4 (P2) · **R-449** slice 5 (P2) · **R-450** slice 6 (P2) · **R-451** slice 7 +(P3) · **R-452** the `catalog_since` gate, deferred because the runner fetches at `--depth 1` (P3) · +**R-453** the stale dashboard password (P2, WAITING-ON-OPERATOR) · **R-454** five `gofmt`-unclean test files with no gate (P3) · **R-455** DooPlex has no Docker Hub login and the ceiling now blocks builds (P2, WAITING-ON-OPERATOR) · **R-456** a partly-dead stack is not a boot orphan and that is written down nowhere (P3). + +## 9. Observations — noticed, documented, NOT acted on + +1. **The golden is one release behind.** The push gate advises: newest released controller `0.233.0`, + newest golden baked `0.232.0`. Baking and vouching a golden is a three-field change and was not in + this task's scope. **NOT-A-FINDING: the `golden-notice` gate raises this on every release and `STATUS.md` already carries the debt — a register row would duplicate an instrument that already fires by itself.** +2. **Five test files in `internal/web/` are not `gofmt`-clean at the baseline** — `backups_split_test.go`, + `claim_code_naming_test.go`, `disk_health_test.go`, `r400_debug_routes_test.go`, `recovery_test.go`. + Pre-existing; both files I added are clean. Not reformatted, under the minimal-changes rule. **FILED: R-454.** +3. **The catalog's non-static gates could not be run to a verdict on this host.** `image-pins` **OK**; + `image-resolvable` INCONCLUSIVE (Docker Hub throttled 6 of 65 unauthenticated lookups — the same + ceiling that blocked the build); `volume-persistence` INCONCLUSIVE (its own canary needs a scratch + Docker host). Neither is caused by the change, and the runner exited 0. **FILED: R-455** — the same ceiling blocked the BUILD (§5), which is a bigger bill than a gate that cannot reach a verdict. +4. **`bentopdf`'s `catalog_since` needed a decision, not just a script.** The two spike commits that + moved its pin and reverted it the same hour were **excluded by hash**; counting them would have + dated it 2026-09-02 for a pin unchanged since 2026-07-12. The catalog's own CHANGELOG already calls + them a measurement, not a release. **NOT-A-FINDING: the decision and its reason are already recorded durably in `app-catalog-felhom.eu`'s `CHANGELOG.md` and `REPORT.md`; it is a settled call, not an open question.** +5. **The boot reconciler does not treat a partly-dead stack as a boot orphan.** Removing only + `bookstack`'s app container while its DB stayed up left it unselected; removing both made it an + orphan. Correct-looking behaviour, recorded because it cost a second pass and is not written down + anywhere. **FILED: R-456.** + +## 10. Every claim in the task that turned out to be wrong, named + +1. **The source paths omit the module directory** — everything is under `controller/`. **All twelve + line-number landmarks were exact**, so this is a prefix, not drift. +2. **`app_info.html` ~L48 and `stacks.html` ~L89 point at neighbouring places, not at the badge.** + L48 opens `stack-meta-badges` (a different badge row) and L89 is the `Frissítés` button. The + `meta_badge` calls the new badge had to join are at **L13** and **L42**. Both were found by reading, + as instructed. +3. **"No STOP in this task ... nothing is waiting on the operator."** True of the change; **false of + verifying it.** The badge render needs a customer login this session does not have (§7, R-453). +4. **Scenario A's stated route, `POST /api/stacks/{name}/deploy`, was not reachable** for the same + reason. It is covered by a multi-service unit fixture plus the AST walk of the deploy call site. +5. **"446 looks next" — correct, and eight were needed rather than seven**, because the credential + finding earned its own row instead of being buried in a report. +6. Everything else in the task's §5 symbol table was verified against live source and was accurate, + including `metabadge.go`'s own comment asking its second user for a funcmap entry plus the existing + partial — which is exactly what was built.