1aa1bd17c2
Three drills, three orphaned hub customers -- drill-r50, sess-c, sess-d -- because §13 covered the clean-tree gate, build/deploy, live validation and the STOP point and said nothing about teardown at all. Layers 1 and 2 (the VM and its volumes; the host's reclaimed space) get remembered because they are visible on the box. Layer 3, the hub-side customer or appliance record, is invisible from there and has been missed every time -- sess-c was not even recorded by its own report, so the record claimed a clean teardown that had not happened. §13: a Teardown subsection at the end, before §14. All three layers, with the hub layer requiring an EXPLICIT disposition -- deleted, retained as a fixture with the reason, or gate-blocked with the command recorded -- because silence is how drill-r50 became simultaneously a blocked customer and the only drift fixture. Cites runbooks/target-selection.md for which machine to provision on rather than restating it. §15: deliverable 8 demands the evidence for all three layers and names the failure it prevents; the former 8 (Observations) becomes 9. No section renumbered, §13/§15 not restructured, author checklist untouched. Part 1 of the R-116 join task, committed alone and before any Go file is opened -- the code half ends in a live run and live runs have stalled twice, while the record work is unconditional.
515 lines
27 KiB
Markdown
515 lines
27 KiB
Markdown
# Claude Code Prompt: [Feature / Fix Name]
|
||
|
||
<!--
|
||
FELHOM PROMPT TEMPLATE v1.0
|
||
Adapted from the jarrs.eu template + harmonised with the live Felhom conventions
|
||
(controller/agent/hub/app-catalog, Go stdlib, trunk-based, two-AI workflow).
|
||
|
||
WHO WRITES THIS: Claude.ai (architecture/validation partner). WHO EXECUTES IT: Claude Code (CC),
|
||
which has live SSH to the build server, the demo nodes, and felhom-pve, plus the Gitea push token.
|
||
|
||
HOW TO USE:
|
||
1. Copy this template into a TASK-*.md (a spec for CC to implement).
|
||
2. Pick the TASK CLASS (§0) — it decides which sections are mandatory vs "N/A".
|
||
3. Fill in every mandatory section. If a section doesn't apply, write "N/A — [reason]"
|
||
(don't delete it, so CC knows it was considered).
|
||
4. Verify all confirmed baselines and reuse line-refs against LIVE Gitea source before sending
|
||
(cardinal rule: do not trust memory or a prior REPORT.md).
|
||
5. Run the AUTHOR CHECKLIST at the bottom before handing the prompt to CC.
|
||
|
||
This is NOT a RUNBOOK. TASK-*.md = "build this, push, document." RUNBOOK-*.md = "execute this
|
||
operational procedure (incl. live validation)." If the deliverable is an operational run, write a
|
||
RUNBOOK instead and use only §0, §1, §13, §15 of this template.
|
||
-->
|
||
|
||
---
|
||
|
||
## For the operator — what this fixes, in one page (plain language)
|
||
|
||
<!--
|
||
MANDATORY for any M+ task and for any task carrying a STOP. Optional but encouraged below that.
|
||
Precedent: TASK-B, TASK-C and TASK-D shipped with this section and it is why those runs needed no
|
||
mid-flight explanation; TASK-E made it a standard block.
|
||
|
||
WHO IT IS FOR: a technical-but-not-in-the-code reader — someone who runs the business and the
|
||
hardware, knows what the components are, and has not read the file you are about to change.
|
||
|
||
IT MUST SAY, in this order:
|
||
1. WHAT BREAKS TODAY — the observable symptom, not the code defect. "Your Stop is silently
|
||
undone on a reboot", not "shouldRecreateOnBoot ignores container state".
|
||
2. WHAT CHANGES — the new behaviour, in the same observable terms.
|
||
3. WHAT THE OPERATOR WILL BE ASKED TO DO — every STOP, every ruling needed, every physical
|
||
action (flash this, press that, confirm this), and anything that is HUMAN-only. If the answer
|
||
is "nothing", say "nothing".
|
||
4. WHAT IS NOT AFFECTED — especially for anything touching customer data, credentials or
|
||
recovery. "Existing codes still work" belongs here.
|
||
|
||
RULES:
|
||
- No jargon the ROADMAP row doesn't already explain. If a term must be introduced, define it in
|
||
the sentence that uses it.
|
||
- No file paths, no function names, no version numbers except the ones the operator acts on.
|
||
- One page. If it does not fit, the task is probably two tasks.
|
||
- Write it LAST, after the parts are settled, but place it HERE — it is what gets read first.
|
||
- It is a summary, never the spec: it must not be the only place a requirement appears, or CC
|
||
will implement from prose. Everything here is restated precisely in §0 onward.
|
||
-->
|
||
|
||
---
|
||
|
||
## 0. Task class & scope
|
||
|
||
<!-- Pick ONE primary class. It sets which sections are mandatory. -->
|
||
|
||
- [ ] **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 domain
|
||
- [ ] `felhom-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, was `deploy-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)
|
||
|
||
<!-- Verify each against LIVE Gitea before sending. This is what catches "CC built on a stale tree".
|
||
curl -H "Authorization: token $TOKEN" \
|
||
"https://gitea.dooplex.hu/api/v1/repos/admin/<repo>/commits?sha=main&limit=1"
|
||
and read the version from the source of truth (ldflags var / VERSION / latest CHANGELOG entry). -->
|
||
|
||
| 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–5 sentences. What this implements, the user-visible outcome when done, which architecture/spike
|
||
doc it corresponds to, and which slice/phase it belongs to. Orientation, not specification. -->
|
||
|
||
---
|
||
|
||
## 3. Spike-first gate
|
||
|
||
<!-- Answer honestly. If this touches a mechanism whose behaviour has NOT been empirically proven
|
||
(a new external API, an undocumented Proxmox/PBS quirk, a filesystem/mount edge), this is a SPIKE,
|
||
not an implementation. The PBS spike caught `ignore-verified=true`, the node-name-from-UPID
|
||
requirement, and the dual privsep+token grant BEFORE any code — that is the bar. -->
|
||
|
||
- 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
|
||
|
||
<!-- Order matters: workspace/repo conventions first, then architecture, then the exact source.
|
||
Be explicit about WHY each matters. -->
|
||
|
||
1. `/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`.
|
||
2. `<repo>/CLAUDE.md` — repo build/deploy, code-quality rules, trunk-based + live-validation rules.
|
||
3. `<repo>/CONTEXT.md` — current project state / decisions / roadmap.
|
||
4. `felhom.eu/documentation/architecture/02-controller-module-map.md` — KEEP/PORT/DELETE/MODIFY
|
||
per-package classification. **Read before touching `backup/`, `storage/`, `system/`, `config/`.**
|
||
5. `felhom.eu/documentation/controller/<feature>.md` — code-verified feature docs (authoritative; match
|
||
code, not summaries, if they drift).
|
||
6. `controller/README.md` (or `felhom-agent/README.md`) — module map, feature reference, REST API.
|
||
7. [exact source file(s) to modify] — [why it's relevant].
|
||
8. [exact source file(s) to depend on] — [why it's relevant].
|
||
|
||
---
|
||
|
||
## 5. Existing functions / helpers to reuse (MUST use — do NOT reinvent)
|
||
|
||
<!-- For Go: cite file + exported symbol + short signature. Verify EVERY symbol against live source
|
||
(read the file; don't write names from memory — they drift). Line numbers are LANDMARKS only:
|
||
the bundled files CC reads are not the source files, so reference by symbol/landmark and tell CC to
|
||
reconfirm the line. If a helper must change to support this task, say "Yes — [exact change]" in
|
||
Modify? — that becomes an explicit task in a Part below, not an implicit assumption.
|
||
|
||
The forbidden reuse: e.g. "do NOT reuse rsyncMirror (tier2.go) — it has --delete". Name the trap. -->
|
||
|
||
| 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)
|
||
|
||
<!-- For every NEW file type this task creates, point to the ONE canonical file to copy from. -->
|
||
|
||
| 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)
|
||
|
||
<!-- WRITE THESE BEFORE THE IMPLEMENTATION SECTIONS. They are the acceptance criteria.
|
||
Given/When/Then with concrete actors and exact counts/states. Always include the WRONG case.
|
||
EVERY scenario here MUST have a corresponding test in §11/Tests. -->
|
||
|
||
### 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
|
||
|
||
<!-- Explicit rules for overlapping concerns: "When X and Y both apply, the result is Z — not X+Y."
|
||
Use a truth table if >2 branches. (E.g. conflict-merge: target absent → copy; present+identical →
|
||
skip; present+differs → write lowest-free (N), never overwrite.) -->
|
||
|
||
| Condition A | Condition B | Result |
|
||
|-------------|-------------|--------|
|
||
| | | |
|
||
|
||
---
|
||
|
||
## 9. Strict rules
|
||
|
||
<!-- Earned through bugs. Copy verbatim; document any per-task exception. -->
|
||
|
||
1. **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.
|
||
2. **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.
|
||
3. **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.yml` quoting bug + the spike's
|
||
exit-swallow lesson.)
|
||
4. **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".
|
||
5. **Line numbers are landmarks:** reference code by symbol/landmark; reconfirm the line in the real
|
||
source before editing (bundled files ≠ source files).
|
||
6. **Trunk-based:** commit directly to `main`. No branches. Can't ship cleanly → revert + report.
|
||
7. **Version discipline:** never `:latest`; bump the version on every task; one CHANGELOG entry per
|
||
shippable change; **overwrite** REPORT.md.
|
||
8. **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)
|
||
|
||
<!-- The Felhom signature. A test that only asserts "no error" is hollow. -->
|
||
|
||
- **Assert the effect happened**, not just absence of error. Security example: prove `mkfs` was 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 small `stackOps` /
|
||
`copier` interface 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.go` never called `SetAuthSink`, 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 is `func main()` and cannot be invoked from a test, walk
|
||
its AST for the call — and note that a `strings.Contains` on 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` **for graceful exits only** — a `defer` does NOT run on SIGKILL, so a
|
||
crash-safe cleanup needs an on-disk marker plus a startup `Recover()` (Campaign 8 fault 10 proved
|
||
this on live hardware); 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]
|
||
|
||
<!-- Felhom UI is Hungarian, adult tone, minimal emoji (keep UI clean/professional). Specify the exact
|
||
strings. Reuse template funcs (stateColor/stateLabel/isOperational/logoURL/...). Progress panels:
|
||
reuse the deploy 3-step + format-status poll pattern. -->
|
||
|
||
**Hungarian strings:** "[exact copy]" — e.g. "Adatok másolása: <app> (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 The coupling rule — FOUR artifacts, same session (end-of-session checklist)
|
||
If the task **changed what the platform can do** (new/removed capability, a capability moved status,
|
||
or **what is open changed**), update **all four** in the SAME session:
|
||
- **`architecture/00-capability-map.md`** — add/adjust the affected *scenario* row with the correct
|
||
**status** (PROVEN-LIVE requires an `audits/`|`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-n` pointer, never PROVEN-LIVE.
|
||
- **`backlog/ROADMAP.md`** — add any new open work / deferred legs as an item (next free `R-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.
|
||
- **the owning `architecture/*.md`** — ruled as **S-1** in `CONTEXT.md` (2026-07-26, R-81): any task
|
||
that changes an **architectural contract** (tiers, targets, cadences, trust boundaries) updates the
|
||
owning design doc in the same session. It was ruled but never written here, in the template CC
|
||
actually reads — so it bound nobody. Now it does.
|
||
- **`backlog/OPEN-ITEMS.md`** — the register, and the single source of truth for open work. A task
|
||
that changes what is open without touching it re-creates exactly the thread-loss the register was
|
||
built to solve: `REPORT.md` is overwritten every session, so nothing durable may live only there.
|
||
|
||
**Report which `OPEN-ITEMS.md` rows the task opened, closed or re-ranked** (§15). Every row carries
|
||
an owner — a row nobody owns is how items got lost in the first place.
|
||
|
||
### N.6 Website version bump (if controller/hub version is shown on the site).
|
||
|
||
---
|
||
|
||
## 11. Tests
|
||
|
||
<!-- Numbered, mapping DIRECTLY to §7 scenarios. Every scenario (incl. error) gets ≥1 test.
|
||
Each correctness/security test gets the §10 companion red-proof. -->
|
||
|
||
### 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
|
||
|
||
<!-- Concrete temptations for THIS task, not "don't add features". -->
|
||
|
||
- Do NOT [specific out-of-scope thing CC might build].
|
||
- Do NOT [**the forbidden ACT**, and **its reason**: e.g. "modify the operator-signed
|
||
`DecommissionExecutor` — it is the signature boundary"]. **Fence the act, not the object.** A bare
|
||
"do not touch X" is read as covering every act on X, including ones nobody meant to restrict — that
|
||
is how "do not re-target demo-hp's backup target" became "do not use demo-hp at all", which pushed a
|
||
destructive drill onto the one machine holding the recovery chain. State the reason too: a rule whose
|
||
reason is recorded can be correctly narrowed later, and one without it becomes permanent by default.
|
||
- 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
|
||
|
||
<!-- Per touched repo. Real commands. CC runs ON DooPlex — builds are LOCAL, no SSH wrapper. -->
|
||
|
||
**Clean-tree gate first:** `git status --porcelain` empty AND `HEAD` == `origin/main` in the repo
|
||
being built. An unpushed change does not exist.
|
||
|
||
**If the task needs a machine to break — NAME IT.** A drill, destructive test or throwaway VM gets an
|
||
explicit target: *"run this on demo-hp"*. Do not leave it to be inferred from the prohibition list;
|
||
listing only what is off-limits leaves the most valuable unfenced machine as the residual choice, which
|
||
is exactly how a drill landed on DooPlex. Tiers and per-machine permitted/care/forbidden:
|
||
[`runbooks/target-selection.md`](runbooks/target-selection.md).
|
||
|
||
**Controller** (local build on DooPlex → guest via golden/bootstrap):
|
||
```bash
|
||
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.
|
||
|
||
**Teardown.** A run that provisions anything owns its removal. Three layers, and the third is the one
|
||
that gets missed:
|
||
|
||
1. **The machine** — the VM or guest and its volumes, deleted.
|
||
2. **The host** — `pvesm status` before and after, and the space actually returned.
|
||
3. **The hub** — the customer or appliance record the run created. State its disposition
|
||
**explicitly**: deleted; or retained as a fixture **with the reason**; or blocked by the
|
||
ONLINE→DOWN delete gate **with the command recorded for later**. Silence is how `drill-r50` became
|
||
both a blocked customer and the only drift fixture.
|
||
|
||
**Precedent: three drills, three orphaned customers** — `drill-r50`, `sess-c`, `sess-d`. `sess-c` was
|
||
not recorded by its own report, so the record said the teardown was clean when it was not. Layers 1 and
|
||
2 are the ones that get remembered because they are visible on the box; layer 3 is invisible from there
|
||
and has been missed every time.
|
||
|
||
Which machine to provision on in the first place: **`documentation/runbooks/target-selection.md`**.
|
||
|
||
---
|
||
|
||
## 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
|
||
|
||
<!-- CC runs this after all phases. This report is what Claude.ai validates against pushed Gitea
|
||
source (file:line) — the cardinal rule. Make it checkable. -->
|
||
|
||
Run, per touched repo: `go build ./... && go vet ./... && go test ./...`
|
||
|
||
Report MUST include:
|
||
1. **Confirmed baselines** used (repo @ hash, version).
|
||
2. **Files created / modified** (paths).
|
||
3. **Per-commit hashes** pushed to `main`.
|
||
4. **Per-test results** (name → pass/fail) + the §10 companion red-proof outcomes.
|
||
5. **Test count before / after**; all green (or list failures).
|
||
6. **Deployed versions** + `docker ps` / agent-service / hub-pod verification output.
|
||
7. **NOT yet live-validated — awaiting supervised [Bx]:** explicit list (the real
|
||
put-data → operate → integrity flow).
|
||
8. **Teardown evidence — all three §13 layers**, if the run provisioned anything: the machine deleted,
|
||
`pvesm status` before/after with the space returned, and the **hub-side record's disposition named**
|
||
(deleted / retained-with-reason / gate-blocked-with-the-command). A run that provisioned nothing says
|
||
so. "Teardown clean" without layer 3 is not a report — it is the `sess-c` failure.
|
||
9. **Observations:** out-of-scope items noticed — documented, NOT acted on.
|
||
|
||
---
|
||
|
||
<!--
|
||
========================================================================
|
||
AUTHOR CHECKLIST (for Claude.ai — verify before handing to CC)
|
||
========================================================================
|
||
|
||
| # | Check | Prevents |
|
||
|---|-------|----------|
|
||
| 1 | Task class picked; mandatory sections filled (rest "N/A — reason")? | Bloated small tasks / under-covered risky ones |
|
||
| 2 | Confirmed baselines verified against LIVE Gitea (hash + version), not memory? | CC builds on a stale tree |
|
||
| 3 | Spike-first gate answered; unvalidated mechanism → switched to Spike? | Spec'ing production code on assumptions |
|
||
| 4 | Every reuse symbol verified against live source; line-refs marked landmark? | Wrong method names; line drift |
|
||
| 5 | Every helper marked "Modify? Yes" has an explicit Part task? | Helper change treated as afterthought |
|
||
| 6 | Dangerous-lookalike reuse explicitly forbidden (the trap named)? | `--delete` / over-broad reuse bug |
|
||
| 7 | Integration scenarios written BEFORE implementation, incl. the WRONG case? | CC reads top-down; ambiguous acceptance |
|
||
| 8 | Every scenario (incl. error/security) has ≥1 test? | Untested boundaries |
|
||
| 9 | Every correctness/security fix has a non-hollow test + companion red-proof? | Hollow "no error" tests; silent regressions |
|
||
| 10| Green gate stated per repo per phase (build && vet && test)? | Accumulated compile/test debt |
|
||
| 11| Secrets-safety: no secret written to committed files; keys-not-values in logs?| Leaked credentials in CHANGELOG/REPORT/logs |
|
||
| 12| Trunk-based: no branch instructions; revert-and-report escape hatch stated? | Work parked on a branch |
|
||
| 13| Build/deploy commands correct for each repo (controller golden / agent / hub)?| Failed or wrong deploy |
|
||
| 14| STOP point explicit for risky/supervised; live-validation hits real pipeline?| Destructive op run unsupervised; F9 bypass |
|
||
| 15| Doc routing correct (CHANGELOG cumulative, REPORT overwrite, CONTEXT, README, documentation/)? | Doc drift / cumulative REPORT |
|
||
| 15b| Capability changed → `00-capability-map.md` row + status/evidence updated AND `backlog/ROADMAP.md` item added/collapsed (coupling rule)? | Capability map goes stale; deferred legs lost |
|
||
| 16| UI strings Hungarian + adult tone + minimal emoji specified? | English/placeholder UI leaking to customers |
|
||
| 17| §15 report format demanded so the result is validatable against Gitea? | Unverifiable "done" claims (cardinal rule) |
|
||
|
||
If any check fails, fix the prompt before sending.
|
||
========================================================================
|
||
--> |