PROMPT-TEMPLATE: standard 'For the operator' plain-language section, mandatory for M+ tasks and anything with a STOP. ROADMAP rulings (operator, 2026-07-21): R-25b full-teardown cascade with three acks + typed name (M-sized, spec to follow, no longer blocks R-3); R-11 channel = direct Messenger, doc is the architect's; R-42 option (a), sidecars follow the app; R-17 delete the archive - spike-lite found NO tooling verb targets it, so it is an operator console action; R-4 complete (freemail.hu verified). R-55 + R-41 slice 1 marked shipped; new R-56 (app difficulty classification - the constructive half of the glance ruling). scripts/build-hub.sh v1.23.0: the hub build script was outside any repo. Adopted verbatim + versioned; the build-dir path is now a symlink to it. felhom-testing skill: the ~1/5 recovery-code 'known flake' is retired - it was a real defect the test was correctly detecting.
24 KiB
Claude Code Prompt: [Feature / Fix Name]
For the operator — what this fixes, in one page (plain language)
0. Task class & scope
- Implementation — code change with unit-test coverage; build + deploy + verify. (All sections.)
- Spike — empirically validate an unproven mechanism BEFORE any production spec is written.
Output is a findings doc in
felhom.eu/documentation/audits/SPIKE-<topic>-<date>.md, not shipped production code. (§1, §2, §3, §4, §13-verify, §15. Skip §5-§12 unless the spike writes throwaway probes.) - Risky / supervised — touches agent / golden image / provisioning / destructive disk ops, or
anything that mutates real customer data. Spec it; implement during the supervised session itself,
directly on
main. Mandatory STOP point in §13. (All sections + §13 STOP is load-bearing.) - Runbook-style validation — see note above; prefer a RUNBOOK-*.md.
Repos touched (check all — each gets its own baseline, green gate, build/deploy, and wrap-up):
felhom-controller(in-guest; Docker-only; no Proxmox creds) — app domainfelhom-agent(host; operator-tier; owns ALL Proxmox interaction)felhom.eu(hub/,website/,documentation/)app-catalog-felhom.eu(compose templates +.felhom.yml)
Don't confuse the two ex-"controllers": felhom-agent (host, was
proxmox-controller) vs this felhom-controller (in-guest, wasdeploy-felhom-compose). Host/disk/Proxmox/Cloudflare logic lives in the agent; the controller keeps stack/deploy/UI/app-data-backup/metrics/git-sync/notifications.
1. Confirmed baselines (anti-drift anchor — MANDATORY)
| Repo | main @ commit |
Current version | → Target version |
|---|---|---|---|
| felhom-controller | <hash> |
v0.XX.0 |
v0.XX+1.0 |
| felhom-agent | <hash> |
v0.YY.0 |
v0.YY+1.0 |
| felhom.eu (hub) | <hash> |
v0.ZZ.0 |
v0.ZZ+1.0 |
An unpushed change does not exist for validation or deployment. CC commits directly to main
(trunk-based — no feature/fix branches). If a change can't be cleanly shipped, revert + report;
never park it on a branch.
2. Overview
3. Spike-first gate
- Does this rely on any unvalidated mechanism? yes / no
- If yes: is it already validated in a spike doc? Cite it:
felhom.eu/documentation/audits/SPIKE-.... - If yes and not validated: STOP — switch task class to Spike. Do not spec production code on assumptions.
4. Read these files first
/mnt/5_hdd/felhom.eu/git/CLAUDE.md— workspace orientation (the felhom system, shared conventions, access). Versioned copy:felhom.eu/documentation/runbooks/workspace-CLAUDE.md.<repo>/CLAUDE.md— repo build/deploy, code-quality rules, trunk-based + live-validation rules.<repo>/CONTEXT.md— current project state / decisions / roadmap.felhom.eu/documentation/architecture/02-controller-module-map.md— KEEP/PORT/DELETE/MODIFY per-package classification. Read before touchingbackup/,storage/,system/,config/.felhom.eu/documentation/controller/<feature>.md— code-verified feature docs (authoritative; match code, not summaries, if they drift).controller/README.md(orfelhom-agent/README.md) — module map, feature reference, REST API.- [exact source file(s) to modify] — [why it's relevant].
- [exact source file(s) to depend on] — [why it's relevant].
5. Existing functions / helpers to reuse (MUST use — do NOT reinvent)
| Symbol | File (landmark) | Signature / use | Modify? |
|---|---|---|---|
Manager.StopStack |
internal/stacks/manager.go (~L682) |
StopStack(name string) error — idempotent |
No |
Manager.RedeployFromEnv |
internal/stacks/deploy.go (~L478) |
writes app.yaml then compose up -d + post-start status |
No |
appbackup.AppDataDir |
internal/.../paths.go |
namespace+app → data dir | No |
writeDiskJSON |
internal/.../agent_disk_handlers.go |
JSON envelope for disk endpoints | No |
| [helper that needs a change] | path/file.go |
Func(args) |
Yes — add intent param; gate on IntentEnrolled |
Do NOT reuse: [name the dangerous-lookalike + why], e.g. rsyncMirror (has --delete).
6. Pattern references (copy structure from these canonical files)
| Pattern | Canonical file | Key traits to copy |
|---|---|---|
| REST handler | internal/api/router.go + a sibling handler |
stdlib net/http, JSON envelope, error logging |
| Local-API disk handler | internal/localapi/disks.go (handleDiskEject) |
withGuest, scopedFromBody, role gate, fail-safe-to-protected |
| Scheduler job | internal/.../scheduler |
register + skip-if-running, graceful shutdown |
| Crash-safe journal | internal/stacks/backup.go |
journal-before-mutate, atomic tmp+fsync+rename, Recover on startup |
| Unit test | internal/.../*_test.go (a recent non-hollow one) |
t.TempDir(), fakes/seams, effect assertions |
| HTML template | internal/web/templates/*.html |
Hungarian UI, Go template funcs, no inline secrets |
7. Integration scenarios (must pass)
Scenario A — [happy path]
Given: [concrete state]
When: [exact endpoint / CC action]
Then:
- [effect 1 — exact value/state]
- [effect 2]
- [the WRONG outcome this must NOT produce]
Scenario B — [alternative / role / mode]
Given: [what differs from A]
When: [action]
Then: [how outcomes differ and WHY]
Scenario C — [error / boundary / security gate]
Given: [bad input / unauthorised actor / data-bearing device]
When: [action that must be refused]
Then: [exact refusal — HTTP status, error, and the proven non-effect, e.g. "mkfs NOT called"]
8. Edge cases & deduplication rules
| Condition A | Condition B | Result |
|---|---|---|
9. Strict rules
- Green gate (Go): after every commit/phase, in each touched repo:
go build ./... && go vet ./... && go test ./...— all green before proceeding. The build IS the typecheck; do not accumulate compile errors. - Minimal changes: build only what's listed. No "while I'm here" refactors. Note anything worth fixing under "Observations" (§15) — don't act on it.
- No silent failures: never swallow a parse/exec error — log it. Check a subprocess's own exit
code; never pipe in a way that hides a 127. (The silent
.felhom.ymlquoting bug + the spike's exit-swallow lesson.) - Secrets safety: log env var keys, never values. Never write secrets (tokens, passwords,
keys) into
CHANGELOG.md,REPORT.md, or any committed file — reference as "stored out-of-band". - Line numbers are landmarks: reference code by symbol/landmark; reconfirm the line in the real source before editing (bundled files ≠ source files).
- Trunk-based: commit directly to
main. No branches. Can't ship cleanly → revert + report. - Version discipline: never
:latest; bump the version on every task; one CHANGELOG entry per shippable change; overwrite REPORT.md. - Coding standards: follow
<repo>/CLAUDE.md(cross-cutting) and the feature doc / README for domain patterns.
10. Non-hollow test discipline (MANDATORY for any correctness/security fix)
- Assert the effect happened, not just absence of error. Security example: prove
mkfswas NOT called on a data-bearing device by inspecting the device/fake state — not by trusting the caller. - Companion red-proof: for every correctness fix, write the test so it FAILS on the pre-fix / trivial implementation. Demonstrate this: run the mutation (or guard-removed shape), confirm the test fails, revert. State the result in §15.
- FS work uses
t.TempDir(); orchestration uses fakes/seams (introduce a smallstackOps/copierinterface so tests don't shell out to docker/rsync). - Generation/idempotency: assert the negative (e.g. "fetch count does NOT increment on an
unchanged heartbeat"; "a re-run finds its own prior
(N)and adds no(1)(1)"). - Seam discipline — every seam added gets ONE test through the PRODUCTION wiring path. An
injected-seam test proves the component, never the caller. Three shipped defects in three days
make this non-negotiable: controller v0.154.0 (the wizard read a flag the handler never sets),
agent v0.91.0 (
main.gonever calledSetAuthSink, so the whole auth-honesty leg was inert), and agent v0.92.0 (the watchdog had no sudoers grant for three of its four probes). Every one of them was fully green. Where the caller isfunc main()and cannot be invoked from a test, walk its AST for the call — and note that astrings.Containson the source is NOT sufficient: a commented-out call still contains the string, which is how the controller's first version of that test passed its own red-proof (2026-07-21).
Part 1: [Foundation / engine]
1.1 [Task]
File(s): path/file.go
Problem: [what's wrong / what to build, and why]
DO: [explicit positive instructions; tricky code snippets only — CC fills the obvious parts]
DO NOT: [the specific anti-pattern for THIS task]
Crash-safety (if stateful): journal-before-mutate; atomic tmp+fsync+rename; Recover on startup;
guaranteed cleanup via defer; single-flight mutex; ListLXC-style ground truth in recovery.
Green gate: go build ./... && go vet ./... && go test ./internal/<pkg>/
Part 2: [Core logic / external surface]
2.1 [Task]
Phase order (risky surfaces): Phase A structural → validate → Phase B security/external surface.
Green gate: full go test ./...
Part 3: [Web / UI — if touched]
3.1 [Task]
Hungarian strings: "[exact copy]" — e.g. "Adatok másolása: (X% — Y/Z GB)…".
Green gate: go build ./... + go test ./...
Part N: Documentation & versioning
N.1 CHANGELOG.md (each touched repo)
Cumulative, newest entry on top. New version number. Cite the files/commits for each item.
N.2 REPORT.md (each touched repo) — OVERWRITE
Most-recent implementation only (not cumulative). MUST contain: confirmed baselines, per-commit hashes, per-test results, deployed versions, and an explicit "NOT yet live-validated — awaiting supervised [Bx]" list. No secrets.
N.3 CONTEXT.md
Decisions made, architectural state, what's next.
N.4 README / architecture docs
<repo>/README.md if architecture/features changed; authoritative architecture/feature docs in
felhom.eu/documentation/{architecture,controller}/. Spikes/audits → felhom.eu/documentation/audits/.
N.5 Capability map + backlog (end-of-session checklist — easy to forget)
If the task changed what the platform can do (new/removed capability, or a capability moved status), update in the SAME session:
architecture/00-capability-map.md— add/adjust the affected scenario row with the correct status (PROVEN-LIVE requires anaudits/|tests/citation; else IMPLEMENTED/PARTIAL) and an evidence citation. A leg not exercised live → PARTIAL/IMPLEMENTED with a note saying which leg and a→ R-npointer, never PROVEN-LIVE.backlog/ROADMAP.md— add any new open work / deferred legs as an item (next freeR-n), collapse a now-shipped item to its one-liner + version, and honor the coupling rule (every roadmap item names the map row it flips; every map gap row points back by ID). Parked sub-features (e.g. an alternative transport, an agent-plane leg) go as a note under the owning arc's item.
N.6 Website version bump (if controller/hub version is shown on the site).
11. Tests
Test Group A — [Scenario A]
- setup (t.TempDir / fakes)
- action (the exact call)
- assert (effects, exact values/counts)
- companion: [mutation that makes it fail on pre-fix code]
Test Group B — [Scenario B]
Test Group C — [Scenario C — error/security gate]
12. What NOT to do
- Do NOT [specific out-of-scope thing CC might build].
- Do NOT touch [the untouchable: e.g. the operator-signed
DecommissionExecutor/classify.go]. - Do NOT reuse [the dangerous lookalike from §5].
- Do NOT refactor nearby code, change passing tests, or create a branch.
- Do NOT add "Co-Authored by..." anywhere
13. Build / deploy / STOP
Clean-tree gate first: git status --porcelain empty AND HEAD == origin/main in the repo
being built. An unpushed change does not exist.
Controller (local build on DooPlex → guest via golden/bootstrap):
FELHOM_ROOT=/mnt/5_hdd/felhom.eu
# build + push
cd $FELHOM_ROOT/build/felhom-controller && git -C $FELHOM_ROOT/git/felhom-controller pull && ./build.sh <VER> --push
# deploy to guest (golden/bootstrap): docker pull → write /etc/felhom-controller-image → restart bootstrap svc
# verify:
ssh felhom-pve "pct exec 9201 -- docker ps --filter name=felhom-controller --format '{{.Image}} {{.Status}}'"
Agent (build locally, scp /tmp/felhom-agent-<VER> felhom-pve:/tmp/ — one hop; deploy to
felhom-pve: backup prior binary, replace, restart service; verify clean restart — e.g.
ReassertGuestBinds does not rebind a non-enrolled drive).
Hub (felhom.eu/hub/): build + push locally, then bump manifests/hub.yaml's image: tag in
git and do a deliberate ArgoCD sync — never kubectl set image (auto-sync is OFF and any
imperative change is reverted on the next sync). Verify Synced/Healthy + rollout + image + logs.
Live validation rule: if validating a user-facing feature, exercise the real server pipeline (connect → enroll → deploy), or invoke the exact endpoint the UI invokes (acceptable proxy — no server logic skipped). Do NOT hand-set state via raw agent attach (the F9 bypass). Browser automation is NOT available on DooPlex — endpoint-level is the standard method; say which was used.
STOP point (risky/supervised tasks): build + unit-test + build/deploy images, but do NOT run a
live destructive op or migrate real customer data — that is the supervised [Bx] step. Throwaway
marker dirs under a scratch /mnt path are OK only if they touch no enrolled customer data.
14. Implementation order
Phase 1: [Part 1] → go build ./... && go vet ./... && go test ./internal/<pkg>/
Phase 2: [Part 2] → full go test ./...
Phase 3: [Part 3 — UI] → go build ./... && go test ./...
Phase N-1: [Tests] → full go test ./... (all green)
Phase N: [Build/deploy/verify + docs + push to main]
15. Final verification & deliverables
Run, per touched repo: go build ./... && go vet ./... && go test ./...
Report MUST include:
- Confirmed baselines used (repo @ hash, version).
- Files created / modified (paths).
- Per-commit hashes pushed to
main. - Per-test results (name → pass/fail) + the §10 companion red-proof outcomes.
- Test count before / after; all green (or list failures).
- Deployed versions +
docker ps/ agent-service / hub-pod verification output. - NOT yet live-validated — awaiting supervised [Bx]: explicit list (the real put-data → operate → integrity flow).
- Observations: out-of-scope items noticed — documented, NOT acted on.