docs(report): lifecycle replaces retired/, live-proven on 9201

This commit is contained in:
2026-07-21 16:36:41 +02:00
parent a32541684a
commit 3067a94652
+47 -74
View File
@@ -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.