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:
+16
-1
@@ -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.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -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 `<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`.
|
||||
### 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.
|
||||
|
||||
Reference in New Issue
Block a user