Extends the content cache (#46) so cacheDirectory/originals stays within a
size limit. Before each original write the effective limit is
min(cacheOriginalsMaxBytes, bytesUsedByOriginals + bytesFree - freeBelowBytes),
with bytesFree from fs.statfs on the cache volume: it falls as the disk fills
and rises as space returns. status().originalsLimitBytes exposes it. A write
that crosses the limit evicts least-recently-used originals until it fits;
pinned originals are skipped, and when only pinned remain the write proceeds
over-limit. Last-use is the file mtime, bumped on every read that returns a
path, so order survives restarts with no ledger.
Eviction runs just after the write, once the plaintext size is known on disk
(the download layer cannot report it beforehand); the freeBelowBytes reserve
absorbs the transient. Defaults: 100 GiB limit, 50 GiB reserve. The pinned
predicate is a hook the precache unit (#48) will supply; only
cacheDirectory/originals is touched — the backup download directory and
thumbnails are never counted or evicted.
Model: opus-4-8
Adds the on-disk content and thumbnail cache keyed by fileID: originals/ and thumbnails/ under cacheDirectory, present-means-complete (streaming atomic rename), orphan temp reaping on open. Photo.original/thumbnail return a cached path with no network when present, else fetch through the shared request pool; thumbnails.ensure drives the thumbnail pool with priority, dedup and AbortSignal. One shared RequestPools set serves both the ML fetch and the content cache. Content-hash integrity is deferred (#68); authenticated streaming decrypt guarantees integrity now.
Model: opus-4-8