diff --git a/CONTEXT.md b/CONTEXT.md index 22304ab..7d6bda1 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -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. --- diff --git a/REPORT.md b/REPORT.md index f58be1b..24883bf 100644 --- a/REPORT.md +++ b/REPORT.md @@ -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). +### B1 — deterministic 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`. -### B1 — Role-aware overview (no destructive controls on protected drives) -- `settings.html` "Meghajtók (ügynök nézet)" restyled from a raw `` 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`. +### B2 — init 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. -### B2 — Customer 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. +### B3 — register 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. -### B3 — Init/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. +### B4 — system-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. -### B4 — UI 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. +### B5 — eject 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.