Export and mirror storage measurement

OfflineKit writes export-run artifacts to Export:Runs:StorageRoot/<tenant>/<run>. Mirror is a profile kind: its materializer writes to the same executor-provided directory. Neither stella-exports/ nor mirror/ is a separate remote bucket in this implementation.

GET /doctor/offlinekit/checks, authorized with ops.health, reports:

State IDMeasurement
volume:offlinekit-export-runsLogical file bytes beneath Export:Runs:StorageRoot, including mirror bundles and carrier files
volume:exportcenter-object-store-dataAll logical file bytes beneath OfflineKit:OwnedState:ExportVolumeRoot, including run outputs, audit/assurance bundles and other stored files

The run total is part of the volume total. Do not add them. The import CAS remains the separate volume:offlinekit-import-cas state, with its existing write guard. These new checks observe usage; they do not delete artifacts or refuse exports.

The Compose settings share OFFLINEKIT_EXPORT_RUN_STORAGE_ROOT between the web and worker roles, defaulting to /var/lib/stella/exportcenter/runs. OFFLINEKIT_EXPORT_VOLUME_ROOT supplies the web measurement root, defaulting to the existing /var/lib/stella/exportcenter mount. An operator changing either path must keep the run directory within the declared volume and mount the same storage in both roles. A directory declaration alone does not prove the mount.

A run subtree absent before the first export reports zero only while its parent volume exists. A missing volume, linked entry, unreadable subtree, root outside the volume, overflow or enumeration limit produces Warning with no byte total. Cancellation propagates. Measurement is not an atomic filesystem snapshot and does not include allocator overhead, deleted open files, or objects elsewhere.

Optional budgets use Doctor:State:<state-id>:BudgetBytes in a configuration file/provider. No deployment default is enabled. Size the whole volume with space for in-flight bundle assembly; size the run subset without adding it again to the volume allocation. A check becomes Warning at 80% and Critical at 100%.

Source e5baeeb09e and the live verification receipt confirm both observations, independent byte counts, 401/403/200 controls and unchanged migration ledgers on 2026-09-21. Re-verify the implementation with tools/scripts/test-targeted-xunit.ps1 -Project src/OfflineKit/__Tests/StellaOps.OfflineKit.WebService.Tests/StellaOps.OfflineKit.WebService.Tests.csproj -Class *OfflineKitExportOwnedStateTests* -BuildProjectReferences. The host adoption test composes a real PostgreSQL-backed host and verifies that an authenticated Doctor response contains the measured bytes and over-budget Critical result. For live acceptance, compare both response totals with file lengths from those exact mounted roots; retain 401/403/200 controls and source/image identity. An HTTP 200 alone is not proof that every check is healthy.