diff --git a/REPORT.md b/REPORT.md index 970d6eb..f45b33b 100644 --- a/REPORT.md +++ b/REPORT.md @@ -1,87 +1,60 @@ -# REPORT — catalog honesty pass (TASK-E Part 3), 2026-07-21 +# REPORT — app lifecycle replaces the `retired/` move (TASK-F Part 1, catalog half), 2026-07-21 -## What ran +Baseline `857ba53`, clean tree. -Diagnose-and-re-pin for the two Campaign 7 apps whose images "do not resolve at all", plus R-41 -slice 1 (a standing resolvability gate). Baseline `34d50a3`, clean tree. +## What changed and why -## wanderer — RE-PINNED (upstream is alive; the template pointed at a ghost) +The `retired/` directory mechanism shipped earlier the same day is **withdrawn**. It un-offers an app +correctly, but the controller discovers catalog templates by directory — so removing one makes the +orphan detector see the template as GONE for anyone already running the app, flagging their working +install `Elavult` and offering a Törlés button. Withdrawing an app must never take a working app away +from a customer. -| | | +Replaced by an optional top-level `lifecycle:` in `.felhom.yml` (controller v0.158.0/.1): +`available` (default, absent/empty ≡ this) / `hidden` / `abandoned`. + +- **`plant-it` returns to `templates/`** with `lifecycle: abandoned` — the first user of the + mechanism and the case that motivated it. `retired/` is removed. Catalog: **53 apps** (52 offered). +- Its compose is deliberately **unchanged**. It pins `msdeluise/plant-it:0.10.0`, a repository that + does not exist (the real one is `-server`), and the app is not installable — rewriting it would + imply it is. The evidence for abandonment is recorded in the template's own header comment. + +## Live-proven on 9201 + +Through the normal git-sync and the real endpoints, not a hand-copy: + +| check | result | |---|---| -| Old pin | `ghcr.io/flomp/wanderer:0.16.0` — **does not resolve** (verified `docker manifest inspect`) | -| Failure class | **repo split + registry move + org rename**, all at once | -| New pins | `flomp/wanderer-web:v0.20.0`, `flomp/wanderer-db:v0.20.0`, `getmeili/meilisearch:v1.36.0` | -| Evidence | upstream `docker-compose.yml` at tag `v0.20.0` (github.com/open-wanderer/wanderer); latest release 2026-07-07; all three tags confirmed present | +| `lifecycle: abandoned` present in `/opt/docker/stacks/plant-it/.felhom.yml` | yes | +| plant-it on the Alkalmazások page | **0 occurrences** (control `bookstack`: 10) | +| direct `POST /api/stacks/plant-it/deploy` | **HTTP 409** „Ez az alkalmazás jelenleg nem telepíthető." | +| `/apps/plant-it` | 200, permanent abandoned notice, no Telepítés button | -Compose restructured from 1 service to 3 (web + PocketBase + Meilisearch), per upstream. Notable -consequences, none of them cosmetic: +## The resolvability gate is now lifecycle-aware -- **A second public hostname was unavoidable.** `PUBLIC_POCKETBASE_URL` is browser-side, so - PocketBase must be publicly reachable; added `SUBDOMAIN_DB` (default `hike-db`). Upstream's own - proxy example does the same. -- **New required volume** `/data/plugins` — v0.20.0 moved the Strava/Komoot/Hammerhead integrations - into a WASM plugin sandbox. -- **New generated secret** `POCKETBASE_ENCRYPTION_KEY` (`hex:16` → exactly 32 chars, as required). -- **Meilisearch pinned DOWN v1.49 → v1.36.0**, applying the R-42 ruling (sidecar follows the app - template's proposed pin). Campaign 7 had bumped it while the app image was dead. -- `mem_limit` 384M → 1024M = the exact sum of the three services (REUSE.md §2 rule). -- Probe container name kept equal to the stack dir (`wanderer`), or the health probe would hit the DB. +An abandoned app's dead image is the **expected end state**, not a finding. Left unhandled, plant-it +would have made the gate permanently exit 1 — and a gate that is always red is a gate nobody reads, +which is the same lesson as yesterday's throttle false-alarm. -**Not deployed live.** This is a from-scratch 3-service restructure of an app that has never once -deployed successfully; it wants a real deploy on the demo box before anyone is invited to it. +- Non-available apps are **skipped by default and REPORTED**, never silently dropped. +- `--all` includes them again. +- An all-skipped run is a **pass (0)**, not the "no images found" error — that would have been a + false alarm of its own. -## plant-it — RETIRED (operator ruling 2026-07-21) +6 new fixture tests (**19 total**, no network), including the `lifecycle:` line-parse table: absent, +quoted, unknown (→ available), and an indented `lifecycle:` that must NOT match because it is not a +top-level key. -The pin was only slightly wrong — repository is `msdeluise/plant-it-server`, and `0.10.0` was the -right version. Correcting the name would have been the wrong fix: +## Gates -- Upstream **discontinued self-hosting**: `backend/` and `deployment/` are deleted from `main`; the - project is now an Android app on F-Droid/Obtainium, maintainer notes development has slowed. -- Last server image pushed **2024-12-10** — a security-frozen Spring Boot 3.4.0. -- Requires **MySQL 8.0 + Redis**. The template claimed "Database: None (file-based)" — never true. +`check-image-pins.py` → OK, 53 templates, 0 unpinned. `test_check_image_resolvable.py` → 19/19. -Moved to `retired/plant-it/` rather than deleted: the controller discovers apps by directory, so this -un-offers it while keeping it revivable with one `git mv`. Catalog is now **52 apps**. +## Remaining -## R-41 slice 1 — `scripts/check-image-resolvable.py` - -Complements the syntactic pin gate, which cannot see rot. Resolves each unique pin one image at a -time; exit 0 / 1 (GONE) / 2 (inconclusive). 14 fixture tests, no network. - -**Two traps encoded, both hit live during this change:** - -1. `docker manifest inspect` prints `toomanyrequests: …` and **still exits 0** — the same - exits-0-on-failure shape as the ISO tooling's `validate-answer`. stderr is therefore checked - even on rc=0. -2. The inverse, and the more dangerous one: the **first full sweep called 24 of 65 pins dead**, - including `postgres:16-alpine` and `redis:7-alpine`, purely because Docker Hub throttled it - partway through. Ambiguity now resolves to INCONCLUSIVE, never to an accusation. - -### Sweep result — INCOMPLETE, and that is the honest answer - -The full 65-pin sweep **could not be completed**: DooPlex is not logged in to Docker Hub, and the -anonymous limit was exhausted partway. The pre-throttle probes are trustworthy and are what the -findings above rest on: - -| Image | Result | When | -|---|---|---| -| `ghcr.io/flomp/wanderer:0.16.0` | **GONE** | pre-throttle | -| `msdeluise/plant-it:0.10.0` | **GONE** | pre-throttle | -| `getmeili/meilisearch:v1.49` | resolves | pre-throttle | -| `flomp/wanderer-web:latest`, `flomp/wanderer-db:latest` | resolve | pre-throttle | -| `msdeluise/plant-it-backend/-frontend:latest` | resolve | pre-throttle | -| `flomp/wanderer-{web,db}:v0.20.0`, `getmeili/meilisearch:v1.36.0`, `msdeluise/plant-it-server:0.10.0` | exist (Hub tag API, HTTP 200) | post-throttle cross-check | - -**Follow-up for the operator:** re-run `python3 scripts/check-image-resolvable.py` after -`docker login` to get the complete 52-app table. The gate now reports the throttle as INCONCLUSIVE -(exit 2) instead of inventing 24 dead images, so a repeat is safe to trust. - -## Observations (recorded, not acted on) - -- `REUSE.md` cited `felhom-controller/internal/stacks/healthprobe.go`; the real path has an extra - `controller/` segment. Corrected, but `reuse_refs_check.py` still flags it — it resolves citations - against the repo root and cannot follow a cross-repo pointer. Pre-existing checker limitation. -- The `-app`-suffixed wanderer tags (`v0.20.0-app`) are undocumented upstream; deliberately not used. -- 6 apps' `.felhom.yml` still carry `mem_limit` values that do not equal the sum of their services' - limits. wanderer is now correct; the others were out of scope. +- **The full resolvability table is still owed** — Part 4 was skipped by operator ruling (no Docker + Hub login on DooPlex), so the sweep would still hit the anonymous throttle and exit 2. One + `docker login` + `python3 scripts/check-image-resolvable.py` closes it. +- **`hidden` has no user yet.** Implemented and tested, but only `abandoned` is exercised end to end. +- **wanderer is still undeployed** (from the previous session): re-pinned to a 3-service upstream + shape with a second public hostname, and never once deployed. It wants a real deploy on the demo + box before anyone is invited to it.