Two defects in v0.254.0's globe, both plain on a browser and neither catchable by anything
that existed — every test read the MARKUP, and the fault was in which CSS file the browser
fetched.
The shells requested /static/style.css with NO ?v=, while layout.html has carried one since
v0.166.0. A browser holding a copy from before v0.254.0 kept serving CSS with no .lang-globe
rules, so the globe came out as a bare unstyled <details> — a stray triangle and two plain
words at the edge of the window. It was FIVE shells, not the three named: both guest share
pages have the same fault for any CSS change, and their visitor is the likeliest of all to be
holding an old copy. And .Version was missing from three of those five data maps, which is
exactly how the next one would be forgotten — it is now filled at the one choke point every
shell renders through.
The globe also floated outside the card, pinned to the corner of the VIEWPORT, reading as part
of the browser rather than the page. It now sits inside the card, centred under the footer, with
the menu opening upward via the shared rule — so the dashboard and the shells cannot drift.
AND A THIRD, caught by a test that already existed: putting the version on the guest share pages
would have printed the controller build onto a page a stranger with a capability URL can open.
TestShareGuest_HeadersTilesNoAdminChrome refused it. Those two now take an opaque per-build tag
— same cache-busting, no disclosure. The fill is ONE function shared with the parity harness,
because a fixture rendered through a different data path is a picture of a page nobody serves,
which the previous release got wrong twice.
15 shell fixtures re-captured; 91 identical, every dashboard page among them.
MinAgent: 0.131.0 (unchanged). No hub release needed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
Ten pages converted against fixtures captured from unconverted templates (6be55a0); marker
coverage test; English page mask narrowed; English retrieval-promise stems; common keys;
title keys and infraMeta from the bundle; old wording tests read the Hungarian expansion.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
Message bundles (internal/i18n) expanded into templates before parsing, one
template set per language. Launcher, /backups, /apps/<slug> and the layout
converted; household language setting, POST /settings/language, ?lang= override,
report field. Parity test against fixtures captured from unconverted templates;
copy gates read templates expanded; new i18n_missing_gate.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
gofmt -w across the controller tree (46 files) so gofmt -l is empty — disarms the
formatting landmine where a targeted edit + accidental gofmt -w swept ~46 unrelated
files. Pure formatting: whitespace + gofmt's optional-semicolon removal in reflowed
inline closures. One doc comment reworded ('' -> 'the empty string') to avoid gofmt's
Go-1.19 doc-comment typographic substitition ('' -> curly quote) muddying its meaning.
No build/vet/test behavior change.
- F6: POST /api/storage/init runs format→mount→register as a DETACHED single-flight job (context.Background, netAddState shape) the wizard polls via GET /api/storage/init/status; 3-step progress; register-last marker-last crash-safety. Fixes the client-disconnect-aborts-mount bug. No agent change (chain reaches FileBrowser sync = controller-only). Red-proof TestStorageInit_DetachedSurvivesClientDisconnect.
- F7: storage_init/attach Vissza → /storage (was /settings). Test TestStorageWizardBackAnchors_PointToStorage.
runStorageInit/runStorageAttach resolved the fs UUID only via agent.Disks(),
which does NOT include a raw (unenrolled, non-PVE-storage) device — so a raw
candidate could be offered but never enrolled ("no fs identifier"). New
resolveEnrollUUID falls back to the raw-device scan (/disks/candidates), which
reports each free disk's durable_id (uuid:<fs-uuid>). Both enroll paths use it;
legacy re-attach (drive in /disks) still works. Test + red-proof.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
In the intermediary-mount model an enrolled drive binds live into the running
guest (no reboot), so the "… meghajtó aktiválásra vár / Újraindítás most" banner
was an obsolete relic — also dead since v0.78 (pendingActivationDrives keyed on
the raw MountPath vs the now-stable sp.Path). Removed the banner block +
activatePendingDrives JS (settings.html), the PendingDrives feed (handlers.go),
and the dead pendingActivationDrives helper + its unused internal/system import.
Renamed handleStorageActivate -> HandleServerReboot (split out a testable
serverReboot core), removed the /api/storage/activate case, and mounted the
handler at the new non-storage route /api/server/reboot (RequireAuth+CsrfProtect).
The agent GuestReboot primitive is reused unchanged.
Added the standalone "Kiszolgáló újraindítása" settings card (sibling to the
controller-only "Vezérlő újraindítása"), reusing the pollRestart() loop.
Test: TestHandleServerReboot_CallsGuestReboot (fake diskAgent asserts GuestReboot
invoked once + 202). diskAgent/mockAgent gained GuestReboot.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017PsnU2ASocYrvzqE82YDYW
handleStorageRegister (the "Regisztrálás" action for an already-mounted,
unregistered drive) registered the raw /mnt/<name> path verbatim, unlike
runStorageInit/runStorageAttach which map to the stable /mnt/felhom-drives/<name>
path the agent actually binds the drive at. The controller then watched an empty
placeholder dir on the guest rootfs → "Rendszermeghajtón" + stuck "activation
pending" banner after a re-provision.
Fix: register stablePathForName(path.Base(req.Where)); attachIntoGuest still uses
the raw path (the agent operates on raw). Test + red-proof added.
Diagnosis: felhom.eu/documentation/audits/DIAGNOSE-drive-bind-after-reprovision-2026-06-23.md
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017PsnU2ASocYrvzqE82YDYW
Drives are visible in-guest only at the STABLE /mnt/felhom-drives/<name>; the
registered path + HDD_PATH + FileBrowser source repoint there while agent calls
map back to raw /mnt/<name> (agentWhere). Enroll binds-under-parent before
register. Drive-absent GATE (planDriveGates + 30s driveGateLoop) stops/blocks
apps when a drive vanishes and auto-restarts on return; start-gate refuses start
when the drive is absent. H1 endpoints (disconnect/reconnect/restart-apps) routed
onto host-side ops. Non-hollow tests + companions.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
agentapi.Decommission + handleStorageDecommission (migrate-all-or-none, Change 2):
migrate-then-decommission via the migration done-hook, or decommission-anyway (stop
apps, keep HDD_PATH). 'Hiányzó tárhely' badge on dashboard/stacks/app card when an
app's drive is decommissioned/disconnected/absent. Change 4: registerStoragePath
clears the decommissioned marker on re-enroll (ClearDecommissioned had no callers).
Non-hollow tests incl. mutation-proven Change-4 companion.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Tier 2 rsync-mirrors each HDD app's recovery unit + appdata to a DIFFERENT physical
disk (the only off-drive protection bind-mounted userdata can get; PBS can't reach it).
Auto-enabled, auto-target: prefer another registered drive (different physical disk via
system.SamePhysicalDevice), else the internal SSD for SMALL units only — with a
size-aware headroom guard that REFUSES rather than fill the ~8G guest rootfs, recording
an honest "needs 2nd HDD" status. Status persisted via the surviving CrossDriveBackup;
"2. mentés" UI card now populated. Daily tier2-backup job + POST /api/backup/tier2.
- backup/tier2.go (engine+selection+headroom), tier2_test.go (headroom arithmetic)
- system.SamePhysicalDevice (linux Stat_t.Dev + stub)
- handlers.go Tier2 UI population + tier2DestLabel; backups.html honest no-target reason
- fixed stale TestBackupCopiesOnPath (old felhom-data layout -> in-guest layout)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4A: user-data drives are backup-target-eligible (not role-locked) — surfaced in
the drive purpose note. 4B: handleStorageImpact returns backup_copies (apps whose
cross-drive backups live on the drive, via backupCopiesOnPath); the wipe/eject
modal warns they'd be destroyed (stays customer-confirmable — copies redundant).
Cross-drive backup engine remains out of scope. Test: TestBackupCopiesOnPath.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
agentapi GuestAttach(where) → POST /disks/guest-attach; runStorageInit/Attach +
handleStorageRegister call attachIntoGuest after register (best-effort, P3 heals).
Closes Branch A: enrolled drives become usable in the guest, banner clears.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>