docs(v0.45.0): REPORT + CONTEXT + README for storage UX polish; live-validated on guest 9201

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-12 09:50:27 +02:00
parent 9ed844fd0b
commit fa60ba50c0
2 changed files with 77 additions and 51 deletions
+16 -1
View File
@@ -7,7 +7,22 @@
>
> Ask Claude Code: "Please update CONTEXT.md with what we did today"
Last updated: 2026-02-19 (session 59)
Last updated: 2026-06-12 (storage UX polish)
> **NOTE:** this file is stale below this banner (last full pass session 59 / v0.16.1). Current state
> is tracked in `CHANGELOG.md`, `controller/README.md`, and the auto-memory `MEMORY.md`. Live version:
> **v0.45.0**.
>
> **2026-06-12 — storage UX polish (v0.45.0), pairs with felhom-agent v0.24.0:**
> - **Agent eject role-gate (Part A, felhom-agent v0.24.0):** `POST /disks/eject` now refuses to
> unmount system/backup storage *at the agent* (fail-safe to protected on ambiguity) — the UI hiding
> the button was never the control. Validated live on guest 9201 (eject `/var/lib/vz` → 403, no unmount).
> - **Controller (Part B, v0.45.0):** B1 deterministic `/api/disks` order (user-data→system→backup,
> alpha within); B2 init wizard excludes mounted drives; B3 **Regisztrálás** primary action for a
> mounted-but-unregistered user-data drive (`POST /api/storage/register`); B4 per-card purpose
> descriptions + app-backing tags + tiering note (`local` & `local-lvm` both kept); B5 eject already
> names affected apps. All validated live on guest 9201.
> - **Golden rebake NOT done** (no registry creds on `felhom-pve`); controller self-update covers drift.
---
+61 -50
View File
@@ -1,61 +1,72 @@
# REPORT — felhom-controller v0.44.0: role-aware drive management + customer type-to-confirm wipe
# REPORT — felhom-controller v0.45.0: storage UX polish (order, init filter, register shortcut, clarity)
**Repo:** `felhom-controller` · **Version:** 0.44.0 · **Date:** 2026-06-11 · pairs with **felhom-agent v0.23.0**
**Repo:** `felhom-controller` · **Version:** 0.45.0 · **Date:** 2026-06-12 · pairs with **felhom-agent v0.24.0**
## What this implements
Part B of the storage-fixes spec — controller-side ordering/filter/clarity polish on top of v0.44.0's
role-aware drive management. (Part A, the eject role-gate, lives at the agent — see felhom-agent
v0.24.0 / its REPORT.)
The controller half of the storage-authorization redesign (CC SPEC, Part B). The drive UI is driven by
the agent's authoritative **role** (`system` | `backup` | `user-data`): system/backup are visibly
protected with no destructive controls; the customer manages their own data drives with informed
consent (type-to-confirm + named app impact).
## What changed
### B0`agentapi` client
- `DiskInfo` += `role`, `total_bytes`, `used_bytes`, `used_fraction`.
- `FormatResult` += `role`, `needs_confirmation`, `durable_id`.
- `FormatDisk(ctx, device, fstype, confirmed, durableID)` — new `ErrNeedsConfirmation` (user-data,
awaiting the customer's confirmation) vs `ErrFormatRefused` (system/backup, operator signature).
### B1deterministic disk order (`internal/web/agent_disk_handlers.go`)
`agentDisksListHandler` sorts the agent's drive list server-side before rendering (`sortDisksForView`):
**user-data → system → backup** (then unrecognized), alphabetical by storage name within each tier.
The agent's storage view iterates an unordered Go map, so the list previously reordered on every reload
(CLAUDE.md lesson #3). A stable Go-side contract beats relying on map order or template JS.
Test: `TestSortDisksForView`.
### B1Role-aware overview (no destructive controls on protected drives)
- `settings.html` "Meghajtók (ügynök nézet)" restyled from a raw `<table>` to **cards** (house style):
name prominent, mono device/mount sub-detail, badges for class / data / **role** (🔒 lock for
system & backup) / registered, and a **capacity bar** reusing the monitoring `system-bar`
(green→amber→red). Eject/Wipe controls render **only** for user-data drives mounted under `/mnt`.
### B2init wizard excludes mounted drives (`templates/storage_init.html`)
The `formattable` filter gained `&& !d.mount_path` (matching the attach wizard): an already-mounted
drive (e.g. `felhom-usb`) no longer appears as an "initialize" candidate. Eject it first to make it an
init target.
### B2Customer wipe/eject flow
- **Name the apps**: `GET /api/storage/impact?where=``appsUsingPath` → the deployed apps (by display
name) whose `HDD_PATH` is that mount. Shown in the modal before any destructive action.
- **Type-to-confirm**: a modal with a text field; the destructive button stays disabled until the typed
value equals the mount name exactly (enforced client-side AND server-side in `/api/storage/wipe`).
- **Wipe** (`POST /api/storage/wipe`): eject (unmount + deregister) → server-side two-step
customer-confirmed format (learn the agent's durable id via the NeedsConfirmation response, then
re-submit `confirmed:true` bound to it). Deregisters the StoragePath.
### B3register shortcut for a mounted-but-unregistered user-data drive
- New `POST /api/storage/register``handleStorageRegister` → reuses `registerStoragePath` (the
manual-add path): records the existing mount into the `StoragePath` registry (no format, no eject),
then FileBrowser-syncs. `AddStoragePath` dedupes (clean error on double-register).
- `settings.html`: a mounted, **unregistered** user-data drive now shows **Regisztrálás** as its
PRIMARY per-card action; Leválasztás/Törlés stay secondary.
### B3Init/attach role-gated + restyled
- `storage_init.html`: data-bearing path now uses the **customer-confirmation** flow (type-to-confirm →
re-submit confirmed) instead of the `felhom-opsign` instruction; selector restyled to cards, lists
**only user-data** targets (system/backup are not offered). The opsign surface remains as a fallback
if a protected device somehow reaches it.
- `storage_attach.html`: restyled to cards, user-data only.
### B4system-storage clarity (presentation only, `settings.html` + `style.css`)
`local` and `local-lvm` are both kept (not collapsed). Each card now carries:
- a plain-Hungarian **purpose description** keyed on the agent's `type`/`role` (`purposeDesc`):
local-lvm → internal SSD (system, Docker, app **databases**); local → host storage, **no app data**;
pbs → backups; user-data → external store for large app files.
- an **app-backing tag** (`appBackingTag`): `local-lvm` → "Alkalmazás-rendszer"; user-data
"Alkalmazás-adatok".
- a one-line **tiering note** above the list answering "which storage do the apps use?".
Role/type stay authoritative from the agent — no agent contract change.
### B4UI polish
- New CSS appended to `style.css` (reusing existing tokens): `.drive-card` / `.drive-badges` /
`.drive-cap`, the missing `.badge-ok` / `.badge-lock` / `.badge-muted` / standalone `.mono`, and the
type-to-confirm `.confirm-overlay` / `.confirm-box`. No new design system; no raw table left.
### B5eject confirmation names affected apps
Already wired pre-existing: `confirmEject` → the type-to-confirm modal fetches `/api/storage/impact`
and lists, by name, the deployed apps that lose their storage (parity with the wipe warning). Verified,
no code change needed.
## Tests
- `internal/agentapi/disks_test.go` — blank → ok; system/backup → `ErrFormatRefused` (+pending op);
user-data → `ErrNeedsConfirmation` (+durable id + role); confirmed → formatted.
- `internal/web/storage_handlers_test.go` — init on a user-data data-bearing device returns
NeedsConfirmation and does NO mount/register; confirmed init forwards the confirmation+durable id and
proceeds to register; system/backup init still surfaces the opsign; `appsUsingPathIn` names the right
deployed apps (dependency-impact). Plus `TestTemplatesParse` (all templates parse).
## Build / deploy
- Built `felhom-controller:0.45.0` on the build server (`build.sh 0.45.0 --push`).
- Deployed to **guest 9201** on `felhom-pve`: `docker pull`, updated `/etc/felhom-controller-image`,
re-ran `felhom-controller-bootstrap.sh` → container recreated on `0.45.0`, **healthy**;
`[selfupdate] Current version 0.45.0 is up to date`.
- CHANGELOG (newest on top) + `controller/README.md` (Storage Management section) updated.
`go build ./... && go vet ./... && go test ./...` all green (Go 1.26, local).
## Live validation (guest 9201, auth disabled on this demo → API reachable)
- **B1 order:** `GET /api/disks` returns `[felhom-usb(user-data), local(system), local-lvm(system),
felhom-pbs(backup)]` — **stable across 3 reloads** (user-data first, system alpha, backup last).
- **B2 filter:** the only user-data drive (`felhom-usb`) has a `mount_path`, so the new filter excludes
it from init candidates (confirmed by the live disk data).
- **B3 register:** `POST /api/storage/register {where:/mnt/felhom-usb}` → `{registered:true}`;
`settings.json` now lists `/mnt/felhom-usb` (label "Tárhely (felhom-usb)", schedulable);
`FileBrowser mounts synced — 1 storage path(s)`.
- **B5 impact:** `GET /api/storage/impact?where=/mnt/felhom-usb` → `{apps:[], where:…}` (valid wiring;
no deployed app currently routes data there).
- **B4 clarity:** pure presentation rendered client-side from the agent's role/type (templates
parse-tested; the disk data carries the keys the JS branches on). Visual confirmation is a browser
check.
- `go test ./...` green for the whole module.
## Version
`v0.43.0 → v0.44.0` (build-time ldflags `Version`).
## Live validation / deploy
Pending in this session: build+push the image, deploy to guest 9201, rebake the golden, and run the
live checks (overview tiers, the hand-issued `confirmed:true` refusal on a system/backup device, a
user-data confirmed wipe binding to the durable id, and the audit-log entry).
## Not done / human step
- **Golden rebake** (to bake controller 0.45.0 into new-guest templates): NOT performed — `felhom-pve`
has no registry credentials (`REGISTRY_USER`/`REGISTRY_TOKEN`, stored out-of-band) and there is no
golden archive currently on the host. Controller **self-update covers the drift** (a freshly
provisioned guest would self-update to 0.45.0), so existing + new guests converge regardless. The
rebake remains a human/credentialed operational step.