app guides night: the spec saved as the record (it existed only in the operator's message)
gates / gates (push) Successful in 6m51s

Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
2026-10-15 22:00:30 +02:00
parent 314308b4aa
commit a3bf208242
@@ -0,0 +1,400 @@
# Night task: app user guides — gate, media pilot, then every other app
> Saved verbatim from the operator's message of 2026-10-15 ~21:50 CEST. The spec (the APPENDIX) existed
> nowhere on disk; this file is the record. The brief above the appendix wins where the two differ.
## For the operator (plain language)
Tonight Claude Code writes a user guide and an FAQ for every app in the catalog, in Hungarian and English.
It starts with the five media apps, then does the other 60. Each guide is checked against the app's own
source code at the exact version we ship. A guide that fails the check stays hidden. It only adds text to
the catalog. Old boxes ignore the text, and boxes on 0.315.0 or newer show it on the „Útmutató" tab.
In the morning: read the Plex and the Sonarr guide on demo-hp. If you want the format changed, it is one
pass over all files.
---
## What to run
Run **the full spec in the APPENDIX at the end of this message** (Part 0 → A → C → B → N). It is the spec.
It is not on disk anywhere — this message is the only copy. Save it as
`felhom.eu/documentation/audits/app-guides-2026-10-15/TASK.md` in your first docs commit, so the record exists.
The part above the appendix states what changed since the spec was written and the night rules. Where they
differ, the part above wins.
**The pilot has not run yet.** At 21:45 CEST the catalog `main` is still `e67aad49`: no gate change, no guide.
## 1. Baselines (re-read at start; do not trust)
| Repo | `main` @ | Note |
|---|---|---|
| app-catalog-felhom.eu | `e67aad49` | the repo this task changes |
| felhom-controller | `e7b64af1` (v0.317.0) | read only. Guide schema as built: `internal/stacks/metadata.go` `AppGuide` (~L171–L200: `checked_version`, `sections[].title/intro/steps`, `faq[].q/a`), overlay in `metadata_i18n.go` (~L73–L104). It matches the spec's Part 5. |
| felhom.eu | `371b72da` | docs, register, STATUS only |
demo-hp guest 9201 runs controller **0.317.0** — it renders guides.
## 2. Night rules
1. **Unattended.** Ask nothing. Take the most reasonable reading, write it at the top of the report, go on.
2. **demo-hp is shared tonight** (other sessions may be building or testing on it). For the pilot's box
check (Part A.3): use only media apps **already running** on 9201, **read only** — API reads and page
fetches, no install, no remove, no restart, no setting change, no Plex claim. An app that is not running
there gets the label check only; set `checked_version` by the spec's §8 label-check row and list it in
the report as „box check owed". Tester 1, N100, ep0, DooPlex: not touched (DooPlex runs your git and
reads only).
3. **The rendering check** goes through the public address: `https://felhom.enkisfelhom.hu/apps/sonarr/guide`
(and plex) after the catalog sync reaches 9201. Log in the way the earlier sessions did (password stored
out-of-band, never printed). Assert the `data-guide-section` count; save a screenshot if a browser is at
hand. If the sync has not arrived by the end, say so — do not force it.
4. **Push in batches** of about 10 apps, gates green before each push. Before every push in any repo:
`git pull --rebase`; other sessions push tonight. Never force-push. Never `--no-verify`. No branches.
5. **Time.** If the night runs out, stop at a pushed batch boundary. The report lists the apps done, hidden
(and why), and not started. Never leave a half-written template on `main`.
6. **One format.** Keep every guide in the same shape (section count range, step style, FAQ length) so a
morning format change is mechanical.
7. **Part C (`pi_compatible` removal):** do it only if the hub's controller floor is ≥ `0.315.0`. If not,
do nothing — **R-1067 already holds it**; do not open a second row.
8. Content rules from the spec stand, especially: no indexer, tracker or download-site names; nothing the
box already wires (`15-media-library.md` §6, §11); labels exactly as the app shows them at the pinned tag.
## 3. One register item from the reviewer (felhom.eu)
The 0.316.3 controller image contains another session's unreleased work (felhom-controller `CHANGELOG.md`,
v0.316.4 entry: „Do not deploy `0.316.3`"). That fact lives **only in the CHANGELOG**; the session said it
was filed with R-1081/R-1082, but neither row mentions it (checked 21:45 CEST). Open one row:
Category „Process & tooling", P3, owner CC — „Registry tag `felhom-controller:0.316.3` holds another
session's uncommitted code; it must never be offered to a box. Today the update button offers the highest
tag (0.317.0), so nothing reaches it — but any path that picks a lower or specific tag could." Name the paths
that choose a version (`09-update-architecture.md`) and say which, if any, could select 0.316.3. Do not
change code tonight.
## 4. Report (catalog `REPORT.md`, overwritten; plain words first)
1. What a household sees now: how many apps have a visible guide, how many hidden, how many not done.
2. The five pilot apps: label check and box check result each.
3. Gate change: tests and red-proofs.
4. Rows opened and closed (with the 0.316.3 row). Register size as four numbers.
5. Readings taken without asking (rule 1).
6. Observations: each `FILED: R-NNN` or `NOT-A-FINDING: <reason>`.
---
---
# APPENDIX — the full spec
## Spec: App user guides — media pilot (5 apps), then the night batch (60 apps)
---
## For the operator — what this does, in one page (plain language)
**What is missing today.** A household that does not know an app has nowhere to look on the box. They
ask us. Each such question costs your time and does not scale.
**What you get.** Every app gets a short user guide and an FAQ, in Hungarian and English. It shows on
the new "Útmutató" tab of the app page (built by the companion task). The text lives in the app
catalog, next to the app's other text, so it reaches every box with the normal catalog sync.
**In what order.**
1. **Pilot, five apps — the media stack:** Plex, Sonarr, Radarr, Prowlarr, qBittorrent. Each guide is
checked twice: against the app's own source code at the exact version we ship, and on a real box.
2. **Night batch, the other 60 apps.** Same format. Each is checked against the app's source code. A
guide that fails the check stays hidden. Hidden means the box does not show it until a later
session fixes it.
**Two rules you should know.**
- A guide shows on a box only when it records which app version it was checked against. When we
update the app later, the catalog warns that the guide may be stale.
- The media guides never name a download source, a tracker or an indexer site. Choosing one stays the
household's own choice (this is already the rule in `15-media-library.md`). The examples use free,
legal content.
**What it costs you.** Tomorrow, read the Plex and the Sonarr guide on demo-hp. If the format is
wrong, we change it in all 65 files in one pass.
**Nothing here touches customer data or a running box's apps, except throwaway installs on demo-hp.**
---
## 0. Task class & scope
- [x] **Implementation** — catalog content + one catalog gate change; checked live on demo-hp.
**Repos touched:**
- [x] `app-catalog-felhom.eu` — `templates/<app>/.felhom.yml` (guide blocks), `scripts/check-copy-i18n.py`,
one advisory gate, CHANGELOG/REPORT/CONTEXT.
- [x] `felhom.eu` — `documentation/` (register rows, STATUS line, the onboarding checklist row).
- [ ] `felhom-controller` — NOT touched. It renders the guide from v0.315.0
(the controller task of 2026-10-15; see §4 item 4). Older controllers ignore the unknown key
(`10-localisation.md` §7: the controller builds no strict YAML decoder), so the content may land
before the controller ships.
---
## 1. Confirmed baselines (verified against live Gitea, 2026-10-15 ~17:00 CEST)
| Repo | `main` @ commit | Version |
|------|-----------------|---------|
| app-catalog-felhom.eu | `e67aad4` | dated CHANGELOG (2026-10-15 evening, R-1058) |
| felhom-controller | `85f65c7` | `v0.314.0` (read only) |
| felhom.eu | `3263bb2` | docs |
Pinned images at baseline (re-read before writing — they are what `checked_version` records):
`plexinc/pms-docker:1.41.4.9463-630c9f557`, `lscr.io/linuxserver/sonarr:4.0.20`,
`lscr.io/linuxserver/radarr:6.4.4`, `lscr.io/linuxserver/prowlarr:version-2.6.5.5623`,
`qbittorrentofficial/qbittorrent-nox:5.2.4-1`.
**An unpushed change does not exist.** Commit directly to `main`. No branches. No `--no-verify`.
---
## 2. Overview
Add an `app_info.guide` block (schema below, identical to what the controller parses, §4 item 4) with an
`i18n.en` overlay to each template. Extend the catalog's copy gate so the guide is checked like all
other copy. Add one advisory gate that warns when a template's image tag differs from its guide's
`checked_version`. Write and check the five media guides (Part A), then the remaining 60 (Part B).
Remove `pi_compatible` from the catalog only if the floor allows it (Part C).
---
## 3. Spike-first gate
No unproven mechanism. The YAML copy model is measured (`10` §7). The one open question is
*how to check a guide without a browser* (none on DooPlex) — answered by method in §8, not by a spike.
---
## 4. Read these files first
1. `app-catalog-felhom.eu/CLAUDE.md`, `CONTEXT.md`, `NEW-APP-CHECKLIST.md` — conventions, the
pre-push gates (`catalog_gates.py --fast`), the gate decoys in `scripts/test_gate_decoys.py`.
2. `felhom.eu/documentation/architecture/10-localisation.md` §7 — the copy model and the copy gate
(`check-copy-i18n.py`, the `EN_MISSING_CEILING` ratchet).
3. `felhom.eu/documentation/architecture/15-media-library.md` §6 and §11 — **what the box wires by
itself** (qBittorrent categories, Sonarr/Radarr tag, root folder, download client, minimum free
space, Prowlarr → Sonarr/Radarr, Plex folders and scan). A guide never tells the household to do
what the box already did; it tells them it is done. Also: „Indexers stay the household's choice."
4. The schema and render rules AS BUILT in the controller (the earlier task file is not on disk): read
`felhom-controller` `internal/stacks/metadata.go` (`AppGuide`, `GuideSection`, `GuideFAQ`),
`metadata_i18n.go` (the overlay), `appinfo_address.go` (`DOMAIN` filled), and the guide tab template
(`templates/app_guide.html`). Rendered only when `checked_version` is set; plain text, no markdown, no HTML.
5. Source: `scripts/check-copy-i18n.py` (~L153–L160 base fields, ~L213–L230 overlay keys and the
"unknown key app_info.%s" refusal), `scripts/catalog_gates.py` (the gate table),
`templates/{plex,sonarr,radarr,prowlarr,qbittorrent}/.felhom.yml` (their current `first_steps`,
`prerequisites`, `after_start`).
---
## 5. Existing functions / helpers to reuse
| Symbol | Where | Use | Modify? |
|---|---|---|---|
| base-copy collector (`add("app_info.…")`) | `scripts/check-copy-i18n.py` | count guide strings as copy | Yes — Part 0 |
| overlay key whitelist (`if k not in ("tagline", …)`) | same | accept `guide` in `i18n.en.app_info` | Yes — Part 0 |
| `catalog_gates.py` gate table | `scripts/` | register the advisory guide-version gate (WARN only) | Yes — Part 0 |
| `scripts/test_gate_decoys.py` | `scripts/` | a decoy per new gate rule | Yes — Part 0 |
**Do not reuse** `ladder.py` / `upgrade-test.py` style box drivers to install apps on Tester 1 or any
customer box — the pilot check runs on demo-hp only.
---
## 6. Pattern references
Existing `first_steps` / `i18n.en.app_info.first_steps` pairs in `templates/prowlarr/.felhom.yml` —
tone, tegező form („Nyisd meg…"), the `DOMAIN` placeholder.
---
## 7. Integration scenarios (must pass)
### Scenario A — the gate accepts a correct guide
Given `templates/sonarr/.felhom.yml` with a full HU guide and a full EN overlay,
When `catalog_gates.py --fast` runs,
Then it passes and the copy count includes every guide string (sections' title/intro/steps, FAQ q/a).
### Scenario B — the gate refuses a broken guide (wrong case)
Given an EN overlay with `guide.checked_version`, or a section with an empty `steps`, or an FAQ entry
with `q` but no `a`, or an overlay with more sections than Hungarian,
When the gate runs,
Then it fails naming the template and the exact key. `checked_version` is not copy and may not be overlaid.
### Scenario C — the version warning
Given Sonarr's guide says `checked_version: "4.0.20"` and the image moves to `4.0.21`,
When the gate runs,
Then it prints a WARN naming the template and both versions and **does not fail**.
### Scenario D — a media guide does what it says
Given Sonarr installed on demo-hp from the live catalog, with the media links the box makes,
When each step of the Sonarr guide is followed through Sonarr's own API / served pages,
Then every menu name in the guide exists at the pinned version, every "the box already did this"
claim is true on the box, and no step is needed that the guide leaves out.
### Scenario E — no source names (wrong case)
Given any of the five media guides,
When it is read,
Then it names no indexer, tracker or download site, in either language. A test enforces a deny-list of
the obvious names; the human rule is wider than the list.
---
## 8. Rules for writing and checking a guide
**Content.**
- Hungarian base, English overlay complete for every string (no `EN_MISSING_CEILING` increase).
- 3–6 sections, each a task the household wants ("Első film hozzáadása", "Telefonos app beállítása",
"Családtag hozzáadása"). One action per step. Short words. Adult tone, tegező form, no emoji.
- 4–8 FAQ entries: the questions a household will really ask (from the upstream FAQ/wiki/issues and
common forum questions). Each answer 1–3 sentences.
- Do not repeat `first_steps` (the tab shows them first). Do not describe what the box did except to
say it is done. Point at Felhom controls by their tab name where they apply (Plex code →
„Beállítások fül"). Use `DOMAIN` for the household's address (`<label>.DOMAIN`).
- Menu and button names: write the label **exactly as the app shows it on a fresh install with the
default UI language**, in „…". If the default is English, write the English label and the
Hungarian meaning after it once: „Settings" (Beállítások).
- Never name an indexer, tracker, or download source. Examples use legal content (public-domain films,
Linux images, the household's own discs).
**Checking (`checked_version` is set only when ALL hold).**
1. Every quoted label is found in the app's source or translation files **at the pinned tag**
(upstream git at that tag). Record the file and line per label in the evidence file.
2. Pilot only (Part A): the app is installed on demo-hp from the live catalog and each step is
exercised through the app's API or served pages; every "the box did it" claim is read back from
the app. No browser exists on DooPlex — say so; the operator's visual read is the last check.
3. `checked_version` = the image tag in `docker-compose.yml` at the commit that adds the guide.
| Label check | Box check (pilot) | Result |
|---|---|---|
| all found | all pass | `checked_version` set |
| all found | not run (Part B) | `checked_version` set |
| any missing | any | `checked_version` empty (hidden); listed in REPORT |
| any | any fails (pilot) | fix the guide; if not fixable now, empty + a row |
---
## 9. Strict rules
1. **Green gate (catalog):** `python3 scripts/catalog_gates.py --fast` and
`python3 -m pytest scripts/` green after every commit. No `--no-verify`.
2. **Minimal changes:** only the guide blocks, the gate change, Part C if allowed. The size rule applies.
3. **No silent failures.**
4. **Secrets safety:** no token, API key or password in any guide or evidence file; keys not values.
5. **Line numbers are landmarks.**
6. **Trunk-based.** Revert + report if a commit cannot ship.
7. **Version discipline:** one dated CHANGELOG entry per push batch, REPORT overwritten.
8. **Coding standards:** `app-catalog-felhom.eu/CLAUDE.md`.
9. **Instruction files are kept true by the session that finds them wrong** — facts only.
10. **Delegation (Part B):** subagents may DRAFT guides in parallel, each into a scratch file outside
the repo. One writer applies them to the templates and runs the gate. No two agents edit the same file.
11. **Document length:** as short as the content allows.
---
## 10. Non-hollow test discipline
- Gate tests assert the exact failing key in the message, not just a non-zero exit.
- Red-proof each new gate rule: remove the rule, see its decoy pass, restore.
- The deny-list test (Scenario E) red-proof: insert one listed name into a fixture, see it fail.
---
## Part 0: The gate (first commit, before any guide)
0.1 `check-copy-i18n.py`: count every guide string as copy (base and overlay); accept `guide` in the
overlay's `app_info`; refuse `guide.checked_version` in an overlay; refuse empty `steps`, an FAQ entry
missing `q` or `a`, and an overlay list longer than the Hungarian one.
0.2 An advisory gate in `catalog_gates.py`: guide `checked_version` ≠ image tag → WARN, never fail.
0.3 Decoys in `test_gate_decoys.py` for each rule; the deny-list test for media guides.
## Part A: The pilot — Plex, Sonarr, Radarr, Prowlarr, qBittorrent
A.1 Read each app's upstream docs and source at the pinned tag; write the guide (HU + EN).
A.2 Label check (§8 rule 1). Evidence: `felhom.eu/documentation/audits/app-guides-2026-10-15/A/<app>-labels.txt`.
A.3 Box check on demo-hp (`ssh hp`): install the five from the live catalog as throwaway apps unless
they already run there as the standing media test set (then use them read-only), follow each guide
through the API, read back the box's wiring. Evidence: `…/A/<app>-box.txt`. Do NOT claim a real Plex
server, and do not touch Tester 1.
A.4 Set `checked_version`, push.
## Part B: The night batch — the other 60 apps
B.1 Same rules, label check only. Batches of about 10 apps per commit; the gate green before each push.
B.2 Any app whose labels do not all check: guide committed with `checked_version: ""` and listed.
B.3 Evidence: `…/B/<app>-labels.txt`.
B.4 Order: the apps most households install first (Immich, Nextcloud, Vaultwarden, Paperless-ngx,
Jellyfin, Home Assistant), then alphabetical.
## Part C: Remove `pi_compatible` from the catalog — ONLY if the floor allows
Old controllers (≤ v0.314.0) show „csak x86" on every app whose template has no `pi_compatible: true`.
So: if the hub's controller floor is ≥ `0.315.0` when this part starts, remove the key from all 65
templates and update `scripts/onboarding_gaps.py` (L109, L209) and the decoy fixture in
`scripts/test_gate_decoys.py` (~L980). If the floor is lower, do nothing and open a register row
"Catalog still carries `pi_compatible`; remove after floor ≥ 0.315.0" (P4, Apps & catalog, owner CC).
## Part N: Documentation
- Catalog `CHANGELOG.md`, `REPORT.md` (overwrite), `CONTEXT.md` (the guide rules in §8, briefly).
- `NEW-APP-CHECKLIST.md`: add one row — a new app ships with a checked guide.
- `felhom.eu/documentation/backlog/OPEN-ITEMS.md`: a row per app left hidden (or one row listing them),
the Part C row if not done; close nothing that is not done. Register size as four numbers.
- `felhom.eu/STATUS.md`, "waiting on the operator": „Olvasd el a Plex és a Sonarr útmutatót a demo-hp-n."
---
## 11. Tests
- **A/B** gate tests per rule in `test_gate_decoys.py` (pass case + each refusal, exact key in message).
- **C** advisory gate: a version mismatch fixture prints WARN and exits 0.
- **E** deny-list test over the five media templates, both languages.
---
## 12. What NOT to do
- Do NOT name an indexer, tracker or download site, or explain how to find one.
- Do NOT write a step for something the box already wires (§4 item 3).
- Do NOT set `checked_version` on a guide whose labels were not all found.
- Do NOT put markdown, HTML or links other than the upstream docs URL in guide text — the controller
renders plain text.
- Do NOT move any image tag, change any compose file, or touch `first_steps` beyond fixing a factual
error (size rule; list it).
- Do NOT install anything on Tester 1, ep0 or DooPlex. demo-hp only.
- Do NOT create a branch. Do NOT add "Co-Authored by..." anywhere.
---
## 13. Build / deploy / STOP
Clean-tree gate first. The catalog has no build: a push to `main` reaches the boxes through the
catalog sync. Verify after push: `ssh hp` → the box's synced template carries the guide
(`grep -c checked_version` on the synced `.felhom.yml`, ASCII only). If demo-hp runs controller
≥ 0.315.0, `curl` `/apps/sonarr/guide` and assert the `data-guide-section` marker count.
Teardown: every throwaway app installed in Part A removed; `docker ps` before/after in the evidence.
No hub record is created; say so.
---
## 14. Implementation order
```
Phase 1: Part 0 gate + decoys → catalog_gates --fast + pytest green, push
Phase 2: Part A pilot (5) → green, demo-hp checks, push
Phase 3: Part C (only if floor ≥ 0.315.0, else the row)
Phase 4: Part B night batch (60) → green per batch, push per batch
Phase 5: docs, register, STATUS, push
```
---
## 15. Final verification & deliverables
REPORT.md must include: 1. Baselines. 2. Files changed. 3. Commit hashes. 4. Gate test results and
each red-proof. 5. Per app: `checked_version` set or hidden, with the evidence path. 6. demo-hp checks
per pilot app (pass / what was fixed). 7. **NOT yet validated:** the operator's read of the Plex and
Sonarr guides; the rendering on a ≥ 0.315.0 box if none ran it. 8. Teardown evidence. 9. Part C done or
its row number. 10. **Observations:** each with `FILED: R-NNN` or `NOT-A-FINDING: <reason>`.