librelad db04f8f000 feat(storage): path primitives for multiple container roots
Phase 0 of docs/roadmap/storage-locations.md — the resolver layer. No
behaviour change yet: with no registry present, every function here
returns exactly what the old single-root code did, which is what makes
the ~200-site sweep that follows safe to land incrementally.

  primaryRoot / webuiDir     the install-time root, and the one tree that
                             never moves
  storageRoots               every registered root, primary first
  storageRootAvailable       marker present == drive mounted
  pathIsContainerData        replaces the `== "$containers_dir"*` idiom
                             that picks manager vs container-user elevation
  appDir / appDirSlash       THE resolver, memoised
  storageLocationPath/Name   name <-> path, via the root-owned registry
  storageIndexGet/Set        app -> location cache

appDir resolves discovery-first: whichever root actually holds
<slug>/<slug>.config wins, so a hand-move or half-finished migration
self-heals rather than corrupting.

The index exists because of a bug the unit test caught immediately.
Discovery cannot see an unmounted disk, so an installed app on an
unplugged drive looked identical to a brand-new app — and the fallback
handed back the PRIMARY root. Docker would then have created the bind
mounts there and booted the app empty on the wrong disk, which is the
precise failure the availability design exists to prevent. The index is
manager-owned (deliberately not on the removable disk: it must be
readable exactly when that disk is absent), consulted only when the scan
comes up empty, and rewritten by every successful scan so the disk stays
authoritative whenever it is actually present.

Availability is gated in appDir alone rather than at each caller: every
site reaches it by construction. It returns non-zero AND prints an
unusable sentinel path, so the many callers that will never check $?
still fail loudly on something harmless.

scripts/dev/lp-storage-test covers all of it against a throwaway tree.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-24 04:01:19 +01:00
..