catalog: the lifecycle implementation itself (fixes the previous commit)

The previous commit landed only the new test/badge files: a 'git stash' used
to compare REUSE.md ref-check output silently dropped the staged index, so
every modification to an existing file was left behind and that commit does
not build. This adds the metadata field, the predicates, the fail-closed
deploy gate, the catalog filter, the funcmap entries, the template edits and
the docs that those tests exercise.
This commit is contained in:
2026-07-21 16:20:24 +02:00
parent ea0d3f1764
commit 5fdd2039fd
11 changed files with 213 additions and 5 deletions
+50
View File
@@ -1,5 +1,55 @@
## Changelog
### v0.158.0 — apps get a lifecycle: available / hidden / abandoned (2026-07-21)
No agent coupling; MinAgent unchanged.
Until now the catalog knew only two states: a template is present, or it is gone. "Gone" is not a
usable way to withdraw an app, because **it orphans every customer already running it** — their app
gets flagged `Elavult` and offered a Törlés button, for software that works fine. That is what the
short-lived `retired/` directory move (2026-07-21, same day) would have done, and it is why this
replaces it.
`.felhom.yml` gains an optional top-level `lifecycle:`:
- **`available`** — the default. Absent or empty means this, so all 52 existing templates are
unchanged.
- **`hidden`** — not offered for new installs. Nothing is shown to anyone already running it; "we
stopped offering this" is not their problem.
- **`abandoned`** — not offered for new installs, AND every box already running it carries a
permanent „Nem karbantartott" badge plus a notice on the app page: *„Az alkalmazás fejlesztője
felhagyott a fejlesztéssel. A telepített verzió továbbra is használható, de frissítések és
biztonsági javítások már nem érkeznek hozzá."*
**A deployed instance keeps full function in every state.** Lifecycle governs what is OFFERED, never
what runs.
- **The deploy gate is server-side and fail-closed** (`api.deployStack`, before any mutation), with
the ruled Hungarian refusal „Ez az alkalmazás jelenleg nem telepíthető." Hiding a button is not a
gate — a stale link, a bookmarked deploy form or a direct POST must all be refused. A second check
in `stacks.DeployStack` covers any future caller that does not route through the API.
- **The unknown-value posture is fail-OPEN, deliberately, and it is the opposite of the gate's.** An
unrecognised value degrades to `available` with one WARN. A typo — or a state added in a later
catalog than this controller understands — must never silently pull a working app out of every
customer's catalog. The gate that actually protects installation reads the same
`EffectiveLifecycle`, so the two can never disagree.
- **Orphan detection is untouched, and that is asserted.** Withdrawn templates stay in the catalog
tree; `getCatalogTemplateSlugs` never looks at lifecycle. A red-proof adds that filter and shows
the abandoned app immediately reading as an orphan.
- **Badge plumbing is generic**: `MetaBadge` + the `meta_badge` partial + a `lifecycleBadge` funcmap
entry. R-56's difficulty labels are meant to be a sibling funcmap function returning the same type
— no new markup, no new CSS.
- **plant-it returns to `templates/`** as the first `abandoned` app, so the mechanism is proven on
the case that motivated it. Its compose is deliberately left as-is: the app is not installable, and
rewriting it would imply it is.
**Red-proofs, all four run:** removing the API gate → the wiring test reports the gate INERT;
dropping the `Deployed ||` clause from the catalog filter → a customer's running app vanishes from
their own Alkalmazások page; removing the badge line → the abandoned app renders unmarked; making
orphan detection lifecycle-aware → `catalog set = map[bookstack:true]`, the two withdrawn apps read
as orphans. The wiring test walks the AST, not `strings.Contains`, because a commented-out call
still contains the string; it also asserts the gate precedes `DeployStack`.
### v0.157.1 — anchor the `controller` .gitignore entry (2026-07-21)
Tooling only; no behaviour change, no rebuild needed.