Skip to main content

acyclic_fs/
facade.rs

1//! Thin embedded `Fs`/`Volume`/`Checkout` composition over the two durable stores.
2
3use crate::async_storage::{AsyncAuthorityStore, AsyncObjectStore};
4use crate::cancellation::{CancellationError, CancellationToken};
5use crate::foundation::{
6    Digest, DurableCommit, Epoch, FileId, GenerationId, Head, OperationId, ProposedCommit,
7    Sequence, VolumeId,
8};
9#[cfg(all(feature = "local", not(target_arch = "wasm32")))]
10use crate::kernel::decode_retention_created;
11use crate::kernel::{
12    AsyncBlobSource, AttributeEntry, AttributeListError, AttributeListing, AttributeLookupError,
13    AttributeMutation, AttributeMutationError, AttributeName, AttributePage,
14    AuthenticatedGenerationProbe, AuthenticatedProbeError, BlobBuildError, BlobBuildOptions,
15    BlobReadError, CanonicalDecodeError, CheckoutDependencies, CheckpointError, CheckpointRequest,
16    ClosureError, ClosureLimits, DecodeLimits, Dependency, DependencyError, DependencyRegion,
17    DependencyState, DirectoryPage, DirectoryReadError, ExtentPlan, ExtentRangeRequest,
18    ExtentSeekRequest, ExtentSeekTarget, FileKind, FileMetadata, FileMutation, FilePayload,
19    FileRangeRead, FileRangeReadError, FileRangeRequest, FileRecord, FileRecordReadError,
20    FileTableMutationError, FileTablePage, GenerationExportManifest, GenerationMutationError,
21    GenerationRoot, GenerationTransferBatch, GenerationTransferError, InlineFileData,
22    InlineFileDataError, LivePublicationObservation, LiveRetryAction, LiveRetryError,
23    LiveRetryState, LogicalName, MAXIMUM_GENERATION_ROOT_BYTES, MergeConflictResolution,
24    MergeConflictSide, MergeGenerationError, MergeGenerationOutcome, MergeGenerationRequest,
25    MetadataField, Mutation, NameEncoding, NamespacePath, PathBatchLookup, PathLookup,
26    PathLookupError, PersistentDiffError, ProbeLimits, PublicationError, PublicationIntent,
27    PublicationReceipt, PublishGenerationRequest, RebaseConflict, RebaseDecision, RebaseError,
28    RegularMutation, RegularMutationError, RetentionCreated, RetentionCreatedError, RetentionKind,
29    TransferCursor, TreeMutationError, TreePage, VolumeCreated, VolumeCreatedError,
30    apply_attribute_mutations_async, apply_generation_mutations_retaining_async,
31    apply_regular_mutation_async, authenticate_generation_export_manifest_async, build_blob_async,
32    build_checkpoint_async, build_generation_export_manifest_async, classify_rebase_async,
33    decode_file_metadata, decode_published_generation, decode_volume_created,
34    decode_workspace_deleted, diff_file_records_async, diff_tree_entries_async,
35    encode_attribute_page, encode_file_metadata, encode_file_table_page, encode_generation_root,
36    encode_retention_created, encode_tree_page, encode_volume_created, encode_workspace_deleted,
37    export_generation_batch_async, generation_root_parent_count, import_generation_batch_async,
38    list_attributes_async, list_tree_entries_async, list_tree_entries_at_or_after_async,
39    lookup_attribute_async, lookup_file_record_async, lookup_file_records_async,
40    merge_generation_async, plan_extent_range_async, prove_generation_closure_async,
41    publish_generation_async, publish_generation_async_with_context,
42    publish_generation_async_with_permit, read_blob_range_async, read_file_range_async,
43    retention_authority_id, seek_extent_async, volume_authority_id,
44};
45#[cfg(test)]
46use crate::kernel::{
47    FileTableMutation, TreeMutation, file_table_mutation,
48    merge_directory_record_async as merge_directory_record, merge_file_fields, resolve_three,
49    tree_mutation,
50};
51pub use crate::kernel::{LiveMutationOutcome, MergeConflict};
52#[cfg(all(feature = "native-watch", not(target_arch = "wasm32")))]
53use crate::kernel::{decode_source_volume, source_authority_id};
54use crate::model::{
55    AccessMode, CaseSensitivity, CheckoutMode, CheckoutModeError, ConcurrencyMode, ConsistencyMode,
56    GenerationSelector, Lifecycle, MutationMode, UnicodePolicy, VolumeConfig, VolumeConfigError,
57    VolumeLimits,
58};
59use crate::performance::{
60    MeasuredResult, OperationFailure, OperationReceipt, WorkBudget, WorkCounters, WorkError,
61};
62use crate::storage::{
63    AppendOutcome, AuthorityStoreError, ByteRange, CreateAuthorityOutcome, FenceOutcome,
64    OBJECT_DIGEST_ENVELOPE_BYTES, ObjectId, ObjectKind, ObjectReadRequest, ObjectReadRetention,
65    ObjectStoreError, PublicationPermit, ReplayLimit, object_digest,
66};
67#[cfg(all(feature = "local", not(target_arch = "wasm32")))]
68use acyclic_native_runtime::OwnershipAnchor;
69#[cfg(all(feature = "local", not(target_arch = "wasm32")))]
70use acyclic_objects::ObjectsProvider as _;
71use bytes::Bytes;
72use futures::{StreamExt as _, stream};
73use std::collections::{BTreeMap, BTreeSet, VecDeque};
74use std::mem::size_of;
75#[cfg(all(feature = "local", not(target_arch = "wasm32")))]
76use std::path::{Path, PathBuf};
77use std::sync::Arc;
78#[cfg(all(feature = "local", not(target_arch = "wasm32")))]
79use std::sync::{OnceLock, Weak};
80use thiserror::Error;
81
82const MAXIMUM_VOLUME_EVENT_BYTES: u64 = 4 * 1024;
83const STAGED_METADATA_CACHE_ENTRIES: usize = 16;
84
85struct AuthoredMetadataCache {
86    entries: [Option<(FileMetadata, ObjectId)>; STAGED_METADATA_CACHE_ENTRIES],
87    next: usize,
88}
89
90impl AuthoredMetadataCache {
91    fn new() -> Self {
92        Self {
93            entries: [None; STAGED_METADATA_CACHE_ENTRIES],
94            next: 0,
95        }
96    }
97
98    fn get(&self, metadata: FileMetadata) -> Option<ObjectId> {
99        self.entries
100            .iter()
101            .flatten()
102            .find_map(|(cached, object)| (*cached == metadata).then_some(*object))
103    }
104
105    fn remember(&mut self, metadata: FileMetadata, object: ObjectId) {
106        if let Some(slot) = self.entries.get_mut(self.next) {
107            *slot = Some((metadata, object));
108        }
109        self.next = (self.next + 1) % STAGED_METADATA_CACHE_ENTRIES;
110    }
111}
112/// Directory-listing page size used to scan siblings for a case-folded
113/// collision under `CaseSensitivity::ProfileFolded`. This makes admitting a
114/// new name under that policy O(directory size) rather than O(1); the tree's
115/// physical page ordering is exact-byte (see `kernel::tree`), so folded
116/// comparison cannot be a binary search over it.
117const CASE_FOLD_COLLISION_SCAN_PAGE: u32 = 256;
118const ROOT_FILE_DOMAIN: &[u8] = b"acyclic-fs-root-file-v1\0";
119const CREATION_OPERATION_DOMAIN: &[u8] = b"acyclic-fs-create-operation-v1\0";
120const CREATION_FINGERPRINT_DOMAIN: &[u8] = b"acyclic-fs-create-fingerprint-v1\0";
121const WORKSPACE_JOIN_OPERATION_DOMAIN: &[u8] = b"acyclic-fs-workspace-join-operation-v1\0";
122
123pub(crate) enum WorkspaceJoinOutcome {
124    Applied(GenerationId),
125    AlreadyApplied(GenerationId),
126    NoChanges(GenerationId),
127    Stale(GenerationId),
128    Conflicted(Vec<MergeConflict>, bool),
129    Fenced,
130    IdempotencyConflict,
131}
132
133pub(crate) struct WorkspaceJoinRequest<'a, A, O> {
134    pub target: &'a Volume<A, O>,
135    pub base: WorkspaceCommonAncestor,
136    pub source: &'a crate::Generation<A, O>,
137    pub expected_target: GenerationId,
138    pub expected_head: Head,
139    pub history: crate::workspace::JoinHistory,
140    pub operation_id: OperationId,
141    pub permit: PublicationPermit,
142    pub maximum_generations: u32,
143    pub maximum_changes: u32,
144    pub maximum_conflicts: u32,
145    pub resolutions: BTreeMap<MergeConflict, MergeConflictResolution>,
146}
147
148impl<A, O> WorkspaceJoinRequest<'_, A, O> {
149    fn operation_context(&self) -> Digest {
150        let mut hasher = blake3::Hasher::new();
151        hasher.update(WORKSPACE_JOIN_OPERATION_DOMAIN);
152        hasher.update(&self.source.workspace.volume.id.into_bytes());
153        hasher.update(self.source.id.digest().as_bytes());
154        hasher.update(&self.target.id.into_bytes());
155        hasher.update(self.expected_target.digest().as_bytes());
156        hasher.update(&self.base.volume_id.into_bytes());
157        hasher.update(self.base.id.digest().as_bytes());
158        hasher.update(&[match self.history {
159            crate::workspace::JoinHistory::Merge => 1,
160            crate::workspace::JoinHistory::Rebase => 2,
161            crate::workspace::JoinHistory::Squash => 3,
162            crate::workspace::JoinHistory::CherryPick => 4,
163        }]);
164        hasher.update(&self.maximum_generations.to_le_bytes());
165        hasher.update(&self.maximum_changes.to_le_bytes());
166        hasher.update(&self.maximum_conflicts.to_le_bytes());
167        for (conflict, resolution) in &self.resolutions {
168            match conflict {
169                MergeConflict::File(file_id) => {
170                    hasher.update(&[0]);
171                    hasher.update(&file_id.into_bytes());
172                }
173                MergeConflict::Binding { directory_id, name } => {
174                    hasher.update(&[1]);
175                    hasher.update(&directory_id.into_bytes());
176                    hasher.update(&[match name.encoding() {
177                        NameEncoding::Utf8 => 0,
178                        NameEncoding::PosixBytes => 1,
179                        NameEncoding::WindowsUtf16Le => 2,
180                    }]);
181                    hasher.update(&(name.as_bytes().len() as u64).to_le_bytes());
182                    hasher.update(name.as_bytes());
183                }
184            }
185            hash_merge_resolution(&mut hasher, resolution);
186        }
187        Digest::from_bytes(*hasher.finalize().as_bytes())
188    }
189}
190
191fn hash_merge_resolution(hasher: &mut blake3::Hasher, resolution: &MergeConflictResolution) {
192    let hash_side = |hasher: &mut blake3::Hasher, side: MergeConflictSide| {
193        hasher.update(&[match side {
194            MergeConflictSide::Base => 0,
195            MergeConflictSide::Ours => 1,
196            MergeConflictSide::Theirs => 2,
197        }]);
198    };
199    match resolution {
200        MergeConflictResolution::Select(side) => {
201            hasher.update(&[0]);
202            hash_side(hasher, *side);
203        }
204        MergeConflictResolution::File(record) => {
205            hasher.update(&[1]);
206            if let Some(record) = record {
207                hasher.update(&[1]);
208                hasher.update(&record.file_id.into_bytes());
209                hasher.update(&[match record.kind {
210                    FileKind::Regular => 0,
211                    FileKind::Directory => 1,
212                    FileKind::SymbolicLink => 2,
213                    FileKind::Fifo => 3,
214                    FileKind::Socket => 4,
215                    FileKind::CharacterDevice => 5,
216                    FileKind::BlockDevice => 6,
217                    FileKind::ReparsePoint => 7,
218                    FileKind::MountBoundary => 8,
219                }]);
220                hasher.update(&record.link_count.to_le_bytes());
221                hash_object_id(hasher, record.metadata);
222                hash_file_payload(hasher, record.payload);
223            } else {
224                hasher.update(&[0]);
225            }
226        }
227        MergeConflictResolution::Binding(file_id) => {
228            hasher.update(&[2]);
229            if let Some(file_id) = file_id {
230                hasher.update(&[1]);
231                hasher.update(&file_id.into_bytes());
232            } else {
233                hasher.update(&[0]);
234            }
235        }
236    }
237}
238
239fn hash_object_id(hasher: &mut blake3::Hasher, object: ObjectId) {
240    hasher.update(&[object.kind.canonical_tag()]);
241    hasher.update(object.digest.as_bytes());
242}
243
244fn hash_file_payload(hasher: &mut blake3::Hasher, payload: FilePayload) {
245    match payload {
246        FilePayload::InlineRegular(data) => {
247            hasher.update(&[0]);
248            hasher.update(&(data.as_bytes().len() as u64).to_le_bytes());
249            hasher.update(data.as_bytes());
250        }
251        FilePayload::Regular {
252            logical_bytes,
253            extents,
254        } => {
255            hasher.update(&[1]);
256            hasher.update(&logical_bytes.to_le_bytes());
257            hash_object_id(hasher, extents);
258        }
259        FilePayload::Directory { entries } => {
260            hasher.update(&[2]);
261            hash_object_id(hasher, entries);
262        }
263        FilePayload::SymbolicLink {
264            target_bytes,
265            target,
266        } => {
267            hasher.update(&[3]);
268            hasher.update(&target_bytes.to_le_bytes());
269            hash_object_id(hasher, target);
270        }
271        FilePayload::Empty => {
272            hasher.update(&[4]);
273        }
274        FilePayload::Device { major, minor } => {
275            hasher.update(&[5]);
276            hasher.update(&major.to_le_bytes());
277            hasher.update(&minor.to_le_bytes());
278        }
279        FilePayload::ReparsePoint {
280            payload_bytes,
281            payload,
282        } => {
283            hasher.update(&[6]);
284            hasher.update(&payload_bytes.to_le_bytes());
285            hash_object_id(hasher, payload);
286        }
287    }
288}
289
290#[derive(Clone, Copy)]
291pub(crate) struct WorkspaceCommonAncestor {
292    pub id: GenerationId,
293    pub volume_id: VolumeId,
294}
295
296pub(crate) struct WorkspaceRebaseRequest<'a, A, O> {
297    pub target: &'a Volume<A, O>,
298    pub operation_id: OperationId,
299    pub maximum_generations: u32,
300    pub maximum_changes: u32,
301    pub maximum_conflicts: u32,
302}
303
304/// Capabilities that cannot be inferred from storage trait syntax alone.
305#[derive(Clone, Copy, Debug, Eq, PartialEq)]
306pub struct EmbeddedCapabilities {
307    /// Both authority and object acknowledgement survive backend restart.
308    pub durable: bool,
309}
310
311impl EmbeddedCapabilities {
312    /// Deterministic process-local memory profile.
313    pub const MEMORY: Self = Self { durable: false };
314}
315
316/// Bounded self-contained local backend options.
317#[cfg(all(feature = "local", not(target_arch = "wasm32")))]
318#[derive(Clone, Debug, Eq, PartialEq)]
319pub struct LocalOptions {
320    /// Durable root containing canonical Stream and Objects provider state.
321    pub root: PathBuf,
322    /// Durable hierarchical Stream provider limits.
323    pub stream: acyclic_stream::LocalStreamLimits,
324    /// Durable immutable Objects provider limits.
325    pub objects: acyclic_objects::LocalObjectsLimits,
326    /// Bounded disposable immutable-object accelerator shared by every handle.
327    pub object_cache: crate::cache::ObjectCacheOptions,
328}
329
330/// Bounded reclamation result for the canonical local Stream/Objects composition.
331#[cfg(all(feature = "local", not(target_arch = "wasm32")))]
332#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
333pub struct LocalGarbageCollection {
334    /// Immutable filesystem object versions examined.
335    pub examined: u64,
336    /// Unreachable immutable filesystem object versions removed.
337    pub removed: u64,
338    /// Physical body segments removed after logical reclamation.
339    pub segments_removed: u64,
340    /// Crash-left temporary body publications removed.
341    pub temporary_files_removed: u64,
342}
343
344#[cfg(all(feature = "local", not(target_arch = "wasm32")))]
345impl LocalOptions {
346    /// Creates local options with the SDK's bounded backend defaults.
347    #[must_use]
348    pub fn new(root: impl Into<PathBuf>) -> Self {
349        Self {
350            root: root.into(),
351            stream: acyclic_stream::LocalStreamLimits::default(),
352            objects: acyclic_objects::LocalObjectsLimits::default(),
353            object_cache: crate::cache::ObjectCacheOptions::default(),
354        }
355    }
356}
357
358struct FsInner<A, O> {
359    authority: A,
360    objects: O,
361    capabilities: EmbeddedCapabilities,
362    workspace_namespace: [u8; 16],
363    path_index: Arc<dyn crate::path_index::GenerationPathIndex>,
364    // This field must remain last: Rust drops fields in declaration order, so every durable
365    // provider and index is gone before a replacement local engine may acquire the root.
366    #[cfg(all(feature = "local", not(target_arch = "wasm32")))]
367    _local_root_lifecycle: Option<OwnershipAnchor>,
368}
369
370#[cfg(all(feature = "local", not(target_arch = "wasm32")))]
371struct LocalRootRegistration {
372    options: LocalOptions,
373    live: Weak<FsInner<LocalAuthorityBackend, LocalObjectBackend>>,
374    opening: Arc<tokio::sync::Mutex<()>>,
375    lifecycle: Arc<tokio::sync::Mutex<()>>,
376}
377
378#[cfg(all(feature = "local", not(target_arch = "wasm32")))]
379type LocalRootRegistry = std::collections::BTreeMap<PathBuf, LocalRootRegistration>;
380
381#[cfg(all(feature = "local", not(target_arch = "wasm32")))]
382fn prepare_local_root_open(
383    registry: &mut LocalRootRegistry,
384    root: &Path,
385    options: &LocalOptions,
386) -> Arc<tokio::sync::Mutex<()>> {
387    if let Some(registration) = registry.get(root) {
388        return Arc::clone(&registration.opening);
389    }
390    let opening = Arc::new(tokio::sync::Mutex::new(()));
391    let lifecycle = Arc::new(tokio::sync::Mutex::new(()));
392    registry.insert(
393        root.to_path_buf(),
394        LocalRootRegistration {
395            options: options.clone(),
396            live: Weak::new(),
397            opening: Arc::clone(&opening),
398            lifecycle: Arc::clone(&lifecycle),
399        },
400    );
401    opening
402}
403
404#[cfg(all(feature = "local", not(target_arch = "wasm32")))]
405async fn run_local_initialization<T, F, I>(
406    ownership: tokio::sync::OwnedMutexGuard<()>,
407    initialize: I,
408) -> Result<T, FsError>
409where
410    T: Send + 'static,
411    F: std::future::Future<Output = T> + Send + 'static,
412    I: FnOnce(tokio::sync::OwnedMutexGuard<()>) -> F + Send + 'static,
413{
414    // Tokio detaches a spawned task when its JoinHandle is dropped. That is intentional here:
415    // cancelling the caller must not release root ownership while provider spawn_blocking workers
416    // can still recover or mutate a journal.
417    tokio::spawn(async move { initialize(ownership).await })
418        .await
419        .map_err(|_| FsError::LocalInitializationWorker)
420}
421
422/// Embedded filesystem composition handle.
423pub struct Fs<A, O> {
424    inner: Arc<FsInner<A, O>>,
425}
426
427impl<A, O> Clone for Fs<A, O> {
428    fn clone(&self) -> Self {
429        Self {
430            inner: Arc::clone(&self.inner),
431        }
432    }
433}
434
435impl<A, O> Fs<A, O> {
436    /// Deployment guarantees supplied when this engine was composed.
437    #[must_use]
438    pub fn capabilities(&self) -> EmbeddedCapabilities {
439        self.inner.capabilities
440    }
441
442    /// Derives the deployment-scoped stable identity for one canonical name.
443    ///
444    /// # Errors
445    ///
446    /// Rejects an invalid workspace name without storage access.
447    pub fn workspace_id(
448        &self,
449        name: impl AsRef<str>,
450    ) -> Result<crate::WorkspaceId, crate::WorkspaceNameError> {
451        let name = crate::WorkspaceName::new(name)?;
452        Ok(crate::WorkspaceId::derive(
453            self.inner.workspace_namespace,
454            &name,
455        ))
456    }
457
458    pub(crate) fn same_deployment(&self, other: &Self) -> bool {
459        Arc::ptr_eq(&self.inner, &other.inner)
460    }
461
462    pub(crate) fn path_index(&self) -> &dyn crate::path_index::GenerationPathIndex {
463        &*self.inner.path_index
464    }
465
466    pub(crate) fn authority(&self) -> &A {
467        &self.inner.authority
468    }
469
470    #[cfg(all(test, feature = "s3-http"))]
471    pub(crate) fn objects(&self) -> &O {
472        &self.inner.objects
473    }
474}
475
476impl<P, O> Fs<crate::StreamAuthorityStore<P>, O> {
477    /// Opens the SDK operation-window store on the exact Stream provider used
478    /// by generation authority. This co-location is what makes lease fencing
479    /// atomic with publication.
480    #[must_use]
481    pub fn operation_window_store(&self) -> crate::StreamOperationWindowStore<P> {
482        crate::StreamOperationWindowStore::new(self.inner.authority.provider())
483    }
484}
485
486/// One independently configured volume handle reconstructed from authority.
487pub struct Volume<A, O> {
488    pub(crate) fs: Fs<A, O>,
489    pub(crate) id: VolumeId,
490    pub(crate) config: VolumeConfig,
491}
492
493impl<A, O> Clone for Volume<A, O> {
494    fn clone(&self) -> Self {
495        Self {
496            fs: self.fs.clone(),
497            id: self.id,
498            config: self.config,
499        }
500    }
501}
502
503/// Immutable generation-fenced checkout admitted by the embedded facade.
504pub struct Checkout<A, O> {
505    volume: Volume<A, O>,
506    base_generation_root: ObjectId,
507    generation_root: ObjectId,
508    base_file_table: ObjectId,
509    base_root: GenerationRoot,
510    root: GenerationRoot,
511    authority_head: Option<Head>,
512    authored_operation_id: Option<OperationId>,
513    live_operation_id: Option<OperationId>,
514    last_commit: Option<LastCommit>,
515    prepared_merge_parent: Option<ObjectId>,
516    dependencies: CheckoutDependencies,
517    mode: CheckoutMode,
518}
519
520/// Cheap immutable reader over one authenticated pinned checkout candidate.
521///
522/// Clones share the underlying stores and immutable generation root. Reads do
523/// not mutate checkout dependency state, so independent requests may overlap.
524pub struct PinnedReader<A, O> {
525    volume: Volume<A, O>,
526    generation_root: ObjectId,
527    root: GenerationRoot,
528}
529
530/// Cheap cloneable writer for immutable authenticated file content.
531///
532/// Staging never mutates a checkout candidate, so independent native or
533/// network sources can overlap before one ordered mutation transaction.
534pub struct ContentStager<A, O> {
535    volume: Volume<A, O>,
536}
537
538impl<A, O> Clone for ContentStager<A, O> {
539    fn clone(&self) -> Self {
540        Self {
541            volume: self.volume.clone(),
542        }
543    }
544}
545
546impl<A, O> Clone for PinnedReader<A, O> {
547    fn clone(&self) -> Self {
548        Self {
549            volume: self.volume.clone(),
550            generation_root: self.generation_root,
551            root: self.root.clone(),
552        }
553    }
554}
555
556/// Public facts needed to plan reads without exposing generation-bound records.
557#[derive(Clone, Debug, Eq, PartialEq)]
558pub struct FileDescription {
559    /// Terminal entry kind.
560    pub kind: FileKind,
561    /// Logical content length for regular files and symbolic links.
562    pub logical_bytes: u64,
563    /// Canonical metadata authenticated with the same pinned generation.
564    pub metadata: FileMetadata,
565}
566
567/// One file resolved and authenticated by a specific pinned reader.
568///
569/// The private record and reader borrow make cross-generation use impossible.
570pub struct ResolvedFile<A, O> {
571    reader: PinnedReader<A, O>,
572    record: FileRecord,
573    description: FileDescription,
574}
575
576impl<A, O> ResolvedFile<A, O> {
577    /// Stable file identity authenticated with this handle.
578    #[must_use]
579    pub const fn file_id(&self) -> FileId {
580        self.record.file_id
581    }
582
583    /// Public semantic facts authenticated with this handle.
584    #[must_use]
585    pub const fn description(&self) -> &FileDescription {
586        &self.description
587    }
588}
589
590impl<A: AsyncAuthorityStore, O: AsyncObjectStore> ResolvedFile<A, O> {
591    /// Reads one logical regular-file range without another namespace lookup.
592    pub async fn read_range(
593        &self,
594        range: ByteRange,
595        budget: WorkBudget,
596        cancellation: &CancellationToken,
597    ) -> FsResult<FileRangeRead> {
598        self.reader
599            .read_record_range(self.record, range, budget, cancellation)
600            .await
601    }
602
603    /// Reads opaque symbolic-link target bytes without another namespace lookup.
604    pub async fn read_symbolic_link(
605        &self,
606        budget: WorkBudget,
607        cancellation: &CancellationToken,
608    ) -> FsResult<Bytes> {
609        self.reader
610            .read_symbolic_link_record(self.record, budget, cancellation)
611            .await
612    }
613
614    /// Plans sparse physical spans without another namespace lookup.
615    pub async fn plan_extents(
616        &self,
617        range: ByteRange,
618        maximum_spans: u32,
619        budget: WorkBudget,
620        cancellation: &CancellationToken,
621    ) -> FsResult<Option<ExtentPlan>> {
622        self.reader
623            .plan_record_extents(self.record, range, maximum_spans, budget, cancellation)
624            .await
625    }
626}
627
628/// One range requested from a reader-scoped resolved file.
629pub struct ResolvedFileRangeReadRequest<'a, A, O> {
630    /// Exact resolved file handle.
631    pub file: &'a ResolvedFile<A, O>,
632    /// Exact logical byte range returned.
633    pub range: ByteRange,
634}
635
636/// One child returned from an authenticated directory page, resolved by the
637/// originating pinned reader without a second namespace traversal.
638pub struct ResolvedDirectoryEntry<A, O> {
639    /// Exact canonical child name.
640    pub name: LogicalName,
641    /// Generation-scoped child handle with authenticated metadata.
642    pub file: ResolvedFile<A, O>,
643}
644
645/// One bounded authenticated directory page with reader-scoped child handles.
646pub struct ResolvedDirectoryPage<A, O> {
647    /// Ordered children strictly after the supplied cursor.
648    pub entries: Vec<ResolvedDirectoryEntry<A, O>>,
649    /// Whether at least one additional child exists.
650    pub has_more: bool,
651}
652
653#[derive(Clone, Copy)]
654struct LastCommit {
655    operation_id: OperationId,
656    generation_id: GenerationId,
657    head: Head,
658}
659
660#[derive(Clone, Copy)]
661struct VolumeCreation {
662    volume_id: VolumeId,
663    config: VolumeConfig,
664    generation_root: ObjectId,
665    operation_id: Option<OperationId>,
666}
667
668#[derive(Clone, Copy)]
669struct VerifiedForkSource {
670    generation_root: ObjectId,
671}
672
673/// Durable local authority backend with nonblocking native storage dispatch.
674#[cfg(all(feature = "local", not(target_arch = "wasm32")))]
675pub type LocalAuthorityBackend =
676    crate::distributed::StreamAuthorityStore<acyclic_stream::LocalStream>;
677
678/// Durable local immutable-object backend with nonblocking native storage dispatch.
679#[cfg(all(feature = "local", not(target_arch = "wasm32")))]
680pub type LocalObjectBackend = crate::staged_objects::StagedObjects<
681    crate::cache::CachedObjectStore<
682        crate::distributed::ProviderObjectStore<acyclic_objects::LocalObjects>,
683    >,
684>;
685
686/// Durable local filesystem composition with nonblocking native storage dispatch.
687#[cfg(all(feature = "local", not(target_arch = "wasm32")))]
688pub type LocalFs = Fs<LocalAuthorityBackend, LocalObjectBackend>;
689
690/// Durable operation-window store sharing the local filesystem's authority
691/// stream. Leases from any other store cannot authorize local publication.
692#[cfg(all(feature = "local", not(target_arch = "wasm32")))]
693pub type LocalOperationWindowStore = crate::StreamOperationWindowStore<acyclic_stream::LocalStream>;
694
695/// Durable local volume handle with nonblocking native storage dispatch.
696#[cfg(all(feature = "local", not(target_arch = "wasm32")))]
697pub type LocalVolume = Volume<LocalAuthorityBackend, LocalObjectBackend>;
698
699/// Deterministic in-process composition over the exact public Stream and Objects providers.
700#[cfg(all(feature = "memory", feature = "distributed"))]
701pub type MemoryFs = Fs<MemoryAuthorityBackend, MemoryObjectBackend>;
702
703#[cfg(all(feature = "memory", feature = "distributed"))]
704/// Filesystem authority adapter backed by the public in-memory Stream provider.
705pub type MemoryAuthorityBackend =
706    crate::distributed::StreamAuthorityStore<acyclic_stream::MemoryStream>;
707
708#[cfg(all(feature = "memory", feature = "distributed"))]
709/// Filesystem object adapter backed by the public in-memory Objects provider.
710pub type MemoryObjectBackend =
711    crate::distributed::ProviderObjectStore<acyclic_objects::MemoryObjects>;
712
713#[cfg(all(test, feature = "memory", feature = "distributed"))]
714pub(crate) type MemoryCheckout = Checkout<MemoryAuthorityBackend, MemoryObjectBackend>;
715
716/// Ephemeral path-independent regular file retained by an open native handle.
717///
718/// Detached files share immutable objects with their originating volume but
719/// are never published into a generation. They exist only until the final
720/// native handle closes, matching POSIX last-unlink lifetime semantics.
721pub struct DetachedFile<A, O> {
722    volume: Volume<A, O>,
723    record: FileRecord,
724}
725
726/// Successful facade operation with exact composed work.
727pub type FsReceipt<T> = OperationReceipt<T>;
728
729/// One within-volume zero-copy logical range clone request.
730#[derive(Clone, Debug, Eq, PartialEq)]
731pub struct FileCloneRequest {
732    /// Existing source regular file.
733    pub source: NamespacePath,
734    /// Inclusive source byte offset.
735    pub source_offset: u64,
736    /// Existing destination regular file.
737    pub destination: NamespacePath,
738    /// Inclusive destination byte offset.
739    pub destination_offset: u64,
740    /// Positive logical byte count.
741    pub length: u64,
742}
743
744/// One caller-authored filesystem operation compiled into a single sparse,
745/// atomic checkout transaction.
746#[derive(Clone, Debug, Eq, PartialEq)]
747pub enum AuthoredMutation {
748    /// Rebinds an existing non-directory object to a stable source identity.
749    Reidentify {
750        /// Existing namespace path.
751        path: NamespacePath,
752        /// Stable path-independent identity to retain.
753        file_id: FileId,
754    },
755    /// Creates one regular file with exact initial bytes and metadata.
756    CreateFile {
757        /// New namespace path.
758        path: NamespacePath,
759        /// Complete initial bytes.
760        bytes: Bytes,
761        /// Exact cross-profile metadata.
762        metadata: FileMetadata,
763    },
764    /// Creates one regular file from already staged authenticated content.
765    CreateFileFromContent {
766        /// New namespace path.
767        path: NamespacePath,
768        /// Authenticated streamed content.
769        content: StagedContent,
770        /// Exact cross-profile metadata.
771        metadata: FileMetadata,
772        /// Optional stable source identity; the engine rejects an existing ID.
773        file_id: Option<FileId>,
774    },
775    /// Creates one empty directory.
776    CreateDirectory {
777        /// New namespace path.
778        path: NamespacePath,
779        /// Exact cross-profile metadata.
780        metadata: FileMetadata,
781    },
782    /// Creates one symbolic link with opaque target bytes.
783    CreateSymbolicLink {
784        /// New namespace path.
785        path: NamespacePath,
786        /// Exact uninterpreted target bytes.
787        target: Bytes,
788        /// Exact cross-profile metadata.
789        metadata: FileMetadata,
790    },
791    /// Creates one payload-free FIFO, socket, or mount boundary.
792    CreateEmptySpecial {
793        /// New namespace path.
794        path: NamespacePath,
795        /// Exact payload-free kind.
796        kind: FileKind,
797        /// Exact cross-profile metadata.
798        metadata: FileMetadata,
799    },
800    /// Creates one POSIX character or block device.
801    CreateDevice {
802        /// New namespace path.
803        path: NamespacePath,
804        /// Character or block device kind.
805        kind: FileKind,
806        /// Native major identity.
807        major: u32,
808        /// Native minor identity.
809        minor: u32,
810        /// Exact cross-profile metadata.
811        metadata: FileMetadata,
812    },
813    /// Creates one opaque Windows reparse point.
814    CreateReparsePoint {
815        /// New namespace path.
816        path: NamespacePath,
817        /// Opaque reparse payload.
818        payload: Bytes,
819        /// Exact cross-profile metadata.
820        metadata: FileMetadata,
821    },
822    /// Removes one namespace binding.
823    Remove {
824        /// Existing namespace path.
825        path: NamespacePath,
826        /// Optional exact path-independent identity precondition.
827        expected_file_id: Option<FileId>,
828    },
829    /// Atomically moves one binding within the volume.
830    Rename {
831        /// Existing source path.
832        source: NamespacePath,
833        /// Destination path.
834        destination: NamespacePath,
835        /// Whether an existing destination may be replaced.
836        replace: bool,
837    },
838    /// Creates a same-volume hard link.
839    HardLink {
840        /// Existing source path.
841        source: NamespacePath,
842        /// New destination path.
843        destination: NamespacePath,
844    },
845    /// Replaces one regular-file range.
846    Write {
847        /// Existing regular-file path.
848        path: NamespacePath,
849        /// Inclusive logical offset.
850        offset: u64,
851        /// Exact replacement bytes.
852        bytes: Bytes,
853    },
854    /// Replaces one range from already staged authenticated content.
855    WriteFromContent {
856        /// Existing regular-file path.
857        path: NamespacePath,
858        /// Inclusive logical offset.
859        offset: u64,
860        /// Authenticated streamed content.
861        content: StagedContent,
862    },
863    /// Replaces complete cross-profile metadata.
864    SetMetadata {
865        /// Existing path.
866        path: NamespacePath,
867        /// Exact replacement metadata.
868        metadata: FileMetadata,
869    },
870    /// Changes logical regular-file length.
871    Resize {
872        /// Existing regular-file path.
873        path: NamespacePath,
874        /// New logical byte length.
875        logical_bytes: u64,
876    },
877    /// Punches a hole or installs physically allocated zeros.
878    ZeroRange {
879        /// Existing regular-file path.
880        path: NamespacePath,
881        /// Exact logical range.
882        range: ByteRange,
883        /// Preserve physical allocation when true.
884        allocated: bool,
885        /// Permit logical file growth to the range end.
886        extend: bool,
887    },
888    /// Allocates holes without replacing existing content.
889    Preallocate {
890        /// Existing regular-file path.
891        path: NamespacePath,
892        /// Exact allocation range.
893        range: ByteRange,
894        /// Preserve logical file length.
895        keep_size: bool,
896    },
897    /// Clones an immutable logical range without reading content bytes.
898    CloneRange(FileCloneRequest),
899}
900
901fn authored_mutation_operation_count(mutation: &AuthoredMutation) -> usize {
902    match mutation {
903        AuthoredMutation::CreateFile { bytes, .. } => {
904            usize::from(bytes.len() > crate::kernel::MAXIMUM_INLINE_FILE_BYTES) + 1
905        }
906        AuthoredMutation::CreateFileFromContent { content, .. } => {
907            usize::from(content.logical_bytes != 0) + 1
908        }
909        AuthoredMutation::Reidentify { .. }
910        | AuthoredMutation::Write { .. }
911        | AuthoredMutation::WriteFromContent { .. }
912        | AuthoredMutation::CreateDirectory { .. }
913        | AuthoredMutation::CreateSymbolicLink { .. }
914        | AuthoredMutation::CreateEmptySpecial { .. }
915        | AuthoredMutation::CreateDevice { .. }
916        | AuthoredMutation::CreateReparsePoint { .. }
917        | AuthoredMutation::Remove { .. }
918        | AuthoredMutation::Rename { .. }
919        | AuthoredMutation::HardLink { .. }
920        | AuthoredMutation::SetMetadata { .. }
921        | AuthoredMutation::Resize { .. }
922        | AuthoredMutation::ZeroRange { .. }
923        | AuthoredMutation::Preallocate { .. }
924        | AuthoredMutation::CloneRange(_) => 1,
925    }
926}
927
928fn pending_name_collision(
929    path: &NamespacePath,
930    pending: &[(NamespacePath, FileId)],
931    allowed_file_ids: &[FileId],
932) -> bool {
933    let Some((parent, name)) = path.split_last() else {
934        return false;
935    };
936    let folded = name.case_fold_key();
937    pending.iter().any(|(pending_path, file_id)| {
938        let Some((pending_parent, pending_name)) = pending_path.split_last() else {
939            return false;
940        };
941        pending_parent == parent
942            && pending_name != name
943            && pending_name.case_fold_key() == folded
944            && !allowed_file_ids.contains(file_id)
945    })
946}
947
948fn pending_binding_path(
949    path: &NamespacePath,
950    pending: &[(NamespacePath, FileId)],
951    case_sensitivity: CaseSensitivity,
952) -> Option<(NamespacePath, FileId)> {
953    let (parent, name) = path.split_last()?;
954    pending.iter().rev().find_map(|(pending_path, file_id)| {
955        let (pending_parent, pending_name) = pending_path.split_last()?;
956        let same_name = match case_sensitivity {
957            CaseSensitivity::Sensitive => pending_name == name,
958            CaseSensitivity::ProfileFolded => pending_name.case_fold_key() == name.case_fold_key(),
959        };
960        (pending_parent == parent && same_name).then_some((pending_path.clone(), *file_id))
961    })
962}
963
964fn pending_binding_id(
965    path: &NamespacePath,
966    pending: &[(NamespacePath, FileId)],
967    case_sensitivity: CaseSensitivity,
968) -> Option<FileId> {
969    pending_binding_path(path, pending, case_sensitivity).map(|(_, file_id)| file_id)
970}
971
972fn pending_parent_exists(
973    path: &NamespacePath,
974    pending: &[(NamespacePath, FileId)],
975    case_sensitivity: CaseSensitivity,
976    limits: VolumeLimits,
977) -> bool {
978    let Some((parent, _)) = path.split_last() else {
979        return false;
980    };
981    let parent_path = NamespacePath::new(parent.to_vec(), limits);
982    parent_path
983        .ok()
984        .and_then(|value| pending_binding_id(&value, pending, case_sensitivity))
985        .is_some()
986}
987
988fn removed_binding_id(path: &NamespacePath, removed: &[(NamespacePath, FileId)]) -> Option<FileId> {
989    let (parent, name) = path.split_last()?;
990    let folded = name.case_fold_key();
991    removed.iter().rev().find_map(|(removed_path, file_id)| {
992        let (removed_parent, removed_name) = removed_path.split_last()?;
993        (removed_parent == parent && removed_name.case_fold_key() == folded).then_some(*file_id)
994    })
995}
996
997/// Stable result positions for an authored transaction.
998#[derive(Clone, Debug, Eq, PartialEq)]
999pub struct AuthoredTransactionResult {
1000    /// One entry per authored operation; create operations contain their new
1001    /// path-independent identity and all other operations contain `None`.
1002    pub created_file_ids: Vec<Option<FileId>>,
1003}
1004
1005/// Stable result positions plus the terminal outcome of one authored live transaction.
1006#[derive(Clone, Debug, Eq, PartialEq)]
1007pub struct AuthoredLiveMutationResult {
1008    /// One entry per authored operation; create operations contain their new
1009    /// path-independent identity and all other operations contain `None`.
1010    pub created_file_ids: Vec<Option<FileId>>,
1011    /// Terminal direct-live publication outcome.
1012    pub outcome: LiveMutationOutcome,
1013}
1014
1015/// Authenticated content staged from a bounded stream without materializing the
1016/// complete file in memory.
1017#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1018pub struct StagedContent {
1019    /// Root of the immutable chunk/index closure.
1020    root: ObjectId,
1021    /// Exact logical source length.
1022    logical_bytes: u64,
1023}
1024
1025impl StagedContent {
1026    #[cfg(feature = "s3-http")]
1027    pub(crate) const fn from_canonical_parts(root: ObjectId, logical_bytes: u64) -> Self {
1028        Self {
1029            root,
1030            logical_bytes,
1031        }
1032    }
1033
1034    /// Authenticated immutable blob-root identity created by this SDK.
1035    #[must_use]
1036    pub const fn root(&self) -> ObjectId {
1037        self.root
1038    }
1039
1040    /// Exact staged logical byte length authenticated by the blob build.
1041    #[must_use]
1042    pub const fn logical_bytes(&self) -> u64 {
1043        self.logical_bytes
1044    }
1045}
1046
1047/// One directory entry paired with its path-independent authenticated record.
1048#[derive(Clone, Debug, Eq, PartialEq)]
1049pub struct DirectoryRecordEntry {
1050    /// Exact canonical name.
1051    pub name: LogicalName,
1052    /// Complete child file record from the shared file-table frontier.
1053    pub record: FileRecord,
1054    /// Complete authenticated metadata referenced by `record`.
1055    pub metadata: FileMetadata,
1056}
1057
1058/// One page-efficient directory listing for native projection callbacks.
1059#[derive(Clone, Debug, Eq, PartialEq)]
1060pub struct DirectoryRecordPage {
1061    /// Ordered entries strictly after the supplied cursor.
1062    pub entries: Vec<DirectoryRecordEntry>,
1063    /// Whether at least one additional entry exists.
1064    pub has_more: bool,
1065}
1066
1067/// One bounded directory binding page requested from a [`PinnedReader`].
1068#[derive(Clone, Debug, Eq, PartialEq)]
1069pub struct DirectoryPageRequest {
1070    /// Exact directory namespace path.
1071    pub path: NamespacePath,
1072    /// Exclusive canonical name cursor.
1073    pub after: Option<LogicalName>,
1074    /// Maximum entries returned for this page.
1075    pub maximum_entries: u32,
1076}
1077
1078/// One present no-follow path result with authenticated metadata decoded in
1079/// the same operation. `None` represents authenticated absence.
1080#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1081pub struct PathMetadataLookup {
1082    /// Path-independent authenticated file record.
1083    pub record: FileRecord,
1084    /// Complete metadata referenced by `record`.
1085    pub metadata: FileMetadata,
1086}
1087
1088/// Exact creation/replacement precondition for one named attribute write.
1089#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1090pub enum NamedAttributeWriteMode {
1091    /// Insert when absent and replace when present.
1092    Upsert,
1093    /// Require authenticated absence.
1094    Create,
1095    /// Require authenticated presence.
1096    Replace,
1097}
1098
1099/// Semantic result of publishing one private checkout overlay.
1100#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1101pub enum CheckoutCommitOutcome {
1102    /// This call durably published the candidate generation.
1103    Committed {
1104        /// Published content-addressed generation.
1105        generation_id: GenerationId,
1106        /// Authority head produced by the durable append.
1107        head: Head,
1108    },
1109    /// The same operation identity and fingerprint was already durable.
1110    AlreadyCommitted {
1111        /// Previously published content-addressed generation.
1112        generation_id: GenerationId,
1113        /// Authority head at the original durable append.
1114        head: Head,
1115    },
1116    /// Another operation advanced the authority first.
1117    Conflict {
1118        /// Linearizable authority head observed by publication.
1119        actual: Head,
1120    },
1121    /// The checkout's writer epoch is stale.
1122    Fenced {
1123        /// Active authority epoch.
1124        actual_epoch: Epoch,
1125    },
1126    /// The operation identity was already bound to another fingerprint.
1127    IdempotencyConflict {
1128        /// Fingerprint durably bound to the reused operation identity.
1129        committed_fingerprint: Digest,
1130    },
1131}
1132
1133/// Receipt-bearing facade result.
1134pub type FsResult<T> = MeasuredResult<FsReceipt<T>, FsError>;
1135
1136/// One path-independent file-record change between immutable generations.
1137#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1138pub struct FileRecordChange {
1139    /// Stable file identity.
1140    pub file_id: FileId,
1141    /// Complete record in the earlier generation.
1142    pub before: Option<FileRecord>,
1143    /// Complete record in the later generation.
1144    pub after: Option<FileRecord>,
1145}
1146
1147/// One exact name binding change within a stable directory identity.
1148#[derive(Clone, Debug, Eq, PartialEq)]
1149pub struct DirectoryBindingChange {
1150    /// Stable parent directory identity.
1151    pub directory_id: FileId,
1152    /// Exact profile-specific component.
1153    pub name: LogicalName,
1154    /// Binding in the earlier generation.
1155    pub before: Option<crate::kernel::TreeEntry>,
1156    /// Binding in the later generation.
1157    pub after: Option<crate::kernel::TreeEntry>,
1158}
1159
1160/// Bounded Merkle-aware semantic diff between two immutable generations.
1161#[derive(Clone, Debug, Eq, PartialEq)]
1162pub struct GenerationDiff {
1163    /// Content, metadata, kind, link-count, or directory-root changes by file identity.
1164    pub files: Vec<FileRecordChange>,
1165    /// Added, removed, renamed, or retargeted names by parent directory identity.
1166    pub bindings: Vec<DirectoryBindingChange>,
1167    /// Additional changes exist beyond the caller's single total bound.
1168    pub truncated: bool,
1169}
1170
1171/// Terminal result of preparing an unpublished two-parent merge generation.
1172#[derive(Clone, Debug, Eq, PartialEq)]
1173pub enum MergePreparation {
1174    /// The checkout now holds this candidate and may commit or discard it.
1175    Prepared {
1176        /// Authenticated two-parent generation identity.
1177        generation_id: GenerationId,
1178    },
1179    /// No checkout state changed; exact conflicts are bounded by the caller.
1180    Conflicted {
1181        /// Stable conflicting regions.
1182        conflicts: Vec<MergeConflict>,
1183        /// Additional conflicts exceeded the supplied bound.
1184        truncated: bool,
1185    },
1186}
1187
1188/// Fail-closed embedded facade errors.
1189#[derive(Debug, Error)]
1190pub enum FsError {
1191    /// Operation was cancelled before further facade work began.
1192    #[error(transparent)]
1193    Cancelled(#[from] CancellationError),
1194    /// Volume configuration is invalid.
1195    #[error(transparent)]
1196    Config(#[from] VolumeConfigError),
1197    /// Durable local Stream initialization or recovery failed.
1198    #[cfg(all(feature = "local", not(target_arch = "wasm32")))]
1199    #[error(transparent)]
1200    LocalStream(#[from] acyclic_stream::LocalStreamError),
1201    /// Durable local Objects initialization or recovery failed.
1202    #[cfg(all(feature = "local", not(target_arch = "wasm32")))]
1203    #[error(transparent)]
1204    LocalObjects(#[from] acyclic_objects::LocalObjectsError),
1205    /// The canonical filesystem Objects bucket could not be opened.
1206    #[cfg(all(feature = "local", not(target_arch = "wasm32")))]
1207    #[error("local filesystem Objects bucket initialization failed: {0}")]
1208    LocalObjectsBucket(acyclic_objects::ObjectsError),
1209    /// Local provider root setup failed before either canonical provider opened.
1210    #[cfg(all(feature = "local", not(target_arch = "wasm32")))]
1211    #[error("local filesystem root setup failed: {0}")]
1212    LocalRoot(std::io::Error),
1213    /// A live process-local engine already owns this root with different limits.
1214    #[cfg(all(feature = "local", not(target_arch = "wasm32")))]
1215    #[error("local filesystem root is already open with different options")]
1216    LocalOptionsConflict,
1217    /// The bounded local root initialization worker could not complete.
1218    #[cfg(all(feature = "local", not(target_arch = "wasm32")))]
1219    #[error("local filesystem initialization worker is unavailable")]
1220    LocalInitializationWorker,
1221    /// Immutable-object accelerator bounds are invalid.
1222    #[cfg(all(feature = "local", not(target_arch = "wasm32")))]
1223    #[error(transparent)]
1224    ObjectCacheConfig(#[from] crate::cache::ObjectCacheConfigError),
1225    /// Checkout behavior is contradictory.
1226    #[error(transparent)]
1227    CheckoutMode(#[from] CheckoutModeError),
1228    /// Requested lifecycle is not guaranteed by the selected backends.
1229    #[error("durable volume requested from non-durable embedded backends")]
1230    UnsupportedDurability,
1231    /// A new name collides with an existing sibling under the volume's
1232    /// `CaseSensitivity::ProfileFolded` policy once both are case-folded,
1233    /// even though their exact bytes differ.
1234    #[error("name collides with an existing entry under the volume's case-folding policy")]
1235    NameCollision,
1236    /// A caller-derived identity already names different authenticated state.
1237    #[error("file identity collides with different authenticated content")]
1238    FileIdentityCollision,
1239    /// A new name is not canonical Unicode NFC, required by the volume's
1240    /// `UnicodePolicy::RequireNfc` policy.
1241    #[error("name is not normalized to NFC as required by the volume's unicode policy")]
1242    NonNormalizedName,
1243    /// The facade does not yet expose this checkout behavior.
1244    #[error("checkout behavior is not implemented by this facade")]
1245    UnsupportedCheckout,
1246    /// Direct-live mutation requires a serialized-authority volume.
1247    #[error("direct-live mutation requires serialized-authority concurrency")]
1248    LiveRequiresSerializedAuthority,
1249    /// Another writer changed the authority before exclusive ownership could be acquired.
1250    #[error("exclusive writer ownership raced with authority head {actual:?}")]
1251    ExclusiveWriterConflict {
1252        /// Linearizable head that defeated this acquisition.
1253        actual: Head,
1254    },
1255    /// A direct-live operation requires a positive safe-retry bound.
1256    #[error("direct-live retry bound must be positive")]
1257    ZeroLiveRetryLimit,
1258    /// Direct-live retry observations violated the canonical transition order.
1259    #[error(transparent)]
1260    LiveRetry(#[from] LiveRetryError),
1261    /// A direct-live checkout already has an unresolved candidate operation.
1262    #[error("direct-live checkout already has a pending operation")]
1263    PendingLiveMutation,
1264    /// Live advancement would cross an exact observed or mutated region.
1265    #[error("live advancement conflicts with an observed or mutated region")]
1266    LiveConflict {
1267        /// Bounded region-specific conflicts.
1268        conflicts: Vec<RebaseConflict>,
1269        /// Whether additional conflicts were omitted by the configured bound.
1270        truncated: bool,
1271    },
1272    /// Authority storage failed.
1273    #[error(transparent)]
1274    Authority(#[from] AuthorityStoreError),
1275    /// Immutable object storage failed.
1276    #[error(transparent)]
1277    Object(#[from] ObjectStoreError),
1278    /// Generation closure authentication failed.
1279    #[error(transparent)]
1280    Closure(#[from] ClosureError),
1281    /// Canonical filesystem data is malformed or unsupported.
1282    #[error(transparent)]
1283    Decode(#[from] CanonicalDecodeError),
1284    /// Canonical volume creation data is malformed or unsupported.
1285    #[error(transparent)]
1286    VolumeCreated(#[from] VolumeCreatedError),
1287    /// Canonical generation-retention fact is invalid.
1288    #[error(transparent)]
1289    RetentionCreated(#[from] RetentionCreatedError),
1290    /// Exact namespace-path resolution failed.
1291    #[error(transparent)]
1292    Path(#[from] PathLookupError),
1293    /// Bounded authenticated directory pagination failed.
1294    #[error(transparent)]
1295    Directory(#[from] DirectoryReadError),
1296    /// Bounded authenticated file-record pagination failed.
1297    #[error(transparent)]
1298    FileRecord(#[from] FileRecordReadError),
1299    /// Composed sparse regular-file range read failed.
1300    #[error(transparent)]
1301    FileRead(#[from] FileRangeReadError),
1302    /// Bounded authenticated blob construction failed.
1303    #[error(transparent)]
1304    BlobBuild(#[from] BlobBuildError),
1305    /// Authenticated blob-range reading failed.
1306    #[error(transparent)]
1307    BlobRead(#[from] BlobReadError),
1308    /// Authenticated named-attribute lookup failed.
1309    #[error(transparent)]
1310    AttributeLookup(#[from] AttributeLookupError),
1311    /// Authenticated named-attribute pagination failed.
1312    #[error(transparent)]
1313    AttributeList(#[from] AttributeListError),
1314    /// Sparse named-attribute path-copy failed.
1315    #[error(transparent)]
1316    AttributeMutation(#[from] AttributeMutationError),
1317    /// Tiny regular-file payload exceeded its canonical bound.
1318    #[error(transparent)]
1319    InlineFile(#[from] InlineFileDataError),
1320    /// Requested path is authentically absent.
1321    #[error("filesystem path does not exist")]
1322    NotFound,
1323    /// Requested operation requires a directory.
1324    #[error("filesystem path is not a directory")]
1325    NotDirectory,
1326    /// Requested operation requires a symbolic link.
1327    #[error("filesystem path is not a symbolic link")]
1328    NotSymbolicLink,
1329    /// Requested operation requires an opaque Windows reparse point.
1330    #[error("filesystem path is not a Windows reparse point")]
1331    NotReparsePoint,
1332    /// Special-file creation selected a kind incompatible with its payload.
1333    #[error("filesystem special-file kind is incompatible with the requested payload")]
1334    InvalidSpecialKind,
1335    /// The configured volume profile cannot represent this kind exactly.
1336    #[error("filesystem kind is unsupported by the configured volume profile")]
1337    UnsupportedFileKind,
1338    /// A directory binding and file-table record disagree or are missing.
1339    #[error("directory entry does not have a matching authenticated file record")]
1340    InvalidDirectoryRecord,
1341    /// Export manifest does not exactly match its authenticated closure.
1342    #[error("generation export manifest does not match the imported closure")]
1343    InvalidExportManifest,
1344    /// Resumable generation transfer validation or paging failed.
1345    #[error(transparent)]
1346    Transfer(#[from] GenerationTransferError),
1347    /// Checkout dependency capture failed.
1348    #[error(transparent)]
1349    Dependency(#[from] DependencyError),
1350    /// Exact authenticated dependency probing failed.
1351    #[error(transparent)]
1352    Probe(#[from] AuthenticatedProbeError),
1353    /// Observation-safe rebase classification failed.
1354    #[error(transparent)]
1355    Rebase(#[from] RebaseError<AuthenticatedProbeError>),
1356    /// One ordered generation mutation failed.
1357    #[error(transparent)]
1358    Mutation(#[from] GenerationMutationError),
1359    /// Path-independent open-file mutation failed.
1360    #[error(transparent)]
1361    DetachedMutation(#[from] RegularMutationError),
1362    /// Immutable checkpoint construction failed.
1363    #[error(transparent)]
1364    Checkpoint(#[from] CheckpointError),
1365    /// Whole-closure generation publication failed.
1366    #[error(transparent)]
1367    Publication(#[from] PublicationError),
1368    /// Authority exists but has no volume creation fact.
1369    #[error("volume authority has no creation fact")]
1370    EmptyAuthority,
1371    /// Authority records do not contain the expected volume event.
1372    #[error("volume authority history has an invalid event shape")]
1373    InvalidAuthorityHistory,
1374    /// Authenticated data belongs to another volume.
1375    #[error("authenticated data belongs to another volume")]
1376    VolumeMismatch,
1377    /// Concurrent creation/publication produced a semantic conflict.
1378    #[error("volume creation was rejected by authority state")]
1379    CreationRejected,
1380    /// The named workspace has been terminally deleted.
1381    #[error("workspace is deleted")]
1382    WorkspaceDeleted,
1383    /// Writable overlays must be opened from the current authority head.
1384    #[error("writable private checkout requires a head selector")]
1385    WritableCheckoutRequiresHead,
1386    /// Mutation was requested from a checkout that cannot mutate.
1387    #[error("checkout does not admit private overlay mutation")]
1388    MutationNotAllowed,
1389    /// Commit was requested without a changed candidate generation.
1390    #[error("checkout has no pending mutation to commit")]
1391    NoPendingMutations,
1392    /// Explicit head refresh requires manual checkout consistency.
1393    #[error("checkout does not admit explicit head refresh")]
1394    RefreshNotAllowed,
1395    /// A dirty overlay must be safely rebased or discarded before refresh.
1396    #[error("pending overlay mutations require safe rebase before refresh")]
1397    PendingMutationsRequireRebase,
1398    /// Uncommitted operations exceed the volume's atomic replay bound.
1399    #[error("checkout pending mutation count exceeds the volume limit")]
1400    TooManyPendingMutations,
1401    /// Caller-supplied mutation storage exceeds the admitted persistent bound.
1402    #[error("mutation input capacity exceeds the volume limit")]
1403    ExcessiveMutationCapacity,
1404    /// Retaining a bounded replay log failed.
1405    #[error("pending mutation replay log allocation failed")]
1406    PendingMutationAllocationFailed,
1407    /// A prepared two-parent merge must be committed or discarded before mutation/rebase.
1408    #[error("prepared merge must be committed or discarded before further mutation")]
1409    PreparedMergePending,
1410    /// Diff roots or persistent page structure do not match the admitted format.
1411    #[error("generation diff structure is invalid")]
1412    InvalidDiff,
1413    /// Bounded diff result allocation failed.
1414    #[error("generation diff allocation failed")]
1415    DiffAllocationFailed,
1416    /// Bounded local live-set allocation failed before physical collection.
1417    #[error("local garbage-collection live-set allocation failed")]
1418    GarbageCollectionAllocationFailed,
1419    /// A three-way merge exceeded its admitted change frontier.
1420    #[error("three-way merge exceeds its change bound")]
1421    MergeChangeLimit,
1422    /// The selected second parent is not the current authority head.
1423    #[error("merge second parent must be the current authority head")]
1424    MergeParentNotHead,
1425    /// Sparse file-table merge application failed.
1426    #[error(transparent)]
1427    MergeFileTable(#[from] FileTableMutationError),
1428    /// Sparse directory-tree merge application failed.
1429    #[error(transparent)]
1430    MergeTree(#[from] TreeMutationError),
1431    /// Exact work overflowed or exceeded the admitted budget.
1432    #[error(transparent)]
1433    Work(#[from] WorkError),
1434}
1435
1436impl<A, O> Fs<A, O> {
1437    /// Composes explicit authority and object backends without another state store.
1438    #[must_use]
1439    pub fn new(authority: A, objects: O, capabilities: EmbeddedCapabilities) -> Self {
1440        Self::new_in_namespace(authority, objects, capabilities, [0; 16])
1441    }
1442
1443    /// Composes explicit backends in one stable workspace-identity namespace.
1444    ///
1445    /// This is an engine-construction boundary. Customer constructors derive
1446    /// the namespace from their deployment rather than exposing it.
1447    #[must_use]
1448    pub fn new_in_namespace(
1449        authority: A,
1450        objects: O,
1451        capabilities: EmbeddedCapabilities,
1452        workspace_namespace: [u8; 16],
1453    ) -> Self {
1454        Self::new_with_path_index(
1455            authority,
1456            objects,
1457            capabilities,
1458            workspace_namespace,
1459            Arc::new(crate::path_index::MemoryGenerationPathIndex::default()),
1460            #[cfg(all(feature = "local", not(target_arch = "wasm32")))]
1461            None,
1462        )
1463    }
1464
1465    fn new_with_path_index(
1466        authority: A,
1467        objects: O,
1468        capabilities: EmbeddedCapabilities,
1469        workspace_namespace: [u8; 16],
1470        path_index: Arc<dyn crate::path_index::GenerationPathIndex>,
1471        #[cfg(all(feature = "local", not(target_arch = "wasm32")))] local_root_lifecycle: Option<
1472            OwnershipAnchor,
1473        >,
1474    ) -> Self {
1475        Self {
1476            inner: Arc::new(FsInner {
1477                authority,
1478                objects,
1479                capabilities,
1480                workspace_namespace,
1481                path_index,
1482                #[cfg(all(feature = "local", not(target_arch = "wasm32")))]
1483                _local_root_lifecycle: local_root_lifecycle,
1484            }),
1485        }
1486    }
1487}
1488
1489impl<A, S> Fs<A, crate::cache::CachedObjectStore<S>> {
1490    /// Returns exact current and cumulative observations from the disposable
1491    /// immutable-object accelerator shared by every clone and volume handle.
1492    ///
1493    /// # Errors
1494    ///
1495    /// Fails closed if process-local accelerator synchronization was poisoned.
1496    pub fn object_cache_stats(&self) -> Result<crate::cache::ObjectCacheStats, ObjectStoreError> {
1497        self.inner.objects.stats()
1498    }
1499
1500    /// Discards all resident accelerated bytes without changing authority or
1501    /// immutable backing storage. Concurrent reads remain correct and may
1502    /// repopulate the cache after this call.
1503    ///
1504    /// # Errors
1505    ///
1506    /// Fails closed if process-local accelerator synchronization was poisoned.
1507    pub fn clear_object_cache(&self) -> Result<(), ObjectStoreError> {
1508        self.inner.objects.clear()
1509    }
1510}
1511
1512#[cfg(all(feature = "local", not(target_arch = "wasm32")))]
1513impl Fs<LocalAuthorityBackend, LocalObjectBackend> {
1514    /// Observes the disposable local immutable-object cache.
1515    pub fn object_cache_stats(&self) -> Result<crate::cache::ObjectCacheStats, ObjectStoreError> {
1516        self.inner.objects.inner().stats()
1517    }
1518
1519    /// Drops cached bytes without changing persistent object storage.
1520    pub fn clear_object_cache(&self) -> Result<(), ObjectStoreError> {
1521        self.inner.objects.inner().clear()
1522    }
1523}
1524
1525#[cfg(all(feature = "memory", feature = "distributed"))]
1526impl
1527    Fs<
1528        crate::distributed::StreamAuthorityStore<acyclic_stream::MemoryStream>,
1529        crate::distributed::ProviderObjectStore<acyclic_objects::MemoryObjects>,
1530    >
1531{
1532    /// Creates the deterministic infrastructure-free composition from the exact public reference
1533    /// Stream and Objects providers used by hosted-service tests.
1534    #[must_use]
1535    pub fn memory() -> Self {
1536        let stream = std::sync::Arc::new(acyclic_stream::MemoryStream::default());
1537        let (objects, bucket) = acyclic_objects::MemoryObjects::with_default_bucket();
1538        Self::from_memory_providers(stream, std::sync::Arc::new(objects), bucket)
1539    }
1540
1541    /// Composes Filesystem over caller-owned public reference providers.
1542    ///
1543    /// Clones of the same providers may be retained to test cross-family behavior without any
1544    /// shadow filesystem storage implementation.
1545    #[must_use]
1546    pub fn from_memory_providers(
1547        stream: std::sync::Arc<acyclic_stream::MemoryStream>,
1548        objects: std::sync::Arc<acyclic_objects::MemoryObjects>,
1549        bucket: acyclic_objects::wire::BucketRef,
1550    ) -> Self {
1551        Self::new(
1552            crate::distributed::StreamAuthorityStore::new(stream),
1553            crate::distributed::ProviderObjectStore::new(objects, bucket),
1554            EmbeddedCapabilities::MEMORY,
1555        )
1556    }
1557}
1558
1559#[cfg(all(feature = "local", not(target_arch = "wasm32")))]
1560impl Fs<LocalAuthorityBackend, LocalObjectBackend> {
1561    /// Observes release of every provider derived from this local root.
1562    ///
1563    /// This is an explicit service-shutdown boundary. The returned barrier does not retain the
1564    /// root; callers must first drop every filesystem, workspace, checkout, and mount handle.
1565    #[doc(hidden)]
1566    #[must_use]
1567    pub fn local_root_release_barrier(
1568        &self,
1569    ) -> Option<acyclic_native_runtime::OwnershipReleaseBarrier> {
1570        self.inner
1571            ._local_root_lifecycle
1572            .as_ref()
1573            .map(OwnershipAnchor::release_barrier)
1574    }
1575
1576    /// Opens the infrastructure-free durable local composition.
1577    ///
1578    /// # Errors
1579    ///
1580    /// Fails if limits are invalid or either durable backend cannot initialize.
1581    pub async fn local(options: LocalOptions) -> Result<Self, FsError> {
1582        static REGISTRY: OnceLock<tokio::sync::Mutex<LocalRootRegistry>> = OnceLock::new();
1583        let requested_root = options.root.clone();
1584        let root = tokio::task::spawn_blocking(move || {
1585            std::fs::create_dir_all(&requested_root)?;
1586            std::fs::canonicalize(requested_root)
1587        })
1588        .await
1589        .map_err(|_| FsError::LocalInitializationWorker)?
1590        .map_err(FsError::LocalRoot)?;
1591        let mut options = options;
1592        options.root = root.clone();
1593        let registry = REGISTRY.get_or_init(|| tokio::sync::Mutex::new(LocalRootRegistry::new()));
1594        let opening = {
1595            let mut registrations = registry.lock().await;
1596            if let Some(existing) = registrations.get(&root)
1597                && let Some(inner) = existing.live.upgrade()
1598            {
1599                if existing.options != options {
1600                    return Err(FsError::LocalOptionsConflict);
1601                }
1602                return Ok(Self { inner });
1603            }
1604            prepare_local_root_open(&mut registrations, &root, &options)
1605        };
1606        // Opening a root may wait for old provider handles or native I/O to finish. Keep that
1607        // wait per-root so unrelated roots can attach and respond to hooks concurrently.
1608        let _opening = opening.lock().await;
1609        let lifecycle = {
1610            let registrations = registry.lock().await;
1611            let existing = registrations
1612                .get(&root)
1613                .ok_or(FsError::LocalInitializationWorker)?;
1614            if let Some(inner) = existing.live.upgrade() {
1615                if existing.options != options {
1616                    return Err(FsError::LocalOptionsConflict);
1617                }
1618                return Ok(Self { inner });
1619            }
1620            Arc::clone(&existing.lifecycle)
1621        };
1622        let ownership = lifecycle.lock_owned().await;
1623        let open_options = options.clone();
1624        let fs = run_local_initialization(ownership, move |ownership| async move {
1625            let ownership = OwnershipAnchor::new(ownership);
1626            Self::open_local_unshared(open_options, Some(ownership)).await
1627        })
1628        .await??;
1629        let mut registrations = registry.lock().await;
1630        let existing = registrations
1631            .get_mut(&root)
1632            .ok_or(FsError::LocalInitializationWorker)?;
1633        existing.options = options;
1634        existing.live = Arc::downgrade(&fs.inner);
1635        Ok(fs)
1636    }
1637
1638    async fn open_local_unshared(
1639        options: LocalOptions,
1640        lifecycle: Option<OwnershipAnchor>,
1641    ) -> Result<Self, FsError> {
1642        let LocalOptions {
1643            root,
1644            stream,
1645            objects,
1646            object_cache,
1647        } = options;
1648        let stream = std::sync::Arc::new(match &lifecycle {
1649            Some(ownership) => {
1650                acyclic_stream::LocalStream::open_with_ownership_anchor(
1651                    root.join("stream"),
1652                    stream,
1653                    ownership.clone(),
1654                )
1655                .await?
1656            }
1657            None => acyclic_stream::LocalStream::open(root.join("stream"), stream).await?,
1658        });
1659        let objects = std::sync::Arc::new(match &lifecycle {
1660            Some(ownership) => {
1661                acyclic_objects::LocalObjects::open_with_ownership_anchor(
1662                    root.join("objects"),
1663                    objects,
1664                    ownership.clone(),
1665                )
1666                .await?
1667            }
1668            None => acyclic_objects::LocalObjects::open(root.join("objects"), objects).await?,
1669        });
1670        let bucket = match objects
1671            .bucket_named("filesystem-objects")
1672            .await
1673            .map_err(FsError::LocalObjectsBucket)?
1674        {
1675            Some(bucket) => bucket,
1676            None => objects
1677                .create_bucket(
1678                    "filesystem-objects".to_owned(),
1679                    Some("filesystem-objects-v1".to_owned()),
1680                )
1681                .await
1682                .map_err(FsError::LocalObjectsBucket)?
1683                .bucket
1684                .ok_or(FsError::LocalObjectsBucket(
1685                    acyclic_objects::ObjectsError::Unavailable,
1686                ))?,
1687        };
1688        let objects = crate::cache::CachedObjectStore::new(
1689            crate::distributed::ProviderObjectStore::new(objects, bucket),
1690            object_cache,
1691        )?;
1692        let objects = crate::staged_objects::StagedObjects::new(objects);
1693        Ok(Self::new_with_path_index(
1694            crate::distributed::StreamAuthorityStore::new(stream),
1695            objects,
1696            EmbeddedCapabilities {
1697                durable: cfg!(any(unix, windows)),
1698            },
1699            [0; 16],
1700            Arc::new(crate::path_index::NativeGenerationPathIndex::new(
1701                root.join("generation-path-index-v1"),
1702            )),
1703            lifecycle,
1704        ))
1705    }
1706
1707    /// Reclaims unreachable local objects under an exclusive cross-process
1708    /// maintenance fence.
1709    ///
1710    /// Every normal local store holds a shared fence for its complete lifetime,
1711    /// so this operation fails while any embedded engine, checkout, mount, or
1712    /// direct local object-store consumer is open. It authenticates every
1713    /// authority head and complete generation closure before physical deletion.
1714    ///
1715    /// # Errors
1716    ///
1717    /// Returns a typed failure for an active local consumer, malformed authority
1718    /// history, incomplete/corrupt closure, cancellation, candidate/result
1719    /// bounds, storage failure, or work outside `budget`.
1720    pub async fn collect_local_garbage(
1721        options: LocalOptions,
1722        maximum_authorities: u32,
1723        maximum_candidates: u64,
1724        budget: WorkBudget,
1725        cancellation: &CancellationToken,
1726    ) -> FsResult<LocalGarbageCollection> {
1727        cancellation
1728            .check()
1729            .map_err(|error| OperationFailure::before_work(error.into()))?;
1730        let fs = Self::open_local_unshared(options, None)
1731            .await
1732            .map_err(OperationFailure::before_work)?;
1733        fs.collect_local_garbage_exclusive(
1734            maximum_authorities,
1735            maximum_candidates,
1736            budget,
1737            cancellation,
1738        )
1739        .await
1740    }
1741
1742    async fn collect_local_garbage_exclusive(
1743        &self,
1744        maximum_authorities: u32,
1745        maximum_candidates: u64,
1746        budget: WorkBudget,
1747        cancellation: &CancellationToken,
1748    ) -> FsResult<LocalGarbageCollection> {
1749        let authorities = self
1750            .inner
1751            .authority
1752            .authorities(maximum_authorities)
1753            .await
1754            .map_err(|error| OperationFailure::before_work(error.into()))?;
1755        let authority_live_bytes = u64::try_from(authorities.capacity())
1756            .unwrap_or(u64::MAX)
1757            .saturating_mul(
1758                u64::try_from(size_of::<crate::foundation::AuthorityId>()).unwrap_or(u64::MAX),
1759            );
1760        let mut work = WorkCounters {
1761            backend_read_operations: 1,
1762            items_examined: u64::try_from(authorities.len()).unwrap_or(u64::MAX),
1763            items_returned: u64::try_from(authorities.len()).unwrap_or(u64::MAX),
1764            allocation_operations: u64::from(!authorities.is_empty()),
1765            peak_allocation_bytes: authority_live_bytes,
1766            ..WorkCounters::default()
1767        };
1768        work.verify(budget)
1769            .map_err(|error| OperationFailure::new(error.into(), work))?;
1770        let mut reachable = Vec::<ObjectId>::new();
1771        for authority_id in authorities {
1772            let Some((generation_root, config, next_work)) = self
1773                .local_authority_generation(
1774                    authority_id,
1775                    authority_live_bytes,
1776                    work,
1777                    budget,
1778                    cancellation,
1779                )
1780                .await?
1781            else {
1782                continue;
1783            };
1784            work = next_work;
1785            let live_bytes = authority_live_bytes.saturating_add(object_vec_bytes(&reachable));
1786            let proof = prove_generation_closure_async(
1787                &self.inner.objects,
1788                generation_root,
1789                closure_limits(config),
1790                remaining(work, budget)?,
1791                cancellation,
1792            )
1793            .await
1794            .map_err(|failure| failure.map_with_prior_work(work, Into::into))?;
1795            work = merge_simultaneous_work(work, proof.work, live_bytes, budget)?;
1796            let incoming_bytes = object_vec_bytes(&proof.objects);
1797            merge_sorted_object_ids(
1798                &mut reachable,
1799                &proof.objects,
1800                incoming_bytes,
1801                authority_live_bytes,
1802                &mut work,
1803                budget,
1804            )?;
1805        }
1806        #[cfg(feature = "s3-http")]
1807        {
1808            work = self
1809                .merge_active_s3_multipart_reachable_objects(
1810                    maximum_candidates,
1811                    authority_live_bytes,
1812                    &mut reachable,
1813                    work,
1814                    budget,
1815                    cancellation,
1816                )
1817                .await?;
1818        }
1819        let collected = self
1820            .collect_unreachable_local_objects(
1821                &reachable,
1822                maximum_candidates,
1823                authority_live_bytes,
1824                &mut work,
1825                budget,
1826                cancellation,
1827            )
1828            .await?;
1829        Ok(FsReceipt {
1830            value: collected,
1831            work,
1832        })
1833    }
1834
1835    /// Scans active S3 multipart staged content and merges its reachable
1836    /// objects into the accumulating local garbage-collection reachable set.
1837    #[cfg(feature = "s3-http")]
1838    async fn merge_active_s3_multipart_reachable_objects(
1839        &self,
1840        maximum_candidates: u64,
1841        authority_live_bytes: u64,
1842        reachable: &mut Vec<ObjectId>,
1843        work: WorkCounters,
1844        budget: WorkBudget,
1845        cancellation: &CancellationToken,
1846    ) -> Result<WorkCounters, OperationFailure<FsError>> {
1847        let multipart = crate::active_s3_multipart_objects(
1848            self.inner.authority.provider().as_ref(),
1849            &self.inner.objects,
1850            crate::FilesystemS3Limits::default(),
1851            crate::S3MultipartRetentionLimits {
1852                maximum_uploads: maximum_candidates.max(1),
1853                ..crate::S3MultipartRetentionLimits::default()
1854            },
1855            remaining(work, budget)?,
1856            cancellation,
1857        )
1858        .await
1859        .map_err(|_| {
1860            OperationFailure::new(
1861                FsError::Object(ObjectStoreError::Rejected(
1862                    "active S3 multipart retention scan failed".to_owned(),
1863                )),
1864                work,
1865            )
1866        })?;
1867        let mut work = merge_simultaneous_work(
1868            work,
1869            multipart.work,
1870            authority_live_bytes.saturating_add(object_vec_bytes(reachable)),
1871            budget,
1872        )?;
1873        let incoming_bytes = object_vec_bytes(&multipart.value);
1874        merge_sorted_object_ids(
1875            reachable,
1876            &multipart.value,
1877            incoming_bytes,
1878            authority_live_bytes,
1879            &mut work,
1880            budget,
1881        )?;
1882        Ok(work)
1883    }
1884
1885    async fn collect_unreachable_local_objects(
1886        &self,
1887        reachable: &[ObjectId],
1888        maximum_candidates: u64,
1889        retained_bytes: u64,
1890        work: &mut WorkCounters,
1891        budget: WorkBudget,
1892        cancellation: &CancellationToken,
1893    ) -> Result<LocalGarbageCollection, OperationFailure<FsError>> {
1894        if maximum_candidates == 0 {
1895            return Err(OperationFailure::new(
1896                FsError::Object(ObjectStoreError::Rejected(
1897                    "garbage-collection candidate bound must be positive".to_owned(),
1898                )),
1899                *work,
1900            ));
1901        }
1902        let (candidates, examined) = self
1903            .list_unreachable_object_candidates(
1904                reachable,
1905                maximum_candidates,
1906                retained_bytes,
1907                work,
1908                budget,
1909                cancellation,
1910            )
1911            .await?;
1912        let provider = self.inner.objects.inner().inner().provider();
1913        let bucket = self.inner.objects.inner().inner().bucket().clone();
1914        let mut removed = 0_u64;
1915        for (object_key, version_id) in candidates {
1916            cancellation
1917                .check()
1918                .map_err(|error| OperationFailure::new(error.into(), *work))?;
1919            let identity = blake3::hash(format!("{object_key}\0{version_id}").as_bytes());
1920            provider
1921                .delete(
1922                    bucket.clone(),
1923                    object_key,
1924                    Some(version_id),
1925                    None,
1926                    Some(format!("fs-gc-{}", identity.to_hex())),
1927                )
1928                .await
1929                .map_err(|error| {
1930                    OperationFailure::new(FsError::LocalObjectsBucket(error), *work)
1931                })?;
1932            removed = removed.saturating_add(1);
1933            *work = work
1934                .checked_add(WorkCounters {
1935                    backend_write_operations: 1,
1936                    items_examined: 1,
1937                    ..WorkCounters::default()
1938                })
1939                .map_err(|error| OperationFailure::new(error.into(), *work))?;
1940            work.verify(budget)
1941                .map_err(|error| OperationFailure::new(error.into(), *work))?;
1942        }
1943        let physical = provider
1944            .collect_garbage(maximum_candidates)
1945            .await
1946            .map_err(|error| OperationFailure::new(FsError::LocalObjects(error), *work))?;
1947        Ok(LocalGarbageCollection {
1948            examined,
1949            removed,
1950            segments_removed: physical.segments_removed,
1951            temporary_files_removed: physical.temporary_files_removed,
1952        })
1953    }
1954
1955    /// Pages the local objects bucket and collects every stored object key
1956    /// and version absent from the `reachable` set, up to `maximum_candidates`.
1957    ///
1958    /// Returns the unreachable candidates alongside the total number of
1959    /// stored objects examined while paging.
1960    async fn list_unreachable_object_candidates(
1961        &self,
1962        reachable: &[ObjectId],
1963        maximum_candidates: u64,
1964        retained_bytes: u64,
1965        work: &mut WorkCounters,
1966        budget: WorkBudget,
1967        cancellation: &CancellationToken,
1968    ) -> Result<(Vec<(String, String)>, u64), OperationFailure<FsError>> {
1969        let reachable_keys = reachable
1970            .iter()
1971            .copied()
1972            .map(crate::distributed::object_key)
1973            .collect::<BTreeSet<_>>();
1974        let provider = self.inner.objects.inner().inner().provider();
1975        let bucket = self.inner.objects.inner().inner().bucket().clone();
1976        let mut continuation = None;
1977        let mut candidates = Vec::new();
1978        let mut examined = 0_u64;
1979        loop {
1980            cancellation
1981                .check()
1982                .map_err(|error| OperationFailure::new(error.into(), *work))?;
1983            let remaining_candidates = maximum_candidates.saturating_sub(examined);
1984            if remaining_candidates == 0 && continuation.is_some() {
1985                return Err(OperationFailure::new(
1986                    FsError::Object(ObjectStoreError::Rejected(
1987                        "garbage-collection candidate bound exceeded".to_owned(),
1988                    )),
1989                    *work,
1990                ));
1991            }
1992            let page_size = u32::try_from(remaining_candidates.min(256)).unwrap_or(256);
1993            let page = provider
1994                .list(
1995                    acyclic_objects::ReadTarget::Bucket(bucket.clone()),
1996                    "fs/v1/".to_owned(),
1997                    None,
1998                    true,
1999                    page_size,
2000                    continuation,
2001                )
2002                .await
2003                .map_err(|error| {
2004                    OperationFailure::new(FsError::LocalObjectsBucket(error), *work)
2005                })?;
2006            let page_items = u64::try_from(page.entries.len()).unwrap_or(u64::MAX);
2007            examined = examined
2008                .checked_add(page_items)
2009                .ok_or_else(|| OperationFailure::new(FsError::Work(WorkError::Overflow), *work))?;
2010            if examined > maximum_candidates {
2011                return Err(OperationFailure::new(
2012                    FsError::Object(ObjectStoreError::Rejected(
2013                        "garbage-collection candidate bound exceeded".to_owned(),
2014                    )),
2015                    *work,
2016                ));
2017            }
2018            for entry in page.entries {
2019                let version = entry.version.ok_or_else(|| {
2020                    OperationFailure::new(FsError::Object(ObjectStoreError::Corrupt), *work)
2021                })?;
2022                if !reachable_keys.contains(&entry.object_key) {
2023                    candidates.push((entry.object_key, version.version_id));
2024                }
2025            }
2026            *work = work
2027                .checked_add(WorkCounters {
2028                    backend_read_operations: 1,
2029                    items_examined: page_items,
2030                    allocation_operations: u64::from(!candidates.is_empty()),
2031                    peak_allocation_bytes: retained_bytes
2032                        .saturating_add(object_slice_bytes(reachable)),
2033                    ..WorkCounters::default()
2034                })
2035                .map_err(|error| OperationFailure::new(error.into(), *work))?;
2036            work.verify(budget)
2037                .map_err(|error| OperationFailure::new(error.into(), *work))?;
2038            continuation = page.continuation;
2039            if continuation.is_none() {
2040                break;
2041            }
2042        }
2043        Ok((candidates, examined))
2044    }
2045
2046    async fn local_authority_generation(
2047        &self,
2048        authority_id: crate::foundation::AuthorityId,
2049        retained_bytes: u64,
2050        mut work: WorkCounters,
2051        budget: WorkBudget,
2052        cancellation: &CancellationToken,
2053    ) -> Result<Option<(ObjectId, VolumeConfig, WorkCounters)>, OperationFailure<FsError>> {
2054        cancellation
2055            .check()
2056            .map_err(|error| OperationFailure::new(error.into(), work))?;
2057        let head = self
2058            .inner
2059            .authority
2060            .head(authority_id, remaining(work, budget)?, cancellation)
2061            .await
2062            .map_err(|failure| failure.map_with_prior_work(work, Into::into))?;
2063        work = merge_simultaneous_work(work, head.work, retained_bytes, budget)?;
2064        if head.value.sequence == Sequence::GENESIS {
2065            return Ok(None);
2066        }
2067        let creation = self
2068            .inner
2069            .authority
2070            .replay(
2071                authority_id,
2072                Sequence::GENESIS,
2073                ReplayLimit {
2074                    records: 1,
2075                    payload_bytes: MAXIMUM_VOLUME_EVENT_BYTES,
2076                },
2077                remaining(work, budget)?,
2078                cancellation,
2079            )
2080            .await
2081            .map_err(|failure| failure.map_with_prior_work(work, Into::into))?;
2082        work = merge_simultaneous_work(work, creation.work, retained_bytes, budget)?;
2083        let first = creation
2084            .value
2085            .first()
2086            .ok_or_else(|| OperationFailure::new(FsError::InvalidAuthorityHistory, work))?;
2087        let Ok(created) = decode_volume_created(&first.payload, MAXIMUM_VOLUME_EVENT_BYTES) else {
2088            #[cfg(all(feature = "native-watch", not(target_arch = "wasm32")))]
2089            if let Ok(source_volume) =
2090                decode_source_volume(&first.payload, MAXIMUM_VOLUME_EVENT_BYTES)
2091            {
2092                if source_authority_id(source_volume) != authority_id {
2093                    return Err(OperationFailure::new(FsError::VolumeMismatch, work));
2094                }
2095                return Ok(None);
2096            }
2097            let retained = decode_retention_created(&first.payload, MAXIMUM_VOLUME_EVENT_BYTES)
2098                .map_err(|error| OperationFailure::new(error.into(), work))?;
2099            return Ok(Some((retained.generation_root, retained.config, work)));
2100        };
2101        if volume_authority_id(created.volume_id) != authority_id {
2102            return Err(OperationFailure::new(FsError::VolumeMismatch, work));
2103        }
2104        let generation_root = if head.value.sequence == Sequence::new(1) {
2105            created.initial_generation_root
2106        } else {
2107            let latest = self
2108                .inner
2109                .authority
2110                .replay(
2111                    authority_id,
2112                    Sequence::new(head.value.sequence.get().saturating_sub(1)),
2113                    ReplayLimit {
2114                        records: 1,
2115                        payload_bytes: MAXIMUM_VOLUME_EVENT_BYTES,
2116                    },
2117                    remaining(work, budget)?,
2118                    cancellation,
2119                )
2120                .await
2121                .map_err(|failure| failure.map_with_prior_work(work, Into::into))?;
2122            work = merge_simultaneous_work(work, latest.work, retained_bytes, budget)?;
2123            let record = latest
2124                .value
2125                .first()
2126                .ok_or_else(|| OperationFailure::new(FsError::InvalidAuthorityHistory, work))?;
2127            if let Ok(deleted) =
2128                decode_workspace_deleted(&record.payload, MAXIMUM_VOLUME_EVENT_BYTES)
2129            {
2130                if deleted != created.volume_id {
2131                    return Err(OperationFailure::new(FsError::VolumeMismatch, work));
2132                }
2133                return Ok(None);
2134            }
2135            generation_from_record(record, created.volume_id, work)?
2136        };
2137        Ok(Some((generation_root, created.config, work)))
2138    }
2139}
2140
2141impl<A: AsyncAuthorityStore, O: AsyncObjectStore> Fs<A, O> {
2142    pub(crate) async fn observe_volume_operation(
2143        &self,
2144        volume_id: VolumeId,
2145        operation_id: OperationId,
2146        budget: WorkBudget,
2147        cancellation: &CancellationToken,
2148    ) -> FsResult<Option<DurableCommit>> {
2149        let receipt = self
2150            .inner
2151            .authority
2152            .find_operation(
2153                volume_authority_id(volume_id),
2154                operation_id,
2155                budget,
2156                cancellation,
2157            )
2158            .await
2159            .map_err(|failure| {
2160                OperationFailure::new(FsError::Authority(failure.error), *failure.work)
2161            })?;
2162        Ok(FsReceipt {
2163            value: receipt.value,
2164            work: receipt.work,
2165        })
2166    }
2167
2168    pub(crate) async fn generation_parents(
2169        &self,
2170        volume: &Volume<A, O>,
2171        generation: GenerationId,
2172    ) -> Result<Vec<GenerationId>, crate::workspace::WorkspaceError> {
2173        let object = ObjectId {
2174            kind: ObjectKind::GenerationRoot,
2175            digest: generation.digest(),
2176        };
2177        let (root, _) = read_generation_root(
2178            &self.inner.objects,
2179            object,
2180            volume.config,
2181            WorkBudget::UNBOUNDED,
2182            &CancellationToken::new(),
2183        )
2184        .await
2185        .map_err(crate::workspace::WorkspaceError::engine)?;
2186        if root.volume_id != volume.id {
2187            return Err(crate::workspace::WorkspaceError::ForeignGeneration);
2188        }
2189        Ok(root.parents)
2190    }
2191
2192    /// Creates or recovers one canonical named workspace with deployment-
2193    /// appropriate portable defaults.
2194    ///
2195    /// # Errors
2196    ///
2197    /// Rejects invalid names, incompatible durability, storage failures, or a
2198    /// pre-existing name with different immutable creation semantics.
2199    pub async fn create_workspace(
2200        &self,
2201        name: impl AsRef<str>,
2202    ) -> Result<crate::Workspace<A, O>, crate::workspace::WorkspaceError> {
2203        let lifecycle = if self.inner.capabilities.durable {
2204            Lifecycle::Durable
2205        } else {
2206            Lifecycle::Ephemeral
2207        };
2208        self.create_workspace_with_config(name, VolumeConfig::portable(lifecycle))
2209            .await
2210    }
2211
2212    /// Creates or recovers one named workspace with exact filesystem semantics.
2213    ///
2214    /// # Errors
2215    ///
2216    /// Returns the same typed failures as [`Self::create_workspace`].
2217    pub async fn create_workspace_with_config(
2218        &self,
2219        name: impl AsRef<str>,
2220        config: VolumeConfig,
2221    ) -> Result<crate::Workspace<A, O>, crate::workspace::WorkspaceError> {
2222        self.create_workspace_with_config_operation(name, config, None)
2223            .await
2224    }
2225
2226    pub(crate) async fn create_workspace_with_config_operation(
2227        &self,
2228        name: impl AsRef<str>,
2229        config: VolumeConfig,
2230        operation_id: Option<OperationId>,
2231    ) -> Result<crate::Workspace<A, O>, crate::workspace::WorkspaceError> {
2232        let name = crate::WorkspaceName::new(name)?;
2233        let id = crate::WorkspaceId::derive(self.inner.workspace_namespace, &name);
2234        let volume = self
2235            .create_volume_with_id_operation(
2236                id.volume_id(),
2237                config,
2238                operation_id,
2239                WorkBudget::UNBOUNDED,
2240                &CancellationToken::new(),
2241            )
2242            .await
2243            .map_err(crate::workspace::WorkspaceError::engine)?
2244            .value;
2245        volume
2246            .resolve_head_generation(WorkBudget::UNBOUNDED, &CancellationToken::new())
2247            .await
2248            .map_err(crate::workspace::WorkspaceError::engine)?;
2249        Ok(crate::Workspace {
2250            name,
2251            id,
2252            volume,
2253            #[cfg(all(feature = "native-watch", not(target_arch = "wasm32")))]
2254            source: None,
2255        })
2256    }
2257
2258    /// Opens one existing named workspace without a mutable catalog lookup.
2259    ///
2260    /// # Errors
2261    ///
2262    /// Rejects invalid or absent names and malformed or unsupported creation
2263    /// state.
2264    pub async fn open_workspace(
2265        &self,
2266        name: impl AsRef<str>,
2267    ) -> Result<crate::Workspace<A, O>, crate::workspace::WorkspaceError> {
2268        let name = crate::WorkspaceName::new(name)?;
2269        let id = crate::WorkspaceId::derive(self.inner.workspace_namespace, &name);
2270        let volume = self
2271            .open_volume(
2272                id.volume_id(),
2273                WorkBudget::UNBOUNDED,
2274                &CancellationToken::new(),
2275            )
2276            .await
2277            .map_err(crate::workspace::WorkspaceError::engine)?
2278            .value;
2279        volume
2280            .resolve_head_generation(WorkBudget::UNBOUNDED, &CancellationToken::new())
2281            .await
2282            .map_err(crate::workspace::WorkspaceError::engine)?;
2283        Ok(crate::Workspace {
2284            name,
2285            id,
2286            volume,
2287            #[cfg(all(feature = "native-watch", not(target_arch = "wasm32")))]
2288            source: None,
2289        })
2290    }
2291
2292    /// Opens an existing volume through the workspace API without changing
2293    /// its stable identity or durable state.
2294    ///
2295    /// This is the migration boundary for consumers created before named
2296    /// workspaces existed. The supplied name is descriptive; the existing
2297    /// volume remains the sole durable identity.
2298    ///
2299    /// # Errors
2300    ///
2301    /// Rejects invalid names, absent volumes, and malformed creation state.
2302    pub async fn open_volume_workspace(
2303        &self,
2304        name: impl AsRef<str>,
2305        volume_id: VolumeId,
2306    ) -> Result<crate::Workspace<A, O>, crate::workspace::WorkspaceError> {
2307        let name = crate::WorkspaceName::new(name)?;
2308        let volume = self
2309            .open_volume(volume_id, WorkBudget::UNBOUNDED, &CancellationToken::new())
2310            .await
2311            .map_err(crate::workspace::WorkspaceError::engine)?
2312            .value;
2313        volume
2314            .resolve_head_generation(WorkBudget::UNBOUNDED, &CancellationToken::new())
2315            .await
2316            .map_err(crate::workspace::WorkspaceError::engine)?;
2317        Ok(crate::Workspace {
2318            name,
2319            id: crate::WorkspaceId::from_volume_id(volume_id),
2320            volume,
2321            #[cfg(all(feature = "native-watch", not(target_arch = "wasm32")))]
2322            source: None,
2323        })
2324    }
2325
2326    /// Adopts an already authenticated volume into the workspace facade.
2327    ///
2328    /// This avoids reopening durable creation state when a compatibility
2329    /// consumer already owns the volume returned by creation or migration.
2330    ///
2331    /// # Errors
2332    ///
2333    /// Rejects an invalid descriptive name.
2334    pub fn adopt_volume_workspace(
2335        &self,
2336        name: impl AsRef<str>,
2337        volume: Volume<A, O>,
2338    ) -> Result<crate::Workspace<A, O>, crate::workspace::WorkspaceError> {
2339        let name = crate::WorkspaceName::new(name)?;
2340        let id = crate::WorkspaceId::from_volume_id(volume.id);
2341        Ok(crate::Workspace {
2342            name,
2343            id,
2344            volume,
2345            #[cfg(all(feature = "native-watch", not(target_arch = "wasm32")))]
2346            source: None,
2347        })
2348    }
2349
2350    /// Terminally deletes one named workspace without requiring a live handle.
2351    /// This stateless form preserves exact retry resolution after the durable
2352    /// tombstone makes ordinary workspace opening fail closed.
2353    ///
2354    /// # Errors
2355    ///
2356    /// Rejects invalid or absent names and returns typed storage failures.
2357    pub async fn delete_workspace(
2358        &self,
2359        name: impl AsRef<str>,
2360        idempotency_key: crate::IdempotencyKey,
2361    ) -> Result<crate::workspace::WorkspaceDelete, crate::workspace::WorkspaceError> {
2362        let name = crate::WorkspaceName::new(name)?;
2363        let id = crate::WorkspaceId::derive(self.inner.workspace_namespace, &name);
2364        let volume = self
2365            .open_volume(
2366                id.volume_id(),
2367                WorkBudget::UNBOUNDED,
2368                &CancellationToken::new(),
2369            )
2370            .await
2371            .map_err(crate::workspace::WorkspaceError::engine)?
2372            .value;
2373        self.delete_workspace_volume(&volume, idempotency_key.operation_id())
2374            .await
2375    }
2376
2377    /// Builds and stores the forked generation root for a new workspace, then
2378    /// proves and returns its complete reachable closure and accrued work.
2379    async fn materialize_forked_generation_root(
2380        &self,
2381        source: &crate::Generation<A, O>,
2382        destination_id: crate::WorkspaceId,
2383        config: VolumeConfig,
2384        budget: WorkBudget,
2385        cancellation: &CancellationToken,
2386    ) -> Result<(ObjectId, VerifiedForkSource, WorkCounters), crate::workspace::WorkspaceError>
2387    {
2388        let source_object = ObjectId {
2389            kind: ObjectKind::GenerationRoot,
2390            digest: source.id.digest(),
2391        };
2392        let (source_root, work) = read_generation_root(
2393            &self.inner.objects,
2394            source_object,
2395            config,
2396            budget,
2397            cancellation,
2398        )
2399        .await
2400        .map_err(crate::workspace::WorkspaceError::engine)?;
2401        if source_root.volume_id != source.workspace.volume.id {
2402            return Err(crate::workspace::WorkspaceError::ForeignGeneration);
2403        }
2404        let fork_root = GenerationRoot {
2405            volume_id: destination_id.volume_id(),
2406            root_file_id: source_root.root_file_id,
2407            file_table: source_root.file_table,
2408            parents: vec![source.id],
2409            required_features: source_root.required_features,
2410        };
2411        let encoded =
2412            encode_generation_root(&fork_root).map_err(crate::workspace::WorkspaceError::engine)?;
2413        let (generation_root, work) = self
2414            .put_encoded(
2415                ObjectKind::GenerationRoot,
2416                encoded,
2417                work,
2418                remaining(work, budget).map_err(crate::workspace::WorkspaceError::engine)?,
2419                cancellation,
2420            )
2421            .await
2422            .map_err(crate::workspace::WorkspaceError::engine)?;
2423        let proof = prove_generation_closure_async(
2424            &self.inner.objects,
2425            generation_root,
2426            closure_limits(config),
2427            remaining(work, budget).map_err(crate::workspace::WorkspaceError::engine)?,
2428            cancellation,
2429        )
2430        .await
2431        .map_err(crate::workspace::WorkspaceError::engine)?;
2432        let work = add(work, proof.work).map_err(crate::workspace::WorkspaceError::engine)?;
2433        Ok((
2434            generation_root,
2435            VerifiedForkSource {
2436                generation_root: source_object,
2437            },
2438            work,
2439        ))
2440    }
2441
2442    /// Creates a new workspace at one exact immutable source generation while
2443    /// sharing the complete file table and every unchanged content object.
2444    ///
2445    /// Only one small generation root and one creation fact are new. The source
2446    /// and destination then publish independently.
2447    pub(crate) async fn fork_workspace_measured(
2448        &self,
2449        destination: crate::WorkspaceName,
2450        source: &crate::Generation<A, O>,
2451        idempotency_key: crate::IdempotencyKey,
2452        budget: WorkBudget,
2453        cancellation: &CancellationToken,
2454    ) -> Result<OperationReceipt<crate::Workspace<A, O>>, crate::workspace::WorkspaceError> {
2455        if !Arc::ptr_eq(&self.inner, &source.workspace.volume.fs.inner) {
2456            return Err(crate::workspace::WorkspaceError::ForeignGeneration);
2457        }
2458        cancellation
2459            .check()
2460            .map_err(crate::workspace::WorkspaceError::from)?;
2461        let destination_id =
2462            crate::WorkspaceId::derive(self.inner.workspace_namespace, &destination);
2463        let config = source.workspace.volume.config;
2464        let (generation_root, verified_source, mut work) =
2465            Box::pin(self.materialize_forked_generation_root(
2466                source,
2467                destination_id,
2468                config,
2469                budget,
2470                cancellation,
2471            ))
2472            .await?;
2473        let retained = Box::pin(self.retain_verified_workspace_generation_measured(
2474            &source.workspace.volume,
2475            verified_source,
2476            RetentionKind::ForkBase,
2477            hex::encode(destination_id.into_bytes()),
2478            remaining(work, budget).map_err(crate::workspace::WorkspaceError::engine)?,
2479            cancellation,
2480        ))
2481        .await?;
2482        work = add(work, retained).map_err(crate::workspace::WorkspaceError::engine)?;
2483        if self.inner.authority.supports_generation_lineage_prefix() {
2484            let head = Box::pin(source.workspace.head_measured(
2485                remaining(work, budget).map_err(crate::workspace::WorkspaceError::engine)?,
2486                cancellation,
2487            ))
2488            .await?;
2489            work = add(work, head.work).map_err(crate::workspace::WorkspaceError::engine)?;
2490            let lineage = if head.value.id == source.id {
2491                crate::GenerationFork::PublishedPrefix
2492            } else {
2493                crate::GenerationFork::Independent
2494            };
2495            let forked = Box::pin(self.inner.authority.fork_generation_authority(
2496                crate::GenerationForkSource {
2497                    authority: volume_authority_id(source.workspace.volume.id),
2498                    generation: source.id,
2499                    lineage,
2500                },
2501                volume_authority_id(destination_id.volume_id()),
2502                idempotency_key.operation_id(),
2503                remaining(work, budget).map_err(crate::workspace::WorkspaceError::engine)?,
2504                cancellation,
2505            ))
2506            .await
2507            .map_err(crate::workspace::WorkspaceError::engine)?;
2508            work = add(work, forked.work).map_err(crate::workspace::WorkspaceError::engine)?;
2509        }
2510        let volume = Box::pin(self.publish_volume_creation(
2511            VolumeCreation {
2512                volume_id: destination_id.volume_id(),
2513                config,
2514                generation_root,
2515                operation_id: Some(idempotency_key.operation_id()),
2516            },
2517            work,
2518            budget,
2519            cancellation,
2520        ))
2521        .await
2522        .map_err(crate::workspace::WorkspaceError::engine)?;
2523        work = volume.work;
2524        let volume = volume.value;
2525        let resolved = Box::pin(volume.resolve_head_generation(
2526            remaining(work, budget).map_err(crate::workspace::WorkspaceError::engine)?,
2527            cancellation,
2528        ))
2529        .await
2530        .map_err(crate::workspace::WorkspaceError::engine)?;
2531        work = add(work, resolved.2).map_err(crate::workspace::WorkspaceError::engine)?;
2532        Ok(OperationReceipt {
2533            value: crate::Workspace {
2534                name: destination,
2535                id: destination_id,
2536                volume,
2537                #[cfg(all(feature = "native-watch", not(target_arch = "wasm32")))]
2538                source: None,
2539            },
2540            work,
2541        })
2542    }
2543
2544    pub(crate) async fn retain_workspace_generation(
2545        &self,
2546        volume: &Volume<A, O>,
2547        generation_id: GenerationId,
2548        kind: RetentionKind,
2549        label: String,
2550    ) -> Result<(), crate::workspace::WorkspaceError> {
2551        let cancellation = CancellationToken::new();
2552        let generation_root = ObjectId {
2553            kind: ObjectKind::GenerationRoot,
2554            digest: generation_id.digest(),
2555        };
2556        let proof = prove_generation_closure_async(
2557            &self.inner.objects,
2558            generation_root,
2559            closure_limits(volume.config),
2560            WorkBudget::UNBOUNDED,
2561            &cancellation,
2562        )
2563        .await
2564        .map_err(crate::workspace::WorkspaceError::engine)?;
2565        if proof.root.volume_id != volume.id {
2566            return Err(crate::workspace::WorkspaceError::ForeignGeneration);
2567        }
2568        self.retain_verified_workspace_generation(
2569            volume,
2570            VerifiedForkSource { generation_root },
2571            kind,
2572            label,
2573            &cancellation,
2574        )
2575        .await
2576    }
2577
2578    async fn retain_verified_workspace_generation(
2579        &self,
2580        volume: &Volume<A, O>,
2581        source: VerifiedForkSource,
2582        kind: RetentionKind,
2583        label: String,
2584        cancellation: &CancellationToken,
2585    ) -> Result<(), crate::workspace::WorkspaceError> {
2586        self.retain_verified_workspace_generation_measured(
2587            volume,
2588            source,
2589            kind,
2590            label,
2591            WorkBudget::UNBOUNDED,
2592            cancellation,
2593        )
2594        .await
2595        .map(|_| ())
2596    }
2597
2598    async fn retain_verified_workspace_generation_measured(
2599        &self,
2600        volume: &Volume<A, O>,
2601        source: VerifiedForkSource,
2602        kind: RetentionKind,
2603        label: String,
2604        budget: WorkBudget,
2605        cancellation: &CancellationToken,
2606    ) -> Result<WorkCounters, crate::workspace::WorkspaceError> {
2607        let generation_root = source.generation_root;
2608        let authority_id = retention_authority_id(volume.id, kind, &label);
2609        let created = self
2610            .inner
2611            .authority
2612            .create_authority(authority_id, Epoch::GENESIS, budget, cancellation)
2613            .await
2614            .map_err(crate::workspace::WorkspaceError::engine)?;
2615        let mut work = created.work;
2616        let active_head = match created.value {
2617            CreateAuthorityOutcome::Created(head) | CreateAuthorityOutcome::Existing(head) => head,
2618        };
2619        let payload = encode_retention_created(&RetentionCreated {
2620            volume_id: volume.id,
2621            kind,
2622            label,
2623            generation_root,
2624            config: volume.config,
2625        })
2626        .map_err(crate::workspace::WorkspaceError::engine)?;
2627        let operation_id = OperationId::from_bytes(authority_id.into_bytes());
2628        let (commit, _) = creation_commit(operation_id, payload);
2629        let drained = self
2630            .inner
2631            .objects
2632            .flush_before_publish(
2633                remaining(work, budget).map_err(crate::workspace::WorkspaceError::engine)?,
2634                cancellation,
2635            )
2636            .await
2637            .map_err(crate::workspace::WorkspaceError::engine)?;
2638        work = add(work, drained.work).map_err(crate::workspace::WorkspaceError::engine)?;
2639        let appended = self
2640            .inner
2641            .authority
2642            .compare_and_append(
2643                authority_id,
2644                active_head.epoch,
2645                Head::genesis(active_head.epoch),
2646                commit,
2647                remaining(work, budget).map_err(crate::workspace::WorkspaceError::engine)?,
2648                cancellation,
2649            )
2650            .await
2651            .map_err(crate::workspace::WorkspaceError::engine)?;
2652        work = add(work, appended.work).map_err(crate::workspace::WorkspaceError::engine)?;
2653        match appended.value {
2654            AppendOutcome::Committed(_) | AppendOutcome::AlreadyCommitted(_) => Ok(work),
2655            AppendOutcome::Conflict { .. }
2656            | AppendOutcome::Fenced { .. }
2657            | AppendOutcome::IdempotencyConflict { .. } => {
2658                Err(crate::workspace::WorkspaceError::RetentionConflict)
2659            }
2660        }
2661    }
2662
2663    pub(crate) async fn delete_workspace_volume(
2664        &self,
2665        volume: &Volume<A, O>,
2666        operation_id: OperationId,
2667    ) -> Result<crate::workspace::WorkspaceDelete, crate::workspace::WorkspaceError> {
2668        let cancellation = CancellationToken::new();
2669        let authority_id = volume_authority_id(volume.id);
2670        let head = self
2671            .inner
2672            .authority
2673            .head(authority_id, WorkBudget::UNBOUNDED, &cancellation)
2674            .await
2675            .map_err(crate::workspace::WorkspaceError::engine)?
2676            .value;
2677        if head.sequence == Sequence::GENESIS {
2678            return Err(crate::workspace::WorkspaceError::engine(
2679                FsError::EmptyAuthority,
2680            ));
2681        }
2682        let latest = self
2683            .inner
2684            .authority
2685            .replay(
2686                authority_id,
2687                Sequence::new(head.sequence.get().saturating_sub(1)),
2688                ReplayLimit {
2689                    records: 1,
2690                    payload_bytes: MAXIMUM_VOLUME_EVENT_BYTES,
2691                },
2692                WorkBudget::UNBOUNDED,
2693                &cancellation,
2694            )
2695            .await
2696            .map_err(crate::workspace::WorkspaceError::engine)?;
2697        let latest = latest.value.first().ok_or_else(|| {
2698            crate::workspace::WorkspaceError::engine(FsError::InvalidAuthorityHistory)
2699        })?;
2700        if let Ok(deleted) = decode_workspace_deleted(&latest.payload, MAXIMUM_VOLUME_EVENT_BYTES) {
2701            if deleted != volume.id {
2702                return Err(crate::workspace::WorkspaceError::engine(
2703                    FsError::VolumeMismatch,
2704                ));
2705            }
2706            return Ok(crate::workspace::WorkspaceDelete::AlreadyDeleted);
2707        }
2708        generation_from_record(latest, volume.id, WorkCounters::default())
2709            .map_err(crate::workspace::WorkspaceError::engine)?;
2710        let (commit, _) = creation_commit(operation_id, encode_workspace_deleted(volume.id));
2711        let appended = self
2712            .inner
2713            .authority
2714            .compare_and_append(
2715                authority_id,
2716                head.epoch,
2717                head,
2718                commit,
2719                WorkBudget::UNBOUNDED,
2720                &cancellation,
2721            )
2722            .await
2723            .map_err(crate::workspace::WorkspaceError::engine)?;
2724        Ok(match appended.value {
2725            AppendOutcome::Committed(_) | AppendOutcome::AlreadyCommitted(_) => {
2726                crate::workspace::WorkspaceDelete::Deleted
2727            }
2728            AppendOutcome::Conflict { .. } | AppendOutcome::Fenced { .. } => {
2729                crate::workspace::WorkspaceDelete::Conflict
2730            }
2731            AppendOutcome::IdempotencyConflict { .. } => {
2732                crate::workspace::WorkspaceDelete::IdempotencyConflict
2733            }
2734        })
2735    }
2736
2737    pub(crate) async fn workspace_common_ancestor(
2738        &self,
2739        source: &crate::Generation<A, O>,
2740        target: &crate::Generation<A, O>,
2741        maximum_generations: u32,
2742    ) -> Result<WorkspaceCommonAncestor, crate::workspace::WorkspaceError> {
2743        if maximum_generations == 0
2744            || !Arc::ptr_eq(&self.inner, &source.workspace.volume.fs.inner)
2745            || !Arc::ptr_eq(&self.inner, &target.workspace.volume.fs.inner)
2746        {
2747            return Err(crate::workspace::WorkspaceError::ForeignGeneration);
2748        }
2749        if source.workspace.volume.config != target.workspace.volume.config {
2750            return Err(crate::workspace::WorkspaceError::IncompatibleWorkspace);
2751        }
2752        let cancellation = CancellationToken::new();
2753        let (target_ancestors, target_truncated) = collect_generation_ancestors(
2754            &self.inner.objects,
2755            target.id,
2756            target.workspace.volume.config,
2757            maximum_generations,
2758            &cancellation,
2759        )
2760        .await?;
2761        find_first_generation_ancestor(
2762            &self.inner.objects,
2763            source.id,
2764            source.workspace.volume.config,
2765            maximum_generations,
2766            &target_ancestors,
2767            target_truncated,
2768            &cancellation,
2769        )
2770        .await
2771    }
2772
2773    pub(crate) async fn workspace_join_changes(
2774        &self,
2775        base: GenerationId,
2776        source: &crate::Generation<A, O>,
2777        maximum_changes: u32,
2778    ) -> Result<FsReceipt<GenerationDiff>, crate::workspace::WorkspaceError> {
2779        self.workspace_join_changes_bounded(
2780            base,
2781            source,
2782            maximum_changes,
2783            WorkBudget::UNBOUNDED,
2784            &CancellationToken::new(),
2785        )
2786        .await
2787    }
2788
2789    pub(crate) async fn workspace_join_changes_bounded(
2790        &self,
2791        base: GenerationId,
2792        source: &crate::Generation<A, O>,
2793        maximum_changes: u32,
2794        budget: WorkBudget,
2795        cancellation: &CancellationToken,
2796    ) -> Result<FsReceipt<GenerationDiff>, crate::workspace::WorkspaceError> {
2797        if maximum_changes == 0 || !Arc::ptr_eq(&self.inner, &source.workspace.volume.fs.inner) {
2798            return Err(crate::workspace::WorkspaceError::ForeignGeneration);
2799        }
2800        let config = source.workspace.volume.config;
2801        let mut work = WorkCounters::default();
2802        let (base_root, base_work) = read_generation_root(
2803            &self.inner.objects,
2804            ObjectId {
2805                kind: ObjectKind::GenerationRoot,
2806                digest: base.digest(),
2807            },
2808            config,
2809            remaining(work, budget).map_err(crate::workspace::WorkspaceError::engine)?,
2810            cancellation,
2811        )
2812        .await
2813        .map_err(crate::workspace::WorkspaceError::engine)?;
2814        work = add(work, base_work).map_err(crate::workspace::WorkspaceError::engine)?;
2815        let (source_root, source_work) = read_generation_root(
2816            &self.inner.objects,
2817            ObjectId {
2818                kind: ObjectKind::GenerationRoot,
2819                digest: source.id.digest(),
2820            },
2821            config,
2822            remaining(work, budget).map_err(crate::workspace::WorkspaceError::engine)?,
2823            cancellation,
2824        )
2825        .await
2826        .map_err(crate::workspace::WorkspaceError::engine)?;
2827        work = add(work, source_work).map_err(crate::workspace::WorkspaceError::engine)?;
2828        if source_root.volume_id != source.workspace.volume.id
2829            || base_root.root_file_id != source_root.root_file_id
2830        {
2831            return Err(crate::workspace::WorkspaceError::IncompatibleWorkspace);
2832        }
2833        let receipt = diff_generation_file_tables(
2834            &self.inner.objects,
2835            base_root.file_table,
2836            source_root.file_table,
2837            maximum_changes,
2838            source.workspace.volume.config,
2839            work,
2840            budget,
2841            cancellation,
2842        )
2843        .await
2844        .map_err(crate::workspace::WorkspaceError::engine)?;
2845        if receipt.value.truncated {
2846            return Err(crate::workspace::WorkspaceError::JoinLimit);
2847        }
2848        Ok(receipt)
2849    }
2850
2851    pub(crate) async fn workspace_head_state(
2852        &self,
2853        workspace: &Volume<A, O>,
2854    ) -> Result<(GenerationId, Head), crate::workspace::WorkspaceError> {
2855        let (root, head, _) = workspace
2856            .resolve_head_generation(WorkBudget::UNBOUNDED, &CancellationToken::new())
2857            .await
2858            .map_err(crate::workspace::WorkspaceError::engine)?;
2859        Ok((GenerationId::new(root.digest), head))
2860    }
2861
2862    pub(crate) async fn workspace_operation_generation(
2863        &self,
2864        workspace: &Volume<A, O>,
2865        operation_id: OperationId,
2866    ) -> Result<Option<GenerationId>, crate::workspace::WorkspaceError> {
2867        let cancellation = CancellationToken::new();
2868        let observed = self
2869            .observe_volume_operation(
2870                workspace.id,
2871                operation_id,
2872                WorkBudget::UNBOUNDED,
2873                &cancellation,
2874            )
2875            .await
2876            .map_err(crate::workspace::WorkspaceError::engine)?;
2877        observed
2878            .value
2879            .as_ref()
2880            .map(|commit| {
2881                generation_from_record(commit, workspace.id, observed.work)
2882                    .map(|object| GenerationId::new(object.digest))
2883                    .map_err(crate::workspace::WorkspaceError::engine)
2884            })
2885            .transpose()
2886    }
2887
2888    pub(crate) async fn workspace_conflict_record(
2889        &self,
2890        workspace: &Volume<A, O>,
2891        generation: GenerationId,
2892        file_id: FileId,
2893    ) -> Result<Option<FileRecord>, crate::workspace::WorkspaceError> {
2894        let cancellation = CancellationToken::new();
2895        let generation_root = ObjectId {
2896            kind: ObjectKind::GenerationRoot,
2897            digest: generation.digest(),
2898        };
2899        let (root, work) = read_generation_root(
2900            &self.inner.objects,
2901            generation_root,
2902            workspace.config,
2903            WorkBudget::UNBOUNDED,
2904            &cancellation,
2905        )
2906        .await
2907        .map_err(crate::workspace::WorkspaceError::engine)?;
2908        let lookup = lookup_file_record_async(
2909            &self.inner.objects,
2910            root.file_table,
2911            file_id,
2912            decode_limits(workspace.config),
2913            WorkBudget::UNBOUNDED,
2914            &cancellation,
2915        )
2916        .await
2917        .map_err(crate::workspace::WorkspaceError::engine)?;
2918        let _ = work;
2919        Ok(lookup.record)
2920    }
2921
2922    pub(crate) async fn workspace_conflict_bytes(
2923        &self,
2924        workspace: &Volume<A, O>,
2925        record: FileRecord,
2926    ) -> Result<Option<Bytes>, crate::workspace::WorkspaceError> {
2927        let logical_bytes = match record.payload {
2928            FilePayload::InlineRegular(data) => {
2929                return Ok(Some(Bytes::copy_from_slice(data.as_bytes())));
2930            }
2931            FilePayload::Regular { logical_bytes, .. } => logical_bytes,
2932            _ => return Ok(None),
2933        };
2934        if logical_bytes > workspace.config.limits.maximum_read_bytes {
2935            return Ok(None);
2936        }
2937        let read = read_file_range_async(
2938            &self.inner.objects,
2939            FileRangeRequest {
2940                record,
2941                range: ByteRange {
2942                    offset: 0,
2943                    length: logical_bytes,
2944                },
2945                maximum_spans: workspace.config.limits.maximum_directory_page_entries,
2946                limits: decode_limits(workspace.config),
2947                budget: WorkBudget::UNBOUNDED,
2948            },
2949            &CancellationToken::new(),
2950        )
2951        .await
2952        .map_err(crate::workspace::WorkspaceError::engine)?;
2953        Ok(Some(read.bytes))
2954    }
2955
2956    pub(crate) async fn workspace_conflict_metadata(
2957        &self,
2958        workspace: &Volume<A, O>,
2959        record: FileRecord,
2960    ) -> Result<Vec<u8>, crate::workspace::WorkspaceError> {
2961        let read = self
2962            .inner
2963            .objects
2964            .read(
2965                record.metadata,
2966                workspace.config.limits.maximum_object_bytes,
2967                WorkBudget::UNBOUNDED,
2968                &CancellationToken::new(),
2969            )
2970            .await
2971            .map_err(crate::workspace::WorkspaceError::engine)?;
2972        decode_file_metadata(&read.value.bytes, decode_limits(workspace.config))
2973            .map_err(crate::workspace::WorkspaceError::engine)?;
2974        Ok(read.value.bytes.to_vec())
2975    }
2976
2977    pub(crate) async fn stage_merge_regular_record(
2978        &self,
2979        workspace: &Volume<A, O>,
2980        mut record: FileRecord,
2981        bytes: Bytes,
2982    ) -> Result<FileRecord, crate::workspace::WorkspaceError> {
2983        if record.kind != FileKind::Regular
2984            || u64::try_from(bytes.len()).unwrap_or(u64::MAX)
2985                > workspace.config.limits.maximum_read_bytes
2986        {
2987            return Err(crate::workspace::WorkspaceError::InvalidMergeResolution);
2988        }
2989        if bytes.len() <= crate::kernel::MAXIMUM_INLINE_FILE_BYTES {
2990            record.payload = FilePayload::InlineRegular(
2991                InlineFileData::new(&bytes).map_err(crate::workspace::WorkspaceError::engine)?,
2992            );
2993            return Ok(record);
2994        }
2995        let maximum_blob_bytes = u64::try_from(bytes.len()).unwrap_or(u64::MAX).max(1);
2996        let mut source = std::io::Cursor::new(bytes);
2997        let blob = build_blob_async(
2998            &self.inner.objects,
2999            &mut source,
3000            BlobBuildOptions {
3001                chunk_bytes: u32::try_from(
3002                    workspace
3003                        .config
3004                        .limits
3005                        .maximum_object_bytes
3006                        .min(1024 * 1024),
3007                )
3008                .unwrap_or(u32::MAX)
3009                .max(1),
3010                page_items: workspace.config.limits.maximum_directory_page_entries,
3011                page_bytes: u32::try_from(workspace.config.limits.maximum_object_bytes)
3012                    .unwrap_or(u32::MAX)
3013                    .max(1),
3014                maximum_blob_bytes,
3015            },
3016            WorkBudget::UNBOUNDED,
3017            &CancellationToken::new(),
3018        )
3019        .await
3020        .map_err(crate::workspace::WorkspaceError::engine)?;
3021        let payload = apply_regular_mutation_async(
3022            &self.inner.objects,
3023            FilePayload::InlineRegular(
3024                InlineFileData::new(&[]).map_err(crate::workspace::WorkspaceError::engine)?,
3025            ),
3026            RegularMutation::Write {
3027                offset: 0,
3028                length: blob.logical_bytes,
3029                content: blob.root,
3030                content_offset: 0,
3031            },
3032            workspace.config,
3033            WorkBudget::UNBOUNDED,
3034            &CancellationToken::new(),
3035        )
3036        .await
3037        .map_err(crate::workspace::WorkspaceError::engine)?;
3038        record.payload = payload.payload;
3039        Ok(record)
3040    }
3041
3042    pub(crate) async fn stage_merge_metadata(
3043        &self,
3044        workspace: &Volume<A, O>,
3045        bytes: &[u8],
3046    ) -> Result<ObjectId, crate::workspace::WorkspaceError> {
3047        decode_file_metadata(bytes, decode_limits(workspace.config))
3048            .map_err(crate::workspace::WorkspaceError::engine)?;
3049        let (object, _) = self
3050            .put_encoded(
3051                ObjectKind::Metadata,
3052                bytes.to_vec(),
3053                WorkCounters::default(),
3054                WorkBudget::UNBOUNDED,
3055                &CancellationToken::new(),
3056            )
3057            .await
3058            .map_err(crate::workspace::WorkspaceError::engine)?;
3059        Ok(object)
3060    }
3061
3062    pub(crate) async fn restore_workspace_generation(
3063        &self,
3064        workspace: &Volume<A, O>,
3065        generation: GenerationId,
3066        expected: GenerationId,
3067        operation_id: OperationId,
3068        permit: PublicationPermit,
3069    ) -> Result<crate::workspace::WorkspaceRestoreOutcome, crate::workspace::WorkspaceError> {
3070        let cancellation = CancellationToken::new();
3071        if let Some(recorded) = self
3072            .workspace_operation_generation(workspace, operation_id)
3073            .await?
3074        {
3075            return Ok(if recorded == generation {
3076                crate::workspace::WorkspaceRestoreOutcome::AlreadyRestored(recorded)
3077            } else {
3078                crate::workspace::WorkspaceRestoreOutcome::IdempotencyConflict
3079            });
3080        }
3081        let (current, authority_head, _) = workspace
3082            .resolve_head_generation(WorkBudget::UNBOUNDED, &cancellation)
3083            .await
3084            .map_err(crate::workspace::WorkspaceError::engine)?;
3085        let current = GenerationId::new(current.digest);
3086        if current != expected {
3087            return Ok(crate::workspace::WorkspaceRestoreOutcome::Stale(current));
3088        }
3089        if current == generation {
3090            return Ok(crate::workspace::WorkspaceRestoreOutcome::Current(current));
3091        }
3092        let generation_root = ObjectId {
3093            kind: ObjectKind::GenerationRoot,
3094            digest: generation.digest(),
3095        };
3096        let (root, _) = read_generation_root(
3097            &self.inner.objects,
3098            generation_root,
3099            workspace.config,
3100            WorkBudget::UNBOUNDED,
3101            &cancellation,
3102        )
3103        .await
3104        .map_err(crate::workspace::WorkspaceError::engine)?;
3105        if root.volume_id != workspace.id {
3106            return Err(crate::workspace::WorkspaceError::ForeignGeneration);
3107        }
3108        let publication = publish_generation_async_with_permit(
3109            &self.inner.objects,
3110            &self.inner.authority,
3111            PublishGenerationRequest {
3112                authority_id: volume_authority_id(workspace.id),
3113                volume_id: workspace.id,
3114                epoch: authority_head.epoch,
3115                expected: authority_head,
3116                operation_id,
3117                generation_root,
3118            },
3119            PublicationIntent::guarded(None, permit),
3120            closure_limits(workspace.config),
3121            WorkBudget::UNBOUNDED,
3122            &cancellation,
3123        )
3124        .await
3125        .map_err(crate::workspace::WorkspaceError::engine)?;
3126        Ok(match publication.outcome {
3127            AppendOutcome::Committed(_) => {
3128                crate::workspace::WorkspaceRestoreOutcome::Restored(publication.proof.generation_id)
3129            }
3130            AppendOutcome::AlreadyCommitted(_) => {
3131                crate::workspace::WorkspaceRestoreOutcome::AlreadyRestored(
3132                    publication.proof.generation_id,
3133                )
3134            }
3135            AppendOutcome::Conflict { .. } => {
3136                let (actual, _, _) = workspace
3137                    .resolve_head_generation(WorkBudget::UNBOUNDED, &cancellation)
3138                    .await
3139                    .map_err(crate::workspace::WorkspaceError::engine)?;
3140                crate::workspace::WorkspaceRestoreOutcome::Stale(GenerationId::new(actual.digest))
3141            }
3142            AppendOutcome::Fenced { .. } => crate::workspace::WorkspaceRestoreOutcome::Fenced,
3143            AppendOutcome::IdempotencyConflict { .. } => {
3144                crate::workspace::WorkspaceRestoreOutcome::IdempotencyConflict
3145            }
3146        })
3147    }
3148
3149    #[allow(clippy::too_many_lines)]
3150    pub(crate) async fn live_rebase_workspace(
3151        &self,
3152        request: WorkspaceRebaseRequest<'_, A, O>,
3153    ) -> Result<WorkspaceJoinOutcome, crate::workspace::WorkspaceError> {
3154        let WorkspaceRebaseRequest {
3155            target,
3156            operation_id,
3157            maximum_generations,
3158            maximum_changes,
3159            maximum_conflicts,
3160        } = request;
3161        if maximum_generations == 0 || maximum_changes == 0 || maximum_conflicts == 0 {
3162            return Err(crate::workspace::WorkspaceError::JoinLimit);
3163        }
3164        let cancellation = CancellationToken::new();
3165        let (target_object, expected_head, _) = target
3166            .resolve_head_generation(WorkBudget::UNBOUNDED, &cancellation)
3167            .await
3168            .map_err(crate::workspace::WorkspaceError::engine)?;
3169        let (target_root, _) = read_generation_root(
3170            &self.inner.objects,
3171            target_object,
3172            target.config,
3173            WorkBudget::UNBOUNDED,
3174            &cancellation,
3175        )
3176        .await
3177        .map_err(crate::workspace::WorkspaceError::engine)?;
3178
3179        let mut cursor = target_object;
3180        let mut adopted = None;
3181        for _ in 0..maximum_generations {
3182            let (root, _) = read_generation_root(
3183                &self.inner.objects,
3184                cursor,
3185                target.config,
3186                WorkBudget::UNBOUNDED,
3187                &cancellation,
3188            )
3189            .await
3190            .map_err(crate::workspace::WorkspaceError::engine)?;
3191            if root.volume_id != target.id {
3192                adopted = Some((cursor, root));
3193                break;
3194            }
3195            let Some(parent) = root.parents.first() else {
3196                return Err(crate::workspace::WorkspaceError::NotFork);
3197            };
3198            cursor = ObjectId {
3199                kind: ObjectKind::GenerationRoot,
3200                digest: parent.digest(),
3201            };
3202        }
3203        let Some((base_object, base_root)) = adopted else {
3204            return Err(crate::workspace::WorkspaceError::LineageLimit);
3205        };
3206        let source = self
3207            .open_volume(base_root.volume_id, WorkBudget::UNBOUNDED, &cancellation)
3208            .await
3209            .map_err(crate::workspace::WorkspaceError::engine)?
3210            .value;
3211        if source.config != target.config || base_root.root_file_id != target_root.root_file_id {
3212            return Err(crate::workspace::WorkspaceError::IncompatibleWorkspace);
3213        }
3214        let (source_object, _, _) = source
3215            .resolve_head_generation(WorkBudget::UNBOUNDED, &cancellation)
3216            .await
3217            .map_err(crate::workspace::WorkspaceError::engine)?;
3218        if source_object == base_object {
3219            return Ok(WorkspaceJoinOutcome::NoChanges(GenerationId::new(
3220                target_object.digest,
3221            )));
3222        }
3223        let (source_root, _) = read_generation_root(
3224            &self.inner.objects,
3225            source_object,
3226            target.config,
3227            WorkBudget::UNBOUNDED,
3228            &cancellation,
3229        )
3230        .await
3231        .map_err(crate::workspace::WorkspaceError::engine)?;
3232        if source_root.volume_id != source.id
3233            || source_root.root_file_id != target_root.root_file_id
3234        {
3235            return Err(crate::workspace::WorkspaceError::IncompatibleWorkspace);
3236        }
3237
3238        let normalize = |root: &GenerationRoot| GenerationRoot {
3239            volume_id: target.id,
3240            root_file_id: target_root.root_file_id,
3241            file_table: root.file_table,
3242            parents: Vec::new(),
3243            required_features: root.required_features,
3244        };
3245        let normalized_base = normalize(&base_root);
3246        let normalized_source = normalize(&source_root);
3247        let (normalized_base_object, _) = self
3248            .put_encoded(
3249                ObjectKind::GenerationRoot,
3250                encode_generation_root(&normalized_base)
3251                    .map_err(crate::workspace::WorkspaceError::engine)?,
3252                WorkCounters::default(),
3253                WorkBudget::UNBOUNDED,
3254                &cancellation,
3255            )
3256            .await
3257            .map_err(crate::workspace::WorkspaceError::engine)?;
3258        let (normalized_source_object, _) = self
3259            .put_encoded(
3260                ObjectKind::GenerationRoot,
3261                encode_generation_root(&normalized_source)
3262                    .map_err(crate::workspace::WorkspaceError::engine)?,
3263                WorkCounters::default(),
3264                WorkBudget::UNBOUNDED,
3265                &cancellation,
3266            )
3267            .await
3268            .map_err(crate::workspace::WorkspaceError::engine)?;
3269        let merged = merge_generation_async(
3270            &self.inner.objects,
3271            MergeGenerationRequest {
3272                base_generation: normalized_base_object,
3273                base: normalized_base,
3274                ours_generation: Some(normalized_source_object),
3275                ours: normalized_source,
3276                theirs_generation: target_object,
3277                theirs: target_root,
3278                retain_theirs_parent: false,
3279                maximum_changes,
3280                maximum_conflicts,
3281                resolutions: BTreeMap::new(),
3282            },
3283            decode_limits(target.config),
3284            WorkBudget::UNBOUNDED,
3285            &cancellation,
3286        )
3287        .await
3288        .map_err(crate::workspace::WorkspaceError::engine)?;
3289        let merged_root = match merged.value {
3290            MergeGenerationOutcome::Conflicted {
3291                conflicts,
3292                truncated,
3293            } => return Ok(WorkspaceJoinOutcome::Conflicted(conflicts, truncated)),
3294            MergeGenerationOutcome::Prepared { root, .. } => root,
3295        };
3296        let rebased_root = GenerationRoot {
3297            volume_id: target.id,
3298            root_file_id: merged_root.root_file_id,
3299            file_table: merged_root.file_table,
3300            parents: vec![GenerationId::new(source_object.digest)],
3301            required_features: merged_root.required_features,
3302        };
3303        let (candidate, _) = self
3304            .put_encoded(
3305                ObjectKind::GenerationRoot,
3306                encode_generation_root(&rebased_root)
3307                    .map_err(crate::workspace::WorkspaceError::engine)?,
3308                WorkCounters::default(),
3309                WorkBudget::UNBOUNDED,
3310                &cancellation,
3311            )
3312            .await
3313            .map_err(crate::workspace::WorkspaceError::engine)?;
3314        let publication = publish_generation_async(
3315            &self.inner.objects,
3316            &self.inner.authority,
3317            PublishGenerationRequest {
3318                authority_id: volume_authority_id(target.id),
3319                volume_id: target.id,
3320                epoch: expected_head.epoch,
3321                expected: expected_head,
3322                operation_id,
3323                generation_root: candidate,
3324            },
3325            closure_limits(target.config),
3326            WorkBudget::UNBOUNDED,
3327            &cancellation,
3328        )
3329        .await
3330        .map_err(crate::workspace::WorkspaceError::engine)?;
3331        Ok(match publication.outcome {
3332            AppendOutcome::Committed(_) => {
3333                WorkspaceJoinOutcome::Applied(publication.proof.generation_id)
3334            }
3335            AppendOutcome::AlreadyCommitted(_) => {
3336                WorkspaceJoinOutcome::AlreadyApplied(publication.proof.generation_id)
3337            }
3338            AppendOutcome::Conflict { .. } => {
3339                let (actual, _, _) = target
3340                    .resolve_head_generation(WorkBudget::UNBOUNDED, &cancellation)
3341                    .await
3342                    .map_err(crate::workspace::WorkspaceError::engine)?;
3343                WorkspaceJoinOutcome::Stale(GenerationId::new(actual.digest))
3344            }
3345            AppendOutcome::Fenced { .. } => WorkspaceJoinOutcome::Fenced,
3346            AppendOutcome::IdempotencyConflict { .. } => WorkspaceJoinOutcome::IdempotencyConflict,
3347        })
3348    }
3349
3350    #[allow(clippy::too_many_lines)]
3351    pub(crate) async fn apply_workspace_join(
3352        &self,
3353        request: WorkspaceJoinRequest<'_, A, O>,
3354    ) -> Result<WorkspaceJoinOutcome, crate::workspace::WorkspaceError> {
3355        let operation_context = request.operation_context();
3356        let WorkspaceJoinRequest {
3357            target,
3358            base,
3359            source,
3360            expected_target,
3361            expected_head,
3362            history,
3363            operation_id,
3364            permit,
3365            maximum_generations,
3366            maximum_changes,
3367            maximum_conflicts,
3368            resolutions,
3369        } = request;
3370        if maximum_generations == 0 || maximum_changes == 0 || maximum_conflicts == 0 {
3371            return Err(crate::workspace::WorkspaceError::JoinLimit);
3372        }
3373        if !Arc::ptr_eq(&self.inner, &source.workspace.volume.fs.inner)
3374            || source.workspace.volume.config != target.config
3375        {
3376            return Err(crate::workspace::WorkspaceError::IncompatibleWorkspace);
3377        }
3378        let cancellation = CancellationToken::new();
3379        let (current_target_object, _, _) = target
3380            .resolve_head_generation(WorkBudget::UNBOUNDED, &cancellation)
3381            .await
3382            .map_err(crate::workspace::WorkspaceError::engine)?;
3383        let current_target = GenerationId::new(current_target_object.digest);
3384        let target_object = ObjectId {
3385            kind: ObjectKind::GenerationRoot,
3386            digest: expected_target.digest(),
3387        };
3388        let base_object = ObjectId {
3389            kind: ObjectKind::GenerationRoot,
3390            digest: base.id.digest(),
3391        };
3392        let source_object = ObjectId {
3393            kind: ObjectKind::GenerationRoot,
3394            digest: source.id.digest(),
3395        };
3396        let (base_root, _) = read_generation_root(
3397            &self.inner.objects,
3398            base_object,
3399            target.config,
3400            WorkBudget::UNBOUNDED,
3401            &cancellation,
3402        )
3403        .await
3404        .map_err(crate::workspace::WorkspaceError::engine)?;
3405        let (source_root, _) = read_generation_root(
3406            &self.inner.objects,
3407            source_object,
3408            target.config,
3409            WorkBudget::UNBOUNDED,
3410            &cancellation,
3411        )
3412        .await
3413        .map_err(crate::workspace::WorkspaceError::engine)?;
3414        let (target_root, _) = read_generation_root(
3415            &self.inner.objects,
3416            target_object,
3417            target.config,
3418            WorkBudget::UNBOUNDED,
3419            &cancellation,
3420        )
3421        .await
3422        .map_err(crate::workspace::WorkspaceError::engine)?;
3423        if source_root.volume_id != source.workspace.volume.id
3424            || target_root.volume_id != target.id
3425            || base_root.root_file_id != target_root.root_file_id
3426            || source_root.root_file_id != target_root.root_file_id
3427        {
3428            return Err(crate::workspace::WorkspaceError::IncompatibleWorkspace);
3429        }
3430        let normalized_base = GenerationRoot {
3431            volume_id: target.id,
3432            root_file_id: target_root.root_file_id,
3433            file_table: base_root.file_table,
3434            parents: base_root.parents,
3435            required_features: base_root.required_features,
3436        };
3437        let normalized_source = GenerationRoot {
3438            volume_id: target.id,
3439            root_file_id: target_root.root_file_id,
3440            file_table: source_root.file_table,
3441            parents: source_root.parents,
3442            required_features: source_root.required_features,
3443        };
3444        let (normalized_base_object, _) = self
3445            .put_encoded(
3446                ObjectKind::GenerationRoot,
3447                encode_generation_root(&normalized_base)
3448                    .map_err(crate::workspace::WorkspaceError::engine)?,
3449                WorkCounters::default(),
3450                WorkBudget::UNBOUNDED,
3451                &cancellation,
3452            )
3453            .await
3454            .map_err(crate::workspace::WorkspaceError::engine)?;
3455        let (normalized_source_object, _) = self
3456            .put_encoded(
3457                ObjectKind::GenerationRoot,
3458                encode_generation_root(&normalized_source)
3459                    .map_err(crate::workspace::WorkspaceError::engine)?,
3460                WorkCounters::default(),
3461                WorkBudget::UNBOUNDED,
3462                &cancellation,
3463            )
3464            .await
3465            .map_err(crate::workspace::WorkspaceError::engine)?;
3466        let merged = merge_generation_async(
3467            &self.inner.objects,
3468            MergeGenerationRequest {
3469                base_generation: normalized_base_object,
3470                base: normalized_base,
3471                ours_generation: Some(target_object),
3472                ours: target_root.clone(),
3473                theirs_generation: normalized_source_object,
3474                theirs: normalized_source,
3475                retain_theirs_parent: history == crate::workspace::JoinHistory::Merge,
3476                maximum_changes,
3477                maximum_conflicts,
3478                resolutions,
3479            },
3480            decode_limits(target.config),
3481            WorkBudget::UNBOUNDED,
3482            &cancellation,
3483        )
3484        .await
3485        .map_err(crate::workspace::WorkspaceError::engine)?;
3486        let (candidate, merged_root) = match merged.value {
3487            MergeGenerationOutcome::Conflicted {
3488                conflicts,
3489                truncated,
3490            } => return Ok(WorkspaceJoinOutcome::Conflicted(conflicts, truncated)),
3491            MergeGenerationOutcome::Prepared {
3492                generation_root,
3493                root,
3494                ..
3495            } => (generation_root, root),
3496        };
3497        if merged_root.file_table == target_root.file_table {
3498            return Ok(WorkspaceJoinOutcome::NoChanges(current_target));
3499        }
3500        let publication = publish_generation_async_with_permit(
3501            &self.inner.objects,
3502            &self.inner.authority,
3503            PublishGenerationRequest {
3504                authority_id: volume_authority_id(target.id),
3505                volume_id: target.id,
3506                epoch: expected_head.epoch,
3507                expected: expected_head,
3508                operation_id,
3509                generation_root: candidate,
3510            },
3511            PublicationIntent::guarded(Some(operation_context), permit),
3512            closure_limits(target.config),
3513            WorkBudget::UNBOUNDED,
3514            &cancellation,
3515        )
3516        .await
3517        .map_err(crate::workspace::WorkspaceError::engine)?;
3518        Ok(match publication.outcome {
3519            AppendOutcome::Committed(_) => {
3520                WorkspaceJoinOutcome::Applied(publication.proof.generation_id)
3521            }
3522            AppendOutcome::AlreadyCommitted(_) => {
3523                WorkspaceJoinOutcome::AlreadyApplied(publication.proof.generation_id)
3524            }
3525            AppendOutcome::Conflict { .. } => {
3526                let (actual, _, _) = target
3527                    .resolve_head_generation(WorkBudget::UNBOUNDED, &cancellation)
3528                    .await
3529                    .map_err(crate::workspace::WorkspaceError::engine)?;
3530                WorkspaceJoinOutcome::Stale(GenerationId::new(actual.digest))
3531            }
3532            AppendOutcome::Fenced { .. } => WorkspaceJoinOutcome::Fenced,
3533            AppendOutcome::IdempotencyConflict { .. } => WorkspaceJoinOutcome::IdempotencyConflict,
3534        })
3535    }
3536
3537    /// Executes one active local-residency prediction through this filesystem's
3538    /// ordinary authenticated object backend and shared cache.
3539    ///
3540    /// # Errors
3541    ///
3542    /// Returns a stale-token, cancellation, authentication, storage, or exact
3543    /// work-budget failure. It never changes filesystem authority.
3544    pub async fn execute_residency(
3545        &self,
3546        speculator: &crate::ResidencySpeculator,
3547        permit: crate::ResidencyPermit,
3548        budget: WorkBudget,
3549        cancellation: &CancellationToken,
3550    ) -> crate::storage::ObjectResult<u64> {
3551        crate::execute_residency(
3552            speculator,
3553            &self.inner.objects,
3554            permit,
3555            budget,
3556            cancellation,
3557        )
3558        .await
3559    }
3560
3561    /// Creates a fresh volume identity and its authenticated empty generation.
3562    ///
3563    /// # Errors
3564    ///
3565    /// Returns a typed measured failure for invalid/unsupported semantics,
3566    /// cancellation, storage, authentication, authority conflict, or budget.
3567    pub async fn create_volume(
3568        &self,
3569        config: VolumeConfig,
3570        budget: WorkBudget,
3571        cancellation: &CancellationToken,
3572    ) -> FsResult<Volume<A, O>> {
3573        self.create_volume_with_id(VolumeId::new(), config, budget, cancellation)
3574            .await
3575    }
3576
3577    /// Idempotently creates one caller-selected volume identity.
3578    ///
3579    /// # Errors
3580    ///
3581    /// Returns a typed measured failure for invalid/unsupported semantics,
3582    /// cancellation, storage, authentication, authority conflict, or budget.
3583    pub async fn create_volume_with_id(
3584        &self,
3585        volume_id: VolumeId,
3586        config: VolumeConfig,
3587        budget: WorkBudget,
3588        cancellation: &CancellationToken,
3589    ) -> FsResult<Volume<A, O>> {
3590        self.create_volume_with_id_operation(volume_id, config, None, budget, cancellation)
3591            .await
3592    }
3593
3594    async fn create_volume_with_id_operation(
3595        &self,
3596        volume_id: VolumeId,
3597        config: VolumeConfig,
3598        operation_id: Option<OperationId>,
3599        budget: WorkBudget,
3600        cancellation: &CancellationToken,
3601    ) -> FsResult<Volume<A, O>> {
3602        cancellation
3603            .check()
3604            .map_err(|error| OperationFailure::before_work(error.into()))?;
3605        validate_volume_capabilities(config, self.inner.capabilities)?;
3606        let (generation_root, mut work) = self
3607            .build_empty_generation(volume_id, config, budget, cancellation)
3608            .await?;
3609        let closure_limits = closure_limits(config);
3610        let remaining_budget = remaining(work, budget)?;
3611        let proof = prove_generation_closure_async(
3612            &self.inner.objects,
3613            generation_root,
3614            closure_limits,
3615            remaining_budget,
3616            cancellation,
3617        )
3618        .await
3619        .map_err(|failure| failure.map_with_prior_work(work, Into::into))?;
3620        work = add(work, proof.work)?;
3621        self.publish_volume_creation(
3622            VolumeCreation {
3623                volume_id,
3624                config,
3625                generation_root,
3626                operation_id,
3627            },
3628            work,
3629            budget,
3630            cancellation,
3631        )
3632        .await
3633    }
3634
3635    /// Opens one volume by reconstructing its immutable configuration from the
3636    /// first bounded authority fact.
3637    ///
3638    /// # Errors
3639    ///
3640    /// Returns a typed measured failure when authority is absent/corrupt, its
3641    /// creation fact is invalid, semantics are unsupported, or work is denied.
3642    pub async fn open_volume(
3643        &self,
3644        volume_id: VolumeId,
3645        budget: WorkBudget,
3646        cancellation: &CancellationToken,
3647    ) -> FsResult<Volume<A, O>> {
3648        cancellation
3649            .check()
3650            .map_err(|error| OperationFailure::before_work(error.into()))?;
3651        let (creation, work) = self
3652            .read_creation(volume_id, WorkCounters::default(), budget, cancellation)
3653            .await?;
3654        validate_volume_capabilities(creation.config, self.inner.capabilities)
3655            .map_err(|failure| OperationFailure::new(failure.error, work))?;
3656        Ok(FsReceipt {
3657            value: Volume {
3658                fs: self.clone(),
3659                id: volume_id,
3660                config: creation.config,
3661            },
3662            work,
3663        })
3664    }
3665
3666    /// Reads one exact immutable object for resumable manifest transfer.
3667    ///
3668    /// # Errors
3669    ///
3670    /// Returns typed identity, corruption, cancellation, backend, size, or
3671    /// work-budget failures. The object is always reauthenticated by storage.
3672    pub async fn export_object(
3673        &self,
3674        object_id: ObjectId,
3675        maximum_bytes: u64,
3676        budget: WorkBudget,
3677        cancellation: &CancellationToken,
3678    ) -> FsResult<crate::storage::ObjectRead> {
3679        let receipt = self
3680            .inner
3681            .objects
3682            .read(object_id, maximum_bytes, budget, cancellation)
3683            .await
3684            .map_err(|failure| OperationFailure::new(failure.error.into(), *failure.work))?;
3685        Ok(FsReceipt {
3686            value: receipt.value,
3687            work: receipt.work,
3688        })
3689    }
3690
3691    /// Idempotently imports one immutable object under its canonical identity.
3692    ///
3693    /// # Errors
3694    ///
3695    /// Rejects an identity/body mismatch before admitting visible authority and
3696    /// returns exact cancellation, backend, allocation, or work failures.
3697    pub async fn import_object(
3698        &self,
3699        object_id: ObjectId,
3700        bytes: Bytes,
3701        budget: WorkBudget,
3702        cancellation: &CancellationToken,
3703    ) -> FsResult<()> {
3704        let receipt = self
3705            .inner
3706            .objects
3707            .put(object_id, bytes, budget, cancellation)
3708            .await
3709            .map_err(|failure| OperationFailure::new(failure.error.into(), *failure.work))?;
3710        Ok(FsReceipt {
3711            value: (),
3712            work: receipt.work,
3713        })
3714    }
3715
3716    /// Reads one ordered resumable page from a canonical generation manifest.
3717    ///
3718    /// Backends with a physical multi-get primitive may service the complete
3719    /// page in one operation. The terminal cursor returns an empty terminal
3720    /// page without storage work.
3721    ///
3722    /// # Errors
3723    ///
3724    /// Returns typed manifest, cursor, cancellation, allocation, storage, or
3725    /// bounded-work failures with exact retained-buffer accounting.
3726    pub async fn export_generation_batch(
3727        &self,
3728        manifest: &GenerationExportManifest,
3729        cursor: TransferCursor,
3730        maximum_objects: u32,
3731        maximum_object_bytes: u64,
3732        budget: WorkBudget,
3733        cancellation: &CancellationToken,
3734    ) -> FsResult<GenerationTransferBatch> {
3735        export_generation_batch_async(
3736            &self.inner.objects,
3737            manifest,
3738            cursor,
3739            maximum_objects,
3740            maximum_object_bytes,
3741            budget,
3742            cancellation,
3743        )
3744        .await
3745        .map_err(|failure| OperationFailure::new(map_transfer_error(failure.error), *failure.work))
3746    }
3747
3748    /// Idempotently imports one manifest-aligned page of immutable bodies.
3749    ///
3750    /// A failed page may leave a valid immutable prefix; retrying the same
3751    /// cursor and bodies is exactly safe because every put authenticates its
3752    /// canonical object identity.
3753    ///
3754    /// # Errors
3755    ///
3756    /// Returns typed manifest, cursor/body bound, cancellation, storage, or
3757    /// exact-work failures while retaining imported-prefix work.
3758    pub async fn import_generation_batch(
3759        &self,
3760        manifest: &GenerationExportManifest,
3761        cursor: TransferCursor,
3762        bodies: &[Bytes],
3763        maximum_objects: u32,
3764        budget: WorkBudget,
3765        cancellation: &CancellationToken,
3766    ) -> FsResult<TransferCursor> {
3767        import_generation_batch_async(
3768            &self.inner.objects,
3769            manifest,
3770            cursor,
3771            bodies,
3772            maximum_objects,
3773            budget,
3774            cancellation,
3775        )
3776        .await
3777        .map_err(|failure| OperationFailure::new(map_transfer_error(failure.error), *failure.work))
3778    }
3779
3780    /// Restores a volume authority from a fully imported immutable generation.
3781    ///
3782    /// The complete closure is authenticated before the first authority fact
3783    /// can expose it. Retrying with the same caller-owned operation identity is
3784    /// idempotent; changing its input is rejected by authority publication.
3785    ///
3786    /// # Errors
3787    ///
3788    /// Returns typed configuration, volume-identity, closure, authority,
3789    /// cancellation, or bounded-work failures.
3790    pub async fn restore_volume(
3791        &self,
3792        manifest: &GenerationExportManifest,
3793        operation_id: OperationId,
3794        budget: WorkBudget,
3795        cancellation: &CancellationToken,
3796    ) -> FsResult<Volume<A, O>> {
3797        cancellation
3798            .check()
3799            .map_err(|error| OperationFailure::before_work(error.into()))?;
3800        validate_volume_capabilities(manifest.config, self.inner.capabilities)?;
3801        let proof = authenticate_generation_export_manifest_async(
3802            &self.inner.objects,
3803            manifest,
3804            closure_limits(manifest.config),
3805            budget,
3806            cancellation,
3807        )
3808        .await
3809        .map_err(|failure| {
3810            OperationFailure::new(map_transfer_error(failure.error), *failure.work)
3811        })?;
3812        let work = proof.work;
3813        self.publish_volume_creation(
3814            VolumeCreation {
3815                volume_id: manifest.volume_id,
3816                config: manifest.config,
3817                generation_root: manifest.generation_root,
3818                operation_id: Some(operation_id),
3819            },
3820            work,
3821            budget,
3822            cancellation,
3823        )
3824        .await
3825    }
3826
3827    async fn publish_volume_creation(
3828        &self,
3829        creation: VolumeCreation,
3830        mut work: WorkCounters,
3831        budget: WorkBudget,
3832        cancellation: &CancellationToken,
3833    ) -> FsResult<Volume<A, O>> {
3834        let VolumeCreation {
3835            volume_id,
3836            config,
3837            generation_root,
3838            operation_id,
3839        } = creation;
3840        let authority_id = volume_authority_id(volume_id);
3841        let created = self
3842            .inner
3843            .authority
3844            .create_authority(
3845                authority_id,
3846                Epoch::GENESIS,
3847                remaining(work, budget)?,
3848                cancellation,
3849            )
3850            .await
3851            .map_err(|failure| failure.map_with_prior_work(work, Into::into))?;
3852        work = add(work, created.work)?;
3853        let active_head = match created.value {
3854            CreateAuthorityOutcome::Created(head) | CreateAuthorityOutcome::Existing(head) => head,
3855        };
3856        let event = encode_volume_created(VolumeCreated {
3857            volume_id,
3858            config,
3859            initial_generation_root: generation_root,
3860        })
3861        .map_err(|error| OperationFailure::new(error.into(), work))?;
3862        let operation_id = if let Some(operation_id) = operation_id {
3863            operation_id
3864        } else {
3865            let (operation_id, identity_work) = derived_operation_id(volume_id);
3866            work = add(work, identity_work)?;
3867            operation_id
3868        };
3869        let (commit, encoding_work) = creation_commit(operation_id, event);
3870        work = add(work, encoding_work)?;
3871        work.verify(budget)
3872            .map_err(|error| OperationFailure::new(error.into(), work))?;
3873        let drained = self
3874            .inner
3875            .objects
3876            .flush_before_publish(remaining(work, budget)?, cancellation)
3877            .await
3878            .map_err(|failure| failure.map_with_prior_work(work, Into::into))?;
3879        work = add(work, drained.work)?;
3880        let appended = self
3881            .inner
3882            .authority
3883            .compare_and_append(
3884                authority_id,
3885                active_head.epoch,
3886                Head::genesis(active_head.epoch),
3887                commit,
3888                remaining(work, budget)?,
3889                cancellation,
3890            )
3891            .await
3892            .map_err(|failure| failure.map_with_prior_work(work, Into::into))?;
3893        work = add(work, appended.work)?;
3894        match appended.value {
3895            AppendOutcome::Committed(_) | AppendOutcome::AlreadyCommitted(_) => Ok(FsReceipt {
3896                value: Volume {
3897                    fs: self.clone(),
3898                    id: volume_id,
3899                    config,
3900                },
3901                work,
3902            }),
3903            AppendOutcome::Conflict { .. }
3904            | AppendOutcome::Fenced { .. }
3905            | AppendOutcome::IdempotencyConflict { .. } => {
3906                Err(OperationFailure::new(FsError::CreationRejected, work))
3907            }
3908        }
3909    }
3910
3911    async fn read_creation(
3912        &self,
3913        volume_id: VolumeId,
3914        work: WorkCounters,
3915        budget: WorkBudget,
3916        cancellation: &CancellationToken,
3917    ) -> Result<(VolumeCreated, WorkCounters), OperationFailure<FsError>> {
3918        let records = self
3919            .inner
3920            .authority
3921            .replay(
3922                volume_authority_id(volume_id),
3923                Sequence::GENESIS,
3924                ReplayLimit {
3925                    records: 1,
3926                    payload_bytes: MAXIMUM_VOLUME_EVENT_BYTES,
3927                },
3928                remaining(work, budget)?,
3929                cancellation,
3930            )
3931            .await
3932            .map_err(|failure| failure.map_with_prior_work(work, Into::into))?;
3933        let combined = add(work, records.work)?;
3934        let first = records
3935            .value
3936            .first()
3937            .ok_or_else(|| OperationFailure::new(FsError::EmptyAuthority, combined))?;
3938        let creation = decode_volume_created(&first.payload, MAXIMUM_VOLUME_EVENT_BYTES)
3939            .map_err(|error| OperationFailure::new(error.into(), combined))?;
3940        if creation.volume_id != volume_id {
3941            return Err(OperationFailure::new(FsError::VolumeMismatch, combined));
3942        }
3943        Ok((creation, combined))
3944    }
3945
3946    async fn build_empty_generation(
3947        &self,
3948        volume_id: VolumeId,
3949        config: VolumeConfig,
3950        budget: WorkBudget,
3951        cancellation: &CancellationToken,
3952    ) -> Result<(ObjectId, WorkCounters), OperationFailure<FsError>> {
3953        let mut work = WorkCounters::default();
3954        let metadata = self
3955            .put_encoded(
3956                ObjectKind::Metadata,
3957                encode_file_metadata(empty_metadata())
3958                    .map_err(|error| OperationFailure::new(error.into(), work))?,
3959                work,
3960                budget,
3961                cancellation,
3962            )
3963            .await?;
3964        work = metadata.1;
3965        let tree = self
3966            .put_encoded(
3967                ObjectKind::TreePage,
3968                encode_tree_page(
3969                    &TreePage::Leaf(Vec::new()),
3970                    config.limits.maximum_directory_page_entries,
3971                )
3972                .map_err(|error| OperationFailure::new(error.into(), work))?,
3973                work,
3974                budget,
3975                cancellation,
3976            )
3977            .await?;
3978        work = tree.1;
3979        let (root_file_id, identity_work) = derived_file_id(volume_id);
3980        work = add(work, identity_work)?;
3981        let records = FileTablePage::Leaf(vec![FileRecord {
3982            file_id: root_file_id,
3983            kind: FileKind::Directory,
3984            link_count: 1,
3985            metadata: metadata.0,
3986            payload: FilePayload::Directory { entries: tree.0 },
3987        }]);
3988        work = add(
3989            work,
3990            WorkCounters {
3991                allocation_operations: 1,
3992                peak_allocation_bytes: u64::try_from(size_of::<FileRecord>()).unwrap_or(u64::MAX),
3993                ..WorkCounters::default()
3994            },
3995        )?;
3996        let encoded_file_table =
3997            encode_file_table_page(&records, config.limits.maximum_directory_page_entries)
3998                .map_err(|error| OperationFailure::new(error.into(), work))?;
3999        drop(records);
4000        let file_table = self
4001            .put_encoded(
4002                ObjectKind::FileTablePage,
4003                encoded_file_table,
4004                work,
4005                budget,
4006                cancellation,
4007            )
4008            .await?;
4009        work = file_table.1;
4010        let root = GenerationRoot {
4011            volume_id,
4012            root_file_id,
4013            file_table: file_table.0,
4014            parents: Vec::new(),
4015            required_features: 0,
4016        };
4017        self.put_encoded(
4018            ObjectKind::GenerationRoot,
4019            encode_generation_root(&root)
4020                .map_err(|error| OperationFailure::new(error.into(), work))?,
4021            work,
4022            budget,
4023            cancellation,
4024        )
4025        .await
4026    }
4027
4028    async fn put_encoded(
4029        &self,
4030        kind: ObjectKind,
4031        encoded: Vec<u8>,
4032        work: WorkCounters,
4033        budget: WorkBudget,
4034        cancellation: &CancellationToken,
4035    ) -> Result<(ObjectId, WorkCounters), OperationFailure<FsError>> {
4036        let length = u64::try_from(encoded.len()).unwrap_or(u64::MAX);
4037        let semantic = WorkCounters {
4038            bytes_encoded: length,
4039            bytes_hashed: length.saturating_add(OBJECT_DIGEST_ENVELOPE_BYTES),
4040            allocation_operations: 1,
4041            peak_allocation_bytes: u64::try_from(encoded.capacity()).unwrap_or(u64::MAX),
4042            ..WorkCounters::default()
4043        };
4044        let prospective = add(work, semantic)?;
4045        prospective
4046            .verify(budget)
4047            .map_err(|error| OperationFailure::new(error.into(), work))?;
4048        let object = ObjectId {
4049            kind,
4050            digest: object_digest(kind, &encoded),
4051        };
4052        let live_bytes = semantic.peak_allocation_bytes;
4053        let mut backend_budget = remaining(prospective, budget)?;
4054        // `prospective.verify` above proves the encoded buffer fits the
4055        // operation-wide peak, so this subtraction is total.
4056        backend_budget.peak_allocation_bytes -= live_bytes;
4057        let receipt = self
4058            .inner
4059            .objects
4060            .put(object, Bytes::from(encoded), backend_budget, cancellation)
4061            .await
4062            .map_err(|failure| {
4063                merge_simultaneous_failure(
4064                    prospective,
4065                    *failure.work,
4066                    live_bytes,
4067                    failure.error.into(),
4068                )
4069            })?;
4070        Ok((
4071            object,
4072            merge_simultaneous_work(prospective, receipt.work, live_bytes, budget)?,
4073        ))
4074    }
4075}
4076
4077impl<A: AsyncAuthorityStore, O: AsyncObjectStore> Volume<A, O> {
4078    /// Stable volume identity.
4079    #[must_use]
4080    pub const fn id(&self) -> VolumeId {
4081        self.id
4082    }
4083
4084    /// Immutable volume configuration reconstructed from authority.
4085    #[must_use]
4086    pub const fn config(&self) -> VolumeConfig {
4087        self.config
4088    }
4089
4090    /// Computes one bounded semantic diff without scanning equal Merkle subtrees.
4091    ///
4092    /// File-record changes are keyed by stable identity, while namespace changes
4093    /// are keyed by stable parent-directory identity and exact logical name. This
4094    /// represents hard links and renames without inventing one canonical path.
4095    /// Equal generation roots return with zero backend work.
4096    ///
4097    /// # Errors
4098    ///
4099    /// Rejects zero bounds, foreign/malformed generations, storage corruption,
4100    /// cancellation, allocation failure, or work beyond the admitted budget.
4101    pub async fn diff_generations(
4102        &self,
4103        before: GenerationId,
4104        after: GenerationId,
4105        maximum_changes: u32,
4106        budget: WorkBudget,
4107        cancellation: &CancellationToken,
4108    ) -> FsResult<GenerationDiff> {
4109        if maximum_changes == 0 {
4110            return Err(OperationFailure::before_work(FsError::InvalidDiff));
4111        }
4112        if before == after {
4113            return Ok(OperationReceipt {
4114                value: GenerationDiff {
4115                    files: Vec::new(),
4116                    bindings: Vec::new(),
4117                    truncated: false,
4118                },
4119                work: WorkCounters::default(),
4120            });
4121        }
4122        let mut work = WorkCounters::default();
4123        let before_id = ObjectId {
4124            kind: ObjectKind::GenerationRoot,
4125            digest: before.digest(),
4126        };
4127        let after_id = ObjectId {
4128            kind: ObjectKind::GenerationRoot,
4129            digest: after.digest(),
4130        };
4131        let (before_root, before_work) = read_generation_root(
4132            &self.fs.inner.objects,
4133            before_id,
4134            self.config,
4135            remaining(work, budget)?,
4136            cancellation,
4137        )
4138        .await
4139        .map_err(|failure| failure.map_with_prior_work(work, Into::into))?;
4140        work = add(work, before_work)?;
4141        let (after_root, after_work) = read_generation_root(
4142            &self.fs.inner.objects,
4143            after_id,
4144            self.config,
4145            remaining(work, budget)?,
4146            cancellation,
4147        )
4148        .await
4149        .map_err(|failure| failure.map_with_prior_work(work, Into::into))?;
4150        work = add(work, after_work)?;
4151        if before_root.volume_id != self.id || after_root.volume_id != self.id {
4152            return Err(OperationFailure::new(FsError::VolumeMismatch, work));
4153        }
4154        diff_generation_file_tables(
4155            &self.fs.inner.objects,
4156            before_root.file_table,
4157            after_root.file_table,
4158            maximum_changes,
4159            self.config,
4160            work,
4161            budget,
4162            cancellation,
4163        )
4164        .await
4165    }
4166
4167    /// Resolves the exact generation root object and, for a writable
4168    /// checkout, the authenticated authority head it must be fenced against.
4169    async fn resolve_checkout_generation(
4170        &self,
4171        selector: GenerationSelector,
4172        mode: CheckoutMode,
4173        work: WorkCounters,
4174        budget: WorkBudget,
4175        cancellation: &CancellationToken,
4176    ) -> Result<(ObjectId, Option<Head>, WorkCounters), OperationFailure<FsError>> {
4177        match selector {
4178            GenerationSelector::Exact(generation_id) => {
4179                let mut work = work;
4180                let authority_head = if mode.access == AccessMode::ReadWrite {
4181                    if mode.consistency != ConsistencyMode::TrackingSafe
4182                        || mode.mutations != MutationMode::PrivateOverlay
4183                    {
4184                        return Err(OperationFailure::before_work(
4185                            FsError::WritableCheckoutRequiresHead,
4186                        ));
4187                    }
4188                    let resolved = self
4189                        .resolve_head_generation(remaining(work, budget)?, cancellation)
4190                        .await
4191                        .map_err(|failure| {
4192                            failure.map_with_prior_work(work, std::convert::identity)
4193                        })?;
4194                    work = add(work, resolved.2)?;
4195                    Some(resolved.1)
4196                } else {
4197                    None
4198                };
4199                Ok((
4200                    ObjectId {
4201                        kind: ObjectKind::GenerationRoot,
4202                        digest: generation_id.digest(),
4203                    },
4204                    authority_head,
4205                    work,
4206                ))
4207            }
4208            GenerationSelector::Head => {
4209                let resolved = self
4210                    .resolve_head_generation(remaining(work, budget)?, cancellation)
4211                    .await
4212                    .map_err(|failure| failure.map_with_prior_work(work, std::convert::identity))?;
4213                let work = add(work, resolved.2)?;
4214                Ok((resolved.0, Some(resolved.1), work))
4215            }
4216        }
4217    }
4218
4219    /// Opens one immutable pinned checkout after authenticating its bounded root.
4220    ///
4221    /// # Errors
4222    ///
4223    /// Returns a typed measured failure for unsupported modes, missing/corrupt
4224    /// generation roots, volume mismatch, authority inconsistency,
4225    /// cancellation, or work outside the admitted budget. Descendant objects
4226    /// are authenticated lazily by the exact operation that demands them.
4227    pub async fn checkout(
4228        &self,
4229        selector: GenerationSelector,
4230        mode: CheckoutMode,
4231        budget: WorkBudget,
4232        cancellation: &CancellationToken,
4233    ) -> FsResult<Checkout<A, O>> {
4234        cancellation
4235            .check()
4236            .map_err(|error| OperationFailure::before_work(error.into()))?;
4237        validate_checkout(mode, self.config)?;
4238        let mut work = WorkCounters::default();
4239        let (generation_root, mut authority_head, next_work) = self
4240            .resolve_checkout_generation(selector, mode, work, budget, cancellation)
4241            .await?;
4242        work = next_work;
4243        let (root, root_work) = read_generation_root(
4244            &self.fs.inner.objects,
4245            generation_root,
4246            self.config,
4247            remaining(work, budget)?,
4248            cancellation,
4249        )
4250        .await
4251        .map_err(|failure| failure.map_with_prior_work(work, Into::into))?;
4252        work = add(work, root_work)?;
4253        if root.volume_id != self.id {
4254            return Err(OperationFailure::new(FsError::VolumeMismatch, work));
4255        }
4256        if mode.access == AccessMode::ReadWrite
4257            && self.config.concurrency == ConcurrencyMode::ExclusiveWriter
4258        {
4259            let expected = authority_head.ok_or_else(|| {
4260                OperationFailure::new(FsError::WritableCheckoutRequiresHead, work)
4261            })?;
4262            let fenced = self
4263                .fs
4264                .inner
4265                .authority
4266                .fence(
4267                    volume_authority_id(self.id),
4268                    expected,
4269                    remaining(work, budget)?,
4270                    cancellation,
4271                )
4272                .await
4273                .map_err(|failure| failure.map_with_prior_work(work, Into::into))?;
4274            work = add(work, fenced.work)?;
4275            authority_head = Some(match fenced.value {
4276                FenceOutcome::Advanced(head) => head,
4277                FenceOutcome::Conflict { actual } => {
4278                    return Err(OperationFailure::new(
4279                        FsError::ExclusiveWriterConflict { actual },
4280                        work,
4281                    ));
4282                }
4283            });
4284        }
4285        let dependencies = CheckoutDependencies::new(
4286            std::iter::empty(),
4287            std::iter::empty(),
4288            self.config.limits.maximum_checkout_dependencies,
4289        )
4290        .map_err(|error| OperationFailure::new(error.into(), work))?;
4291        Ok(FsReceipt {
4292            value: Checkout {
4293                volume: self.clone(),
4294                base_generation_root: generation_root,
4295                generation_root,
4296                base_file_table: root.file_table,
4297                base_root: root.clone(),
4298                root,
4299                authority_head,
4300                authored_operation_id: None,
4301                live_operation_id: None,
4302                last_commit: None,
4303                prepared_merge_parent: None,
4304                dependencies,
4305                mode,
4306            },
4307            work,
4308        })
4309    }
4310
4311    async fn resolve_head_generation(
4312        &self,
4313        budget: WorkBudget,
4314        cancellation: &CancellationToken,
4315    ) -> Result<(ObjectId, Head, WorkCounters), OperationFailure<FsError>> {
4316        let authority_id = volume_authority_id(self.id);
4317        let head = self
4318            .fs
4319            .inner
4320            .authority
4321            .head(authority_id, budget, cancellation)
4322            .await
4323            .map_err(|failure| OperationFailure::new(failure.error.into(), *failure.work))?;
4324        let mut work = head.work;
4325        if head.value.sequence == Sequence::GENESIS {
4326            return Err(OperationFailure::new(FsError::EmptyAuthority, work));
4327        }
4328        let after = Sequence::new(head.value.sequence.get().saturating_sub(1));
4329        let records = self
4330            .fs
4331            .inner
4332            .authority
4333            .replay(
4334                authority_id,
4335                after,
4336                ReplayLimit {
4337                    records: 1,
4338                    payload_bytes: MAXIMUM_VOLUME_EVENT_BYTES,
4339                },
4340                remaining(work, budget)?,
4341                cancellation,
4342            )
4343            .await
4344            .map_err(|failure| failure.map_with_prior_work(work, Into::into))?;
4345        work = add(work, records.work)?;
4346        let latest = records
4347            .value
4348            .first()
4349            .ok_or_else(|| OperationFailure::new(FsError::InvalidAuthorityHistory, work))?;
4350        if let Ok(deleted) = decode_workspace_deleted(&latest.payload, MAXIMUM_VOLUME_EVENT_BYTES) {
4351            if deleted != self.id {
4352                return Err(OperationFailure::new(FsError::VolumeMismatch, work));
4353            }
4354            return Err(OperationFailure::new(FsError::WorkspaceDeleted, work));
4355        }
4356        let generation_root = generation_from_record(latest, self.id, work)?;
4357        Ok((generation_root, head.value, work))
4358    }
4359}
4360
4361impl<A, O> Checkout<A, O> {
4362    /// Owning volume identity.
4363    #[must_use]
4364    pub const fn volume_id(&self) -> VolumeId {
4365        self.volume.id
4366    }
4367
4368    /// Immutable owning-volume semantics and operation bounds.
4369    #[must_use]
4370    pub const fn volume_config(&self) -> VolumeConfig {
4371        self.volume.config
4372    }
4373
4374    /// Exact immutable generation identity.
4375    #[must_use]
4376    pub const fn generation_id(&self) -> GenerationId {
4377        GenerationId::new(self.generation_root.digest)
4378    }
4379
4380    /// Authenticated decoded generation root.
4381    #[must_use]
4382    pub const fn root(&self) -> &GenerationRoot {
4383        &self.root
4384    }
4385
4386    /// Admitted checkout behavior.
4387    #[must_use]
4388    pub const fn mode(&self) -> CheckoutMode {
4389        self.mode
4390    }
4391
4392    /// Whether the private candidate differs from its immutable base.
4393    #[must_use]
4394    pub fn has_pending_mutations(&self) -> bool {
4395        self.root.file_table != self.base_file_table || self.prepared_merge_parent.is_some()
4396    }
4397
4398    /// Creates an immutable reader for this checkout's current private candidate.
4399    ///
4400    /// The reader snapshots the exact authenticated root already held by the
4401    /// checkout, including pending private overlay state, without authority or
4402    /// storage work. Later checkout mutations do not change the snapshot.
4403    #[must_use]
4404    pub fn snapshot_reader(&self) -> PinnedReader<A, O> {
4405        PinnedReader {
4406            volume: self.volume.clone(),
4407            generation_root: self.generation_root,
4408            root: self.root.clone(),
4409        }
4410    }
4411
4412    /// Creates an immutable reader for a pinned checkout.
4413    ///
4414    /// # Errors
4415    ///
4416    /// Rejects non-pinned checkouts before any storage work.
4417    pub fn pinned_reader(&self) -> Result<PinnedReader<A, O>, FsError> {
4418        if self.mode.consistency != ConsistencyMode::Pinned {
4419            return Err(FsError::MutationNotAllowed);
4420        }
4421        Ok(self.snapshot_reader())
4422    }
4423
4424    /// Creates an immutable content writer that can stage independent sources
4425    /// concurrently before this checkout admits their ordered mutations.
4426    #[must_use]
4427    pub fn content_stager(&self) -> ContentStager<A, O> {
4428        ContentStager {
4429            volume: self.volume.clone(),
4430        }
4431    }
4432}
4433
4434impl<A: AsyncAuthorityStore, O: AsyncObjectStore> ContentStager<A, O> {
4435    /// Streams one bounded source into immutable authenticated chunks.
4436    pub async fn stage<R: AsyncBlobSource>(
4437        &self,
4438        source: &mut R,
4439        maximum_source_bytes: u64,
4440        budget: WorkBudget,
4441        cancellation: &CancellationToken,
4442    ) -> FsResult<StagedContent> {
4443        stage_content_for_volume(
4444            &self.volume,
4445            source,
4446            maximum_source_bytes,
4447            budget,
4448            cancellation,
4449        )
4450        .await
4451    }
4452}
4453
4454impl<A: AsyncAuthorityStore, O: AsyncObjectStore> PinnedReader<A, O> {
4455    async fn resolve_file_records(
4456        &self,
4457        paths: &[NamespacePath],
4458        budget: WorkBudget,
4459        cancellation: &CancellationToken,
4460    ) -> FsResult<Vec<Option<FileRecord>>> {
4461        let lookup = crate::kernel::lookup_paths_async(
4462            &self.volume.fs.inner.objects,
4463            &self.root,
4464            paths,
4465            self.volume.config,
4466            budget,
4467            cancellation,
4468        )
4469        .await
4470        .map_err(|failure| failure.map_with_prior_work(WorkCounters::default(), FsError::Path))?;
4471        let mut records = Vec::new();
4472        records
4473            .try_reserve_exact(lookup.entries.len())
4474            .map_err(|_| {
4475                OperationFailure::new(FsError::PendingMutationAllocationFailed, lookup.work)
4476            })?;
4477        for entry in lookup.entries {
4478            records.push(entry.record);
4479        }
4480        Ok(FsReceipt {
4481            value: records,
4482            work: lookup.work,
4483        })
4484    }
4485
4486    async fn list_directory_page(
4487        &self,
4488        request: DirectoryPageRequest,
4489        budget: WorkBudget,
4490        cancellation: &CancellationToken,
4491    ) -> FsResult<DirectoryPage> {
4492        list_directory_page_pinned(
4493            &self.volume,
4494            &self.root,
4495            &request.path,
4496            request.after.as_ref(),
4497            request.maximum_entries,
4498            budget,
4499            cancellation,
4500        )
4501        .await
4502    }
4503
4504    /// Lists ordered directory binding pages with at most `concurrency`
4505    /// requests in flight while preserving input order. Finite budgets are
4506    /// consumed sequentially so concurrent requests cannot each admit the
4507    /// complete group budget.
4508    pub async fn list_directory_pages(
4509        &self,
4510        requests: &[DirectoryPageRequest],
4511        concurrency: usize,
4512        budget: WorkBudget,
4513        cancellation: &CancellationToken,
4514    ) -> FsResult<Vec<DirectoryPage>> {
4515        if concurrency == 0
4516            || requests.len()
4517                > usize::try_from(self.volume.config.limits.maximum_paths_per_batch)
4518                    .unwrap_or(usize::MAX)
4519        {
4520            return Err(OperationFailure::before_work(FsError::Path(
4521                PathLookupError::TooManyPaths,
4522            )));
4523        }
4524        if budget != WorkBudget::UNBOUNDED {
4525            let mut work = WorkCounters::default();
4526            let mut ordered = Vec::new();
4527            ordered
4528                .try_reserve_exact(requests.len())
4529                .map_err(|_| OperationFailure::before_work(FsError::Work(WorkError::Overflow)))?;
4530            for request in requests.iter().cloned() {
4531                let receipt = self
4532                    .list_directory_page(request, remaining(work, budget)?, cancellation)
4533                    .await
4534                    .map_err(|failure| failure.map_with_prior_work(work, std::convert::identity))?;
4535                work = add(work, receipt.work)?;
4536                ordered.push(receipt.value);
4537            }
4538            return Ok(FsReceipt {
4539                value: ordered,
4540                work,
4541            });
4542        }
4543        let results = stream::iter(
4544            requests
4545                .iter()
4546                .cloned()
4547                .enumerate()
4548                .map(|(index, request)| {
4549                    let reader = self.clone();
4550                    async move {
4551                        (
4552                            index,
4553                            reader
4554                                .list_directory_page(request, budget, cancellation)
4555                                .await,
4556                        )
4557                    }
4558                }),
4559        )
4560        .buffer_unordered(concurrency.min(requests.len().max(1)))
4561        .collect::<Vec<_>>()
4562        .await;
4563        order_batch_results(results, budget)
4564    }
4565
4566    /// Resolves one bounded directory page into generation-scoped child
4567    /// handles in the same authenticated traversal. Child metadata is fetched
4568    /// as one object batch; no child path is looked up again.
4569    pub async fn resolve_directory_page(
4570        &self,
4571        path: &NamespacePath,
4572        after: Option<&LogicalName>,
4573        maximum_entries: u32,
4574        budget: WorkBudget,
4575        cancellation: &CancellationToken,
4576    ) -> FsResult<ResolvedDirectoryPage<A, O>> {
4577        let listing = list_directory_page_pinned(
4578            &self.volume,
4579            &self.root,
4580            path,
4581            after,
4582            maximum_entries,
4583            budget,
4584            cancellation,
4585        )
4586        .await?;
4587        let mut work = listing.work;
4588        let page = listing.value;
4589        let mut file_ids = Vec::new();
4590        file_ids
4591            .try_reserve_exact(page.entries.len())
4592            .map_err(|_| OperationFailure::new(FsError::Work(WorkError::Overflow), work))?;
4593        file_ids.extend(page.entries.iter().map(|entry| entry.file_id));
4594        let records = if file_ids.is_empty() {
4595            Vec::new()
4596        } else {
4597            let receipt = lookup_file_records_async(
4598                &self.volume.fs.inner.objects,
4599                self.root.file_table,
4600                &file_ids,
4601                maximum_entries,
4602                decode_limits(self.volume.config),
4603                remaining(work, budget)?,
4604                cancellation,
4605            )
4606            .await
4607            .map_err(|failure| failure.map_with_prior_work(work, FsError::FileRecord))?;
4608            work = add(work, receipt.work)?;
4609            receipt.records
4610        };
4611        let mut metadata_requests = Vec::new();
4612        metadata_requests
4613            .try_reserve_exact(records.len())
4614            .map_err(|_| OperationFailure::new(FsError::Work(WorkError::Overflow), work))?;
4615        for record in &records {
4616            let record = record
4617                .as_ref()
4618                .ok_or_else(|| OperationFailure::new(FsError::InvalidDirectoryRecord, work))?;
4619            metadata_requests.push(ObjectReadRequest {
4620                object_id: record.metadata,
4621                maximum_bytes: self.volume.config.limits.maximum_object_bytes,
4622            });
4623        }
4624        let metadata = if metadata_requests.is_empty() {
4625            Vec::new()
4626        } else {
4627            let receipt = self
4628                .volume
4629                .fs
4630                .inner
4631                .objects
4632                .read_many(&metadata_requests, remaining(work, budget)?, cancellation)
4633                .await
4634                .map_err(|failure| failure.map_with_prior_work(work, FsError::Object))?;
4635            work = add(work, receipt.work)?;
4636            receipt.value
4637        };
4638        let mut entries = Vec::new();
4639        entries
4640            .try_reserve_exact(page.entries.len())
4641            .map_err(|_| OperationFailure::new(FsError::Work(WorkError::Overflow), work))?;
4642        for ((binding, record), metadata) in page.entries.into_iter().zip(records).zip(metadata) {
4643            let record = record
4644                .ok_or_else(|| OperationFailure::new(FsError::InvalidDirectoryRecord, work))?;
4645            if record.file_id != binding.file_id || record.kind != binding.kind {
4646                return Err(OperationFailure::new(FsError::InvalidDirectoryRecord, work));
4647            }
4648            let metadata = decode_file_metadata(&metadata, decode_limits(self.volume.config))
4649                .map_err(|error| OperationFailure::new(error.into(), work))?;
4650            let logical_bytes = record_logical_bytes(record);
4651            entries.push(ResolvedDirectoryEntry {
4652                name: binding.name,
4653                file: ResolvedFile {
4654                    reader: self.clone(),
4655                    record,
4656                    description: FileDescription {
4657                        kind: record.kind,
4658                        logical_bytes,
4659                        metadata,
4660                    },
4661                },
4662            });
4663        }
4664        Ok(FsReceipt {
4665            value: ResolvedDirectoryPage {
4666                entries,
4667                has_more: page.has_more,
4668            },
4669            work,
4670        })
4671    }
4672
4673    async fn read_symbolic_link_record(
4674        &self,
4675        record: FileRecord,
4676        budget: WorkBudget,
4677        cancellation: &CancellationToken,
4678    ) -> FsResult<Bytes> {
4679        read_symbolic_link_record(&self.volume, record, budget, cancellation).await
4680    }
4681
4682    async fn read_record_range(
4683        &self,
4684        record: FileRecord,
4685        range: ByteRange,
4686        budget: WorkBudget,
4687        cancellation: &CancellationToken,
4688    ) -> FsResult<FileRangeRead> {
4689        if range.length > self.volume.config.limits.maximum_read_bytes {
4690            return Err(OperationFailure::before_work(FsError::FileRead(
4691                FileRangeReadError::InvalidRange,
4692            )));
4693        }
4694        let read = read_file_range_async(
4695            &self.volume.fs.inner.objects,
4696            FileRangeRequest {
4697                record,
4698                range,
4699                maximum_spans: self.volume.config.limits.maximum_directory_page_entries,
4700                limits: decode_limits(self.volume.config),
4701                budget,
4702            },
4703            cancellation,
4704        )
4705        .await
4706        .map_err(|failure| {
4707            failure.map_with_prior_work(WorkCounters::default(), FsError::FileRead)
4708        })?;
4709        Ok(FsReceipt {
4710            work: read.work,
4711            value: read,
4712        })
4713    }
4714
4715    async fn read_record_metadata(
4716        &self,
4717        record: FileRecord,
4718        budget: WorkBudget,
4719        cancellation: &CancellationToken,
4720    ) -> FsResult<FileMetadata> {
4721        let metadata = self
4722            .volume
4723            .fs
4724            .inner
4725            .objects
4726            .read(
4727                record.metadata,
4728                self.volume.config.limits.maximum_object_bytes,
4729                budget,
4730                cancellation,
4731            )
4732            .await
4733            .map_err(|failure| {
4734                failure.map_with_prior_work(WorkCounters::default(), FsError::Object)
4735            })?;
4736        let value = decode_file_metadata(&metadata.value, decode_limits(self.volume.config))
4737            .map_err(|error| OperationFailure::new(error.into(), metadata.work))?;
4738        Ok(FsReceipt {
4739            value,
4740            work: metadata.work,
4741        })
4742    }
4743
4744    async fn resolve_file(
4745        &self,
4746        path: &NamespacePath,
4747        budget: WorkBudget,
4748        cancellation: &CancellationToken,
4749    ) -> FsResult<FileRecord> {
4750        let lookup = crate::kernel::observe_path_edges_async(
4751            &self.volume.fs.inner.objects,
4752            &self.root,
4753            path,
4754            self.volume.config,
4755            budget,
4756            cancellation,
4757        )
4758        .await
4759        .map_err(|failure| failure.map_with_prior_work(WorkCounters::default(), FsError::Path))?;
4760        let work = lookup.lookup.work;
4761        let record = lookup
4762            .lookup
4763            .record
4764            .ok_or_else(|| OperationFailure::new(FsError::NotFound, work))?;
4765        Ok(FsReceipt {
4766            value: record,
4767            work,
4768        })
4769    }
4770
4771    /// Plans sparse physical spans without reading content bodies.
4772    pub async fn plan_file_extents(
4773        &self,
4774        path: &NamespacePath,
4775        range: ByteRange,
4776        maximum_spans: u32,
4777        budget: WorkBudget,
4778        cancellation: &CancellationToken,
4779    ) -> FsResult<Option<ExtentPlan>> {
4780        let resolved = self.resolve_file(path, budget, cancellation).await?;
4781        let mut work = resolved.work;
4782        let plan = self
4783            .plan_record_extents(
4784                resolved.value,
4785                range,
4786                maximum_spans,
4787                remaining(work, budget)?,
4788                cancellation,
4789            )
4790            .await
4791            .map_err(|failure| failure.map_with_prior_work(work, std::convert::identity))?;
4792        work = add(work, plan.work)?;
4793        Ok(FsReceipt {
4794            value: plan.value,
4795            work,
4796        })
4797    }
4798
4799    async fn plan_record_extents(
4800        &self,
4801        record: FileRecord,
4802        range: ByteRange,
4803        maximum_spans: u32,
4804        budget: WorkBudget,
4805        cancellation: &CancellationToken,
4806    ) -> FsResult<Option<ExtentPlan>> {
4807        if maximum_spans == 0 || range.length > self.volume.config.limits.maximum_generation_bytes {
4808            return Err(OperationFailure::before_work(FsError::FileRead(
4809                FileRangeReadError::InvalidRange,
4810            )));
4811        }
4812        let mut work = WorkCounters::default();
4813        let (logical_bytes, extents) = match record.payload {
4814            FilePayload::InlineRegular(data) => {
4815                validate_planned_file_range(
4816                    u64::try_from(data.as_bytes().len()).unwrap_or(u64::MAX),
4817                    range,
4818                    work,
4819                )?;
4820                return Ok(FsReceipt { value: None, work });
4821            }
4822            FilePayload::Regular {
4823                logical_bytes,
4824                extents,
4825            } => (logical_bytes, extents),
4826            _ => {
4827                return Err(OperationFailure::new(
4828                    FsError::FileRead(FileRangeReadError::NotRegular),
4829                    work,
4830                ));
4831            }
4832        };
4833        let mut plan = plan_extent_range_async(
4834            &self.volume.fs.inner.objects,
4835            ExtentRangeRequest {
4836                root: extents,
4837                file_size: logical_bytes,
4838                range,
4839                maximum_spans,
4840                limits: decode_limits(self.volume.config),
4841                budget: remaining(work, budget)?,
4842            },
4843            cancellation,
4844        )
4845        .await
4846        .map_err(|failure| {
4847            failure.map_with_prior_work(work, |error| {
4848                FsError::FileRead(FileRangeReadError::Extent(error))
4849            })
4850        })?;
4851        work = add(work, plan.work)?;
4852        plan.work = work;
4853        Ok(FsReceipt {
4854            value: Some(plan),
4855            work,
4856        })
4857    }
4858
4859    /// Reads one exact logical range without mutating checkout state.
4860    pub async fn read_file_range(
4861        &self,
4862        path: &NamespacePath,
4863        range: ByteRange,
4864        budget: WorkBudget,
4865        cancellation: &CancellationToken,
4866    ) -> FsResult<FileRangeRead> {
4867        if range.length > self.volume.config.limits.maximum_read_bytes {
4868            return Err(OperationFailure::before_work(FsError::FileRead(
4869                FileRangeReadError::InvalidRange,
4870            )));
4871        }
4872        let resolved = self.resolve_file(path, budget, cancellation).await?;
4873        let mut work = resolved.work;
4874        let record = resolved.value;
4875        let read = read_file_range_async(
4876            &self.volume.fs.inner.objects,
4877            FileRangeRequest {
4878                record,
4879                range,
4880                maximum_spans: self.volume.config.limits.maximum_directory_page_entries,
4881                limits: decode_limits(self.volume.config),
4882                budget: remaining(work, budget)?,
4883            },
4884            cancellation,
4885        )
4886        .await
4887        .map_err(|failure| failure.map_with_prior_work(work, FsError::FileRead))?;
4888        work = add(work, read.work)?;
4889        Ok(FsReceipt { value: read, work })
4890    }
4891
4892    /// Reads exact opaque symbolic-link target bytes without following it.
4893    pub async fn read_symbolic_link(
4894        &self,
4895        path: &NamespacePath,
4896        budget: WorkBudget,
4897        cancellation: &CancellationToken,
4898    ) -> FsResult<Bytes> {
4899        let resolved = self.resolve_file(path, budget, cancellation).await?;
4900        let mut work = resolved.work;
4901        let read = self
4902            .read_symbolic_link_record(resolved.value, remaining(work, budget)?, cancellation)
4903            .await
4904            .map_err(|failure| failure.map_with_prior_work(work, std::convert::identity))?;
4905        work = add(work, read.work)?;
4906        Ok(FsReceipt {
4907            value: read.value,
4908            work,
4909        })
4910    }
4911
4912    /// Reads complete canonical metadata for one exact path.
4913    pub async fn read_metadata(
4914        &self,
4915        path: &NamespacePath,
4916        budget: WorkBudget,
4917        cancellation: &CancellationToken,
4918    ) -> FsResult<FileMetadata> {
4919        let resolved = self.resolve_file(path, budget, cancellation).await?;
4920        let mut work = resolved.work;
4921        let metadata = self
4922            .read_record_metadata(resolved.value, remaining(work, budget)?, cancellation)
4923            .await
4924            .map_err(|failure| failure.map_with_prior_work(work, std::convert::identity))?;
4925        work = add(work, metadata.work)?;
4926        Ok(FsReceipt {
4927            value: metadata.value,
4928            work,
4929        })
4930    }
4931
4932    /// Describes an ordered path batch while sharing namespace and object traversal.
4933    pub async fn describe_files(
4934        &self,
4935        paths: &[NamespacePath],
4936        budget: WorkBudget,
4937        cancellation: &CancellationToken,
4938    ) -> FsResult<Vec<Option<FileDescription>>> {
4939        let resolved = self.resolve_files(paths, budget, cancellation).await?;
4940        let descriptions = resolved
4941            .value
4942            .into_iter()
4943            .map(|file| file.map(|file| file.description))
4944            .collect();
4945        Ok(FsReceipt {
4946            value: descriptions,
4947            work: resolved.work,
4948        })
4949    }
4950
4951    /// Resolves and describes paths once, returning reader-scoped handles for
4952    /// later body reads without another namespace traversal.
4953    pub async fn resolve_files(
4954        &self,
4955        paths: &[NamespacePath],
4956        budget: WorkBudget,
4957        cancellation: &CancellationToken,
4958    ) -> FsResult<Vec<Option<ResolvedFile<A, O>>>> {
4959        let resolved = self
4960            .resolve_file_records(paths, budget, cancellation)
4961            .await?;
4962        let mut work = resolved.work;
4963        let mut requests = Vec::new();
4964        requests
4965            .try_reserve_exact(resolved.value.len())
4966            .map_err(|_| OperationFailure::new(FsError::Work(WorkError::Overflow), work))?;
4967        requests.extend(
4968            resolved
4969                .value
4970                .iter()
4971                .flatten()
4972                .map(|record| ObjectReadRequest {
4973                    object_id: record.metadata,
4974                    maximum_bytes: self.volume.config.limits.maximum_object_bytes,
4975                }),
4976        );
4977        let reads = if requests.is_empty() {
4978            Vec::new()
4979        } else {
4980            let receipt = self
4981                .volume
4982                .fs
4983                .inner
4984                .objects
4985                .read_many(&requests, remaining(work, budget)?, cancellation)
4986                .await
4987                .map_err(|failure| failure.map_with_prior_work(work, FsError::Object))?;
4988            work = add(work, receipt.work)?;
4989            receipt.value
4990        };
4991        let mut reads = reads.into_iter();
4992        let mut handles = Vec::new();
4993        handles
4994            .try_reserve_exact(resolved.value.len())
4995            .map_err(|_| OperationFailure::new(FsError::Work(WorkError::Overflow), work))?;
4996        for record in resolved.value {
4997            let Some(record) = record else {
4998                handles.push(None);
4999                continue;
5000            };
5001            let bytes = reads
5002                .next()
5003                .ok_or_else(|| OperationFailure::new(FsError::Work(WorkError::Overflow), work))?;
5004            let metadata = decode_file_metadata(&bytes, decode_limits(self.volume.config))
5005                .map_err(|error| OperationFailure::new(error.into(), work))?;
5006            let logical_bytes = record_logical_bytes(record);
5007            handles.push(Some(ResolvedFile {
5008                reader: self.clone(),
5009                record,
5010                description: FileDescription {
5011                    kind: record.kind,
5012                    logical_bytes,
5013                    metadata,
5014                },
5015            }));
5016        }
5017        Ok(FsReceipt {
5018            value: handles,
5019            work,
5020        })
5021    }
5022
5023    /// Reads ranges from already resolved files with bounded concurrency and
5024    /// no additional namespace traversal.
5025    pub async fn read_resolved_ranges(
5026        &self,
5027        requests: &[ResolvedFileRangeReadRequest<'_, A, O>],
5028        concurrency: usize,
5029        budget: WorkBudget,
5030        cancellation: &CancellationToken,
5031    ) -> FsResult<Vec<FileRangeRead>> {
5032        if concurrency == 0
5033            || requests.len()
5034                > usize::try_from(self.volume.config.limits.maximum_paths_per_batch)
5035                    .unwrap_or(usize::MAX)
5036        {
5037            return Err(OperationFailure::before_work(FsError::FileRead(
5038                FileRangeReadError::InvalidRange,
5039            )));
5040        }
5041        if budget != WorkBudget::UNBOUNDED {
5042            let mut work = WorkCounters::default();
5043            let mut reads = Vec::new();
5044            reads
5045                .try_reserve_exact(requests.len())
5046                .map_err(|_| OperationFailure::before_work(FsError::Work(WorkError::Overflow)))?;
5047            for request in requests {
5048                let read = request
5049                    .file
5050                    .read_range(request.range, remaining(work, budget)?, cancellation)
5051                    .await
5052                    .map_err(|failure| failure.map_with_prior_work(work, std::convert::identity))?;
5053                work = add(work, read.work)?;
5054                reads.push(read.value);
5055            }
5056            return Ok(FsReceipt { value: reads, work });
5057        }
5058        let cancellation = cancellation.clone();
5059        let pending = requests
5060            .iter()
5061            .enumerate()
5062            .map(|(index, request)| {
5063                (
5064                    index,
5065                    request.file.reader.clone(),
5066                    request.file.record,
5067                    request.range,
5068                )
5069            })
5070            .collect::<Vec<_>>();
5071        let results = stream::iter(pending.into_iter().map(|(index, reader, record, range)| {
5072            let cancellation = cancellation.clone();
5073            async move {
5074                (
5075                    index,
5076                    reader
5077                        .read_record_range(record, range, budget, &cancellation)
5078                        .await,
5079                )
5080            }
5081        }))
5082        .buffer_unordered(concurrency.min(requests.len().max(1)))
5083        .collect::<Vec<_>>()
5084        .await;
5085        order_batch_results(results, budget)
5086    }
5087}
5088
5089fn record_logical_bytes(record: FileRecord) -> u64 {
5090    match record.payload {
5091        FilePayload::InlineRegular(bytes) => {
5092            u64::try_from(bytes.as_bytes().len()).unwrap_or(u64::MAX)
5093        }
5094        FilePayload::Regular { logical_bytes, .. } => logical_bytes,
5095        FilePayload::SymbolicLink { target_bytes, .. } => target_bytes,
5096        _ => 0,
5097    }
5098}
5099
5100async fn list_directory_page_pinned<A: AsyncAuthorityStore, O: AsyncObjectStore>(
5101    volume: &Volume<A, O>,
5102    root: &GenerationRoot,
5103    path: &NamespacePath,
5104    after: Option<&LogicalName>,
5105    maximum_entries: u32,
5106    budget: WorkBudget,
5107    cancellation: &CancellationToken,
5108) -> FsResult<DirectoryPage> {
5109    let lookup = crate::kernel::observe_path_edges_async(
5110        &volume.fs.inner.objects,
5111        root,
5112        path,
5113        volume.config,
5114        budget,
5115        cancellation,
5116    )
5117    .await
5118    .map_err(|failure| failure.map_with_prior_work(WorkCounters::default(), FsError::Path))?;
5119    let mut work = lookup.lookup.work;
5120    let record = lookup
5121        .lookup
5122        .record
5123        .ok_or_else(|| OperationFailure::new(FsError::NotFound, work))?;
5124    let FilePayload::Directory { entries } = record.payload else {
5125        return Err(OperationFailure::new(FsError::NotDirectory, work));
5126    };
5127    if record.kind != FileKind::Directory {
5128        return Err(OperationFailure::new(FsError::NotDirectory, work));
5129    }
5130    let mut page = list_tree_entries_async(
5131        &volume.fs.inner.objects,
5132        entries,
5133        after,
5134        maximum_entries,
5135        decode_limits(volume.config),
5136        remaining(work, budget)?,
5137        cancellation,
5138    )
5139    .await
5140    .map_err(|failure| failure.map_with_prior_work(work, FsError::Directory))?;
5141    work = add(work, page.work)?;
5142    page.work = work;
5143    Ok(FsReceipt { value: page, work })
5144}
5145
5146async fn read_symbolic_link_record<A: AsyncAuthorityStore, O: AsyncObjectStore>(
5147    volume: &Volume<A, O>,
5148    record: FileRecord,
5149    budget: WorkBudget,
5150    cancellation: &CancellationToken,
5151) -> FsResult<Bytes> {
5152    let FilePayload::SymbolicLink {
5153        target_bytes,
5154        target,
5155    } = record.payload
5156    else {
5157        return Err(OperationFailure::before_work(FsError::NotSymbolicLink));
5158    };
5159    if target_bytes > volume.config.limits.maximum_read_bytes {
5160        return Err(OperationFailure::before_work(FsError::FileRead(
5161            FileRangeReadError::InvalidRange,
5162        )));
5163    }
5164    if target_bytes == 0 {
5165        return Ok(FsReceipt {
5166            value: Bytes::new(),
5167            work: WorkCounters::default(),
5168        });
5169    }
5170    let read = read_blob_range_async(
5171        &volume.fs.inner.objects,
5172        target,
5173        ByteRange {
5174            offset: 0,
5175            length: target_bytes,
5176        },
5177        decode_limits(volume.config),
5178        budget,
5179        cancellation,
5180    )
5181    .await
5182    .map_err(|failure| failure.map_with_prior_work(WorkCounters::default(), FsError::BlobRead))?;
5183    Ok(FsReceipt {
5184        value: read.bytes,
5185        work: read.work,
5186    })
5187}
5188
5189fn order_batch_results<T>(
5190    mut results: Vec<(usize, FsResult<T>)>,
5191    budget: WorkBudget,
5192) -> FsResult<Vec<T>> {
5193    results.sort_unstable_by_key(|(index, _)| *index);
5194    let mut work = WorkCounters::default();
5195    let mut completed = Vec::new();
5196    completed
5197        .try_reserve_exact(results.len())
5198        .map_err(|_| OperationFailure::before_work(FsError::Work(WorkError::Overflow)))?;
5199    let mut first_failure = None;
5200    for (index, result) in results {
5201        match result {
5202            Ok(receipt) => {
5203                work = add(work, receipt.work)?;
5204                completed.push((index, receipt.value));
5205            }
5206            Err(failure) => {
5207                work = add(work, *failure.work)?;
5208                if first_failure.is_none() {
5209                    first_failure = Some(failure.error);
5210                }
5211            }
5212        }
5213    }
5214    work.verify(budget)
5215        .map_err(|error| OperationFailure::new(error.into(), work))?;
5216    if let Some(error) = first_failure {
5217        return Err(OperationFailure::new(error, work));
5218    }
5219    Ok(FsReceipt {
5220        value: completed.into_iter().map(|(_, value)| value).collect(),
5221        work,
5222    })
5223}
5224
5225impl<A: AsyncAuthorityStore, O: AsyncObjectStore> Checkout<A, O> {
5226    /// Prepares one bounded two-parent merge against this checkout's immutable base.
5227    ///
5228    /// Independent file identities merge without scanning equal Merkle subtrees.
5229    /// A conflict leaves the checkout unchanged; a successful result is an
5230    /// authenticated unpublished generation that must be committed or discarded.
5231    ///
5232    /// # Errors
5233    ///
5234    /// Rejects non-writable/prepared checkouts, foreign generations, truncated
5235    /// change frontiers, malformed storage, cancellation, or bounded-work failure.
5236    pub async fn prepare_merge(
5237        &mut self,
5238        theirs: GenerationId,
5239        maximum_changes: u32,
5240        maximum_conflicts: u32,
5241        budget: WorkBudget,
5242        cancellation: &CancellationToken,
5243    ) -> FsResult<MergePreparation> {
5244        if self.mode.access != AccessMode::ReadWrite
5245            || self.mode.mutations != MutationMode::PrivateOverlay
5246        {
5247            return Err(OperationFailure::before_work(FsError::MutationNotAllowed));
5248        }
5249        if self.prepared_merge_parent.is_some() {
5250            return Err(OperationFailure::before_work(FsError::PreparedMergePending));
5251        }
5252        if maximum_changes == 0 || maximum_conflicts == 0 {
5253            return Err(OperationFailure::before_work(FsError::MergeChangeLimit));
5254        }
5255        let theirs_id = ObjectId {
5256            kind: ObjectKind::GenerationRoot,
5257            digest: theirs.digest(),
5258        };
5259        let (current_head_root, current_head, mut work) = self
5260            .volume
5261            .resolve_head_generation(budget, cancellation)
5262            .await?;
5263        if current_head_root != theirs_id {
5264            return Err(OperationFailure::new(FsError::MergeParentNotHead, work));
5265        }
5266        let (theirs_root, theirs_work) = read_generation_root(
5267            &self.volume.fs.inner.objects,
5268            theirs_id,
5269            self.volume.config,
5270            remaining(work, budget)?,
5271            cancellation,
5272        )
5273        .await
5274        .map_err(|failure| failure.map_with_prior_work(work, std::convert::identity))?;
5275        work = add(work, theirs_work)?;
5276        let merged = merge_generation_async(
5277            &self.volume.fs.inner.objects,
5278            MergeGenerationRequest {
5279                base_generation: self.base_generation_root,
5280                base: self.base_root.clone(),
5281                ours_generation: (self.root.file_table == self.base_file_table)
5282                    .then_some(self.generation_root),
5283                ours: self.root.clone(),
5284                theirs_generation: theirs_id,
5285                theirs: theirs_root,
5286                retain_theirs_parent: true,
5287                maximum_changes,
5288                maximum_conflicts,
5289                resolutions: BTreeMap::new(),
5290            },
5291            decode_limits(self.volume.config),
5292            remaining(work, budget)?,
5293            cancellation,
5294        )
5295        .await
5296        .map_err(|failure| map_merge_failure(failure, work))?;
5297        work = add(work, merged.work)?;
5298        match merged.value {
5299            MergeGenerationOutcome::Prepared {
5300                generation_root,
5301                root,
5302                generation_id,
5303            } => {
5304                self.root = root;
5305                self.generation_root = generation_root;
5306                self.prepared_merge_parent = Some(theirs_id);
5307                self.authority_head = Some(current_head);
5308                Ok(OperationReceipt {
5309                    value: MergePreparation::Prepared { generation_id },
5310                    work,
5311                })
5312            }
5313            MergeGenerationOutcome::Conflicted {
5314                conflicts,
5315                truncated,
5316            } => Ok(OperationReceipt {
5317                value: MergePreparation::Conflicted {
5318                    conflicts,
5319                    truncated,
5320                },
5321                work,
5322            }),
5323        }
5324    }
5325
5326    /// Builds a deterministic complete manifest for the current immutable or
5327    /// sparse candidate generation without publishing authority.
5328    ///
5329    /// A dirty checkout first writes one unreachable checkpoint root; the
5330    /// complete closure is then authenticated and sorted. Object bodies remain
5331    /// independently transferable through [`Fs::export_object`].
5332    ///
5333    /// # Errors
5334    ///
5335    /// Returns typed checkpoint, closure, cancellation, storage, allocation,
5336    /// authentication, or bounded-work failures.
5337    pub async fn export_manifest(
5338        &self,
5339        budget: WorkBudget,
5340        cancellation: &CancellationToken,
5341    ) -> FsResult<GenerationExportManifest> {
5342        let checkpoint = self.checkpoint_root(budget, cancellation).await?;
5343        let generation_root = checkpoint.value;
5344        let mut work = checkpoint.work;
5345        let manifest = build_generation_export_manifest_async(
5346            &self.volume.fs.inner.objects,
5347            self.volume.id,
5348            self.volume.config,
5349            generation_root,
5350            closure_limits(self.volume.config),
5351            remaining(work, budget)?,
5352            cancellation,
5353        )
5354        .await
5355        .map_err(|failure| failure.map_with_prior_work(work, map_transfer_error))?;
5356        work = add(work, manifest.work)?;
5357        Ok(FsReceipt {
5358            value: manifest.value,
5359            work,
5360        })
5361    }
5362
5363    /// Writes the immutable generation-root checkpoint for the current sparse
5364    /// candidate without publishing authority.
5365    ///
5366    /// A clean checkout returns its selected generation with zero object work.
5367    /// Repeated dirty checkpoints are content-addressed and therefore exactly
5368    /// idempotent. The checkout and its pending publication state are unchanged.
5369    ///
5370    /// # Errors
5371    ///
5372    /// Returns typed canonical encoding, storage, cancellation, allocation, or
5373    /// bounded-work failures.
5374    pub async fn checkpoint(
5375        &self,
5376        budget: WorkBudget,
5377        cancellation: &CancellationToken,
5378    ) -> FsResult<GenerationId> {
5379        let checkpoint = self.checkpoint_root(budget, cancellation).await?;
5380        Ok(FsReceipt {
5381            value: GenerationId::new(checkpoint.value.digest),
5382            work: checkpoint.work,
5383        })
5384    }
5385
5386    async fn checkpoint_root(
5387        &self,
5388        budget: WorkBudget,
5389        cancellation: &CancellationToken,
5390    ) -> FsResult<ObjectId> {
5391        if self.prepared_merge_parent.is_some() || !self.has_pending_mutations() {
5392            return Ok(FsReceipt {
5393                value: self.generation_root,
5394                work: WorkCounters::default(),
5395            });
5396        }
5397        let checkpoint = build_checkpoint_async(
5398            &self.volume.fs.inner.objects,
5399            CheckpointRequest {
5400                base: self.base_generation_root,
5401                file_table: self.root.file_table,
5402                merge_parent: None,
5403            },
5404            decode_limits(self.volume.config),
5405            budget,
5406            cancellation,
5407        )
5408        .await
5409        .map_err(|failure| OperationFailure::new(failure.error.into(), *failure.work))?;
5410        Ok(FsReceipt {
5411            value: checkpoint.root,
5412            work: checkpoint.work,
5413        })
5414    }
5415
5416    /// Discards a private candidate and reauthenticates its immutable base.
5417    ///
5418    /// # Errors
5419    ///
5420    /// Rejects non-overlay modes and returns exact cancellation, storage,
5421    /// decode, or budget failures without changing the checkout on failure.
5422    pub async fn discard(
5423        &mut self,
5424        budget: WorkBudget,
5425        cancellation: &CancellationToken,
5426    ) -> FsResult<()> {
5427        if self.mode.mutations == MutationMode::None {
5428            return Err(OperationFailure::before_work(FsError::MutationNotAllowed));
5429        }
5430        let (root, work) = read_generation_root(
5431            &self.volume.fs.inner.objects,
5432            self.base_generation_root,
5433            self.volume.config,
5434            budget,
5435            cancellation,
5436        )
5437        .await?;
5438        if root.volume_id != self.volume.id {
5439            return Err(OperationFailure::new(FsError::VolumeMismatch, work));
5440        }
5441        self.root = root;
5442        self.base_root = self.root.clone();
5443        self.base_file_table = self.root.file_table;
5444        self.generation_root = self.base_generation_root;
5445        self.live_operation_id = None;
5446        self.prepared_merge_parent = None;
5447        self.dependencies.clear();
5448        Ok(FsReceipt { value: (), work })
5449    }
5450
5451    /// Explicitly advances one clean manual or tracking checkout to head.
5452    ///
5453    /// Equal generations avoid an immutable-object read. Dirty overlays are
5454    /// retained and rejected because adopting them requires safe rebase.
5455    ///
5456    /// # Errors
5457    ///
5458    /// Rejects pinned, live, or dirty checkouts and returns exact authority,
5459    /// storage, authentication, cancellation, or budget failures.
5460    pub async fn refresh_head(
5461        &mut self,
5462        budget: WorkBudget,
5463        cancellation: &CancellationToken,
5464    ) -> FsResult<GenerationId> {
5465        if !matches!(
5466            self.mode.consistency,
5467            ConsistencyMode::Manual | ConsistencyMode::TrackingSafe
5468        ) {
5469            return Err(OperationFailure::before_work(FsError::RefreshNotAllowed));
5470        }
5471        if self.has_pending_mutations() {
5472            return Err(OperationFailure::before_work(
5473                FsError::PendingMutationsRequireRebase,
5474            ));
5475        }
5476        let (generation_root, head, mut work) = self
5477            .volume
5478            .resolve_head_generation(budget, cancellation)
5479            .await?;
5480        if generation_root == self.generation_root {
5481            self.authority_head = Some(head);
5482            return Ok(FsReceipt {
5483                value: self.generation_id(),
5484                work,
5485            });
5486        }
5487        let (root, root_work) = read_generation_root(
5488            &self.volume.fs.inner.objects,
5489            generation_root,
5490            self.volume.config,
5491            remaining(work, budget)?,
5492            cancellation,
5493        )
5494        .await
5495        .map_err(|failure| failure.map_with_prior_work(work, std::convert::identity))?;
5496        work = add(work, root_work)?;
5497        if root.volume_id != self.volume.id {
5498            return Err(OperationFailure::new(FsError::VolumeMismatch, work));
5499        }
5500        self.base_generation_root = generation_root;
5501        self.generation_root = generation_root;
5502        self.root = root;
5503        self.base_root = self.root.clone();
5504        self.base_file_table = self.root.file_table;
5505        self.authority_head = Some(head);
5506        self.dependencies.clear();
5507        Ok(FsReceipt {
5508            value: self.generation_id(),
5509            work,
5510        })
5511    }
5512
5513    /// Safely advances to the current head and rebases the private candidate.
5514    ///
5515    /// Only exact regions captured by reads and mutation preconditions are
5516    /// compared. A conflict leaves the checkout unchanged. A safe dirty rebase
5517    /// combines the immutable candidate with the new base without retaining
5518    /// an unbounded operation log.
5519    ///
5520    /// # Errors
5521    ///
5522    /// Returns measured authority, probe, decode, replay, cancellation, or
5523    /// budget failures without changing the checkout unless the full rebase succeeds.
5524    pub async fn rebase_head(
5525        &mut self,
5526        maximum_conflicts: u32,
5527        budget: WorkBudget,
5528        cancellation: &CancellationToken,
5529    ) -> FsResult<RebaseDecision> {
5530        if self.live_operation_id.is_some() {
5531            return Err(OperationFailure::before_work(FsError::PendingLiveMutation));
5532        }
5533        self.rebase_head_for_live_retry(maximum_conflicts, budget, cancellation)
5534            .await
5535    }
5536
5537    async fn rebase_head_for_live_retry(
5538        &mut self,
5539        maximum_conflicts: u32,
5540        budget: WorkBudget,
5541        cancellation: &CancellationToken,
5542    ) -> FsResult<RebaseDecision> {
5543        if self.prepared_merge_parent.is_some() {
5544            return Err(OperationFailure::before_work(FsError::PreparedMergePending));
5545        }
5546        if !matches!(
5547            self.mode.consistency,
5548            ConsistencyMode::TrackingSafe | ConsistencyMode::Manual | ConsistencyMode::Live
5549        ) {
5550            return Err(OperationFailure::before_work(FsError::RefreshNotAllowed));
5551        }
5552        let (candidate_object, candidate_head, mut work) = self
5553            .volume
5554            .resolve_head_generation(budget, cancellation)
5555            .await?;
5556        let base = GenerationId::new(self.base_generation_root.digest);
5557        let candidate = GenerationId::new(candidate_object.digest);
5558        let probe = AuthenticatedGenerationProbe::new(
5559            &self.volume.fs.inner.objects,
5560            ProbeLimits {
5561                decode: decode_limits(self.volume.config),
5562                maximum_cached_generations: 2,
5563                maximum_cached_records: self.volume.config.limits.maximum_checkout_dependencies,
5564                maximum_extent_spans: self.volume.config.limits.maximum_directory_page_entries,
5565                maximum_content_payload_bytes: self.volume.config.limits.maximum_read_bytes,
5566                maximum_directory_entries: self.volume.config.limits.maximum_directory_page_entries,
5567            },
5568        )
5569        .map_err(|error| OperationFailure::new(FsError::Rebase(RebaseError::Probe(error)), work))?;
5570        let classification = classify_rebase_async(
5571            &probe,
5572            base,
5573            candidate,
5574            &self.dependencies,
5575            maximum_conflicts,
5576            remaining(work, budget)?,
5577            cancellation,
5578        )
5579        .await
5580        .map_err(|failure| failure.map_with_prior_work(work, FsError::Rebase))?;
5581        work = add(work, classification.work)?;
5582        if matches!(classification.decision, RebaseDecision::Conflicted { .. }) {
5583            return Ok(FsReceipt {
5584                value: classification.decision,
5585                work,
5586            });
5587        }
5588        if candidate_object == self.base_generation_root {
5589            self.authority_head = Some(candidate_head);
5590            return Ok(FsReceipt {
5591                value: classification.decision,
5592                work,
5593            });
5594        }
5595        let (candidate_root, root_work) = read_generation_root(
5596            &self.volume.fs.inner.objects,
5597            candidate_object,
5598            self.volume.config,
5599            remaining(work, budget)?,
5600            cancellation,
5601        )
5602        .await
5603        .map_err(|failure| failure.map_with_prior_work(work, std::convert::identity))?;
5604        work = add(work, root_work)?;
5605        if candidate_root.volume_id != self.volume.id {
5606            return Err(OperationFailure::new(FsError::VolumeMismatch, work));
5607        }
5608        let candidate_file_table = candidate_root.file_table;
5609        let candidate_base_root = candidate_root.clone();
5610        let rebased_root = self
5611            .rebase_private_candidate(
5612                candidate_object,
5613                &candidate_root,
5614                maximum_conflicts,
5615                &mut work,
5616                budget,
5617                cancellation,
5618            )
5619            .await?;
5620        self.base_generation_root = candidate_object;
5621        self.base_file_table = candidate_file_table;
5622        self.base_root = candidate_base_root;
5623        self.generation_root = candidate_object;
5624        self.root = rebased_root;
5625        self.authority_head = Some(candidate_head);
5626        Ok(FsReceipt {
5627            value: classification.decision,
5628            work,
5629        })
5630    }
5631
5632    async fn rebase_private_candidate(
5633        &self,
5634        candidate_object: ObjectId,
5635        candidate_root: &GenerationRoot,
5636        maximum_conflicts: u32,
5637        work: &mut WorkCounters,
5638        budget: WorkBudget,
5639        cancellation: &CancellationToken,
5640    ) -> Result<GenerationRoot, OperationFailure<FsError>> {
5641        if self.root.file_table == self.base_file_table {
5642            return Ok(candidate_root.clone());
5643        }
5644        let checkpoint = self
5645            .checkpoint_root(remaining(*work, budget)?, cancellation)
5646            .await
5647            .map_err(|failure| failure.map_with_prior_work(*work, std::convert::identity))?;
5648        *work = add(*work, checkpoint.work)?;
5649        let merged = merge_generation_async(
5650            &self.volume.fs.inner.objects,
5651            MergeGenerationRequest {
5652                base_generation: self.base_generation_root,
5653                base: self.base_root.clone(),
5654                ours_generation: Some(candidate_object),
5655                ours: candidate_root.clone(),
5656                theirs_generation: checkpoint.value,
5657                theirs: self.root.clone(),
5658                retain_theirs_parent: false,
5659                maximum_changes: u32::MAX,
5660                maximum_conflicts,
5661                resolutions: BTreeMap::new(),
5662            },
5663            decode_limits(self.volume.config),
5664            remaining(*work, budget)?,
5665            cancellation,
5666        )
5667        .await
5668        .map_err(|failure| map_merge_failure(failure, *work))?;
5669        *work = add(*work, merged.work)?;
5670        match merged.value {
5671            MergeGenerationOutcome::Prepared { root, .. } => Ok(root),
5672            MergeGenerationOutcome::Conflicted { .. } => {
5673                Err(OperationFailure::new(FsError::InvalidDiff, *work))
5674            }
5675        }
5676    }
5677
5678    /// Explicitly performs the same observation-safe head advancement used
5679    /// automatically before live reads.
5680    ///
5681    /// # Errors
5682    ///
5683    /// Rejects non-live checkouts and returns a typed region conflict rather
5684    /// than crossing any prior observation or pending mutation dependency.
5685    pub async fn refresh_live(
5686        &mut self,
5687        budget: WorkBudget,
5688        cancellation: &CancellationToken,
5689    ) -> FsResult<GenerationId> {
5690        if self.mode.consistency != ConsistencyMode::Live {
5691            return Err(OperationFailure::before_work(FsError::RefreshNotAllowed));
5692        }
5693        let synchronized = self.synchronize_live(budget, cancellation).await?;
5694        Ok(FsReceipt {
5695            value: self.generation_id(),
5696            work: synchronized.work,
5697        })
5698    }
5699
5700    async fn synchronize_live(
5701        &mut self,
5702        budget: WorkBudget,
5703        cancellation: &CancellationToken,
5704    ) -> FsResult<()> {
5705        if self.mode.consistency != ConsistencyMode::Live {
5706            return Ok(FsReceipt {
5707                value: (),
5708                work: WorkCounters::default(),
5709            });
5710        }
5711        let rebase = self
5712            .rebase_head(
5713                self.volume.config.limits.maximum_checkout_dependencies,
5714                budget,
5715                cancellation,
5716            )
5717            .await?;
5718        match rebase.value {
5719            RebaseDecision::Safe { .. } => Ok(FsReceipt {
5720                value: (),
5721                work: rebase.work,
5722            }),
5723            RebaseDecision::Conflicted {
5724                conflicts,
5725                truncated,
5726            } => Err(OperationFailure::new(
5727                FsError::LiveConflict {
5728                    conflicts,
5729                    truncated,
5730                },
5731                rebase.work,
5732            )),
5733        }
5734    }
5735
5736    async fn capture_mutation_dependencies(
5737        &self,
5738        operations: &[Mutation],
5739        budget: WorkBudget,
5740        cancellation: &CancellationToken,
5741    ) -> FsResult<Vec<Dependency>> {
5742        let mut work = WorkCounters::default();
5743        let mut dependencies = Vec::new();
5744        let mut exact_regions = Vec::new();
5745        exact_regions
5746            .try_reserve_exact(operations.len().saturating_mul(4))
5747            .map_err(|_| OperationFailure::before_work(FsError::PendingMutationAllocationFailed))?;
5748        for operation in operations {
5749            let Some([first_path, second_path]) = operation.paths() else {
5750                let records = self
5751                    .identity_base_records(operation, remaining(work, budget)?, cancellation)
5752                    .await
5753                    .map_err(|failure| failure.map_with_prior_work(work, std::convert::identity))?;
5754                work = add(work, records.work)?;
5755                let (first_record, second_record) = records.value;
5756                for known_region in exact_mutation_regions(operation, first_record, second_record)
5757                    .into_iter()
5758                    .flatten()
5759                {
5760                    exact_regions.push(known_region);
5761                }
5762                continue;
5763            };
5764            let first = crate::kernel::observe_path_edges_async(
5765                &self.volume.fs.inner.objects,
5766                &self.base_root,
5767                first_path,
5768                self.volume.config,
5769                remaining(work, budget)?,
5770                cancellation,
5771            )
5772            .await
5773            .map_err(|failure| failure.map_with_prior_work(work, FsError::Path))?;
5774            work = add(work, first.lookup.work)?;
5775            dependencies.extend(first.dependencies);
5776            let second_record = if second_path == first_path {
5777                first.lookup.record
5778            } else {
5779                let second = crate::kernel::observe_path_edges_async(
5780                    &self.volume.fs.inner.objects,
5781                    &self.base_root,
5782                    second_path,
5783                    self.volume.config,
5784                    remaining(work, budget)?,
5785                    cancellation,
5786                )
5787                .await
5788                .map_err(|failure| failure.map_with_prior_work(work, FsError::Path))?;
5789                work = add(work, second.lookup.work)?;
5790                dependencies.extend(second.dependencies);
5791                second.lookup.record
5792            };
5793            for known_region in
5794                exact_mutation_regions(operation, first.lookup.record, second_record)
5795                    .into_iter()
5796                    .flatten()
5797            {
5798                exact_regions.push(known_region);
5799            }
5800        }
5801        let captured = capture_known_dependencies_async(
5802            &self.volume.fs.inner.objects,
5803            self.volume.config,
5804            exact_regions,
5805            remaining(work, budget)?,
5806            cancellation,
5807        )
5808        .await
5809        .map_err(|failure| failure.map_with_prior_work(work, std::convert::identity))?;
5810        work = add(work, captured.work)?;
5811        dependencies.extend(captured.value);
5812        Ok(FsReceipt {
5813            value: dependencies,
5814            work,
5815        })
5816    }
5817
5818    async fn identity_base_records(
5819        &self,
5820        operation: &Mutation,
5821        budget: WorkBudget,
5822        cancellation: &CancellationToken,
5823    ) -> FsResult<(Option<FileRecord>, Option<FileRecord>)> {
5824        let (first_id, second_id) = match operation {
5825            Mutation::File { file_id, .. } => (Some(*file_id), None),
5826            Mutation::CloneFileRange {
5827                source_file_id,
5828                destination_file_id,
5829                ..
5830            } => (Some(*source_file_id), Some(*destination_file_id)),
5831            _ => (None, None),
5832        };
5833        let mut work = WorkCounters::default();
5834        let first_record = if let Some(file_id) = first_id {
5835            let lookup = lookup_file_record_async(
5836                &self.volume.fs.inner.objects,
5837                self.base_root.file_table,
5838                file_id,
5839                decode_limits(self.volume.config),
5840                budget,
5841                cancellation,
5842            )
5843            .await
5844            .map_err(|failure| failure.map_with_prior_work(work, FsError::FileRecord))?;
5845            work = add(work, lookup.work)?;
5846            lookup.record
5847        } else {
5848            None
5849        };
5850        let second_record = if second_id == first_id {
5851            first_record
5852        } else if let Some(file_id) = second_id {
5853            let lookup = lookup_file_record_async(
5854                &self.volume.fs.inner.objects,
5855                self.base_root.file_table,
5856                file_id,
5857                decode_limits(self.volume.config),
5858                remaining(work, budget)?,
5859                cancellation,
5860            )
5861            .await
5862            .map_err(|failure| failure.map_with_prior_work(work, FsError::FileRecord))?;
5863            work = add(work, lookup.work)?;
5864            lookup.record
5865        } else {
5866            None
5867        };
5868        Ok(FsReceipt {
5869            value: (first_record, second_record),
5870            work,
5871        })
5872    }
5873
5874    /// Atomically applies one ordered mutation batch to this private checkout.
5875    ///
5876    /// The immutable base remains unchanged. A failed batch may leave harmless
5877    /// unreferenced objects but never replaces the checkout candidate.
5878    ///
5879    /// # Errors
5880    ///
5881    /// Rejects non-writable modes and returns exact planning, storage,
5882    /// cancellation, semantic, or work-budget failures.
5883    pub async fn mutate(
5884        &mut self,
5885        operations: Vec<Mutation>,
5886        budget: WorkBudget,
5887        cancellation: &CancellationToken,
5888    ) -> FsResult<()> {
5889        if self.mode.access != AccessMode::ReadWrite
5890            || !matches!(
5891                self.mode.mutations,
5892                MutationMode::PrivateOverlay | MutationMode::DirectLive
5893            )
5894        {
5895            return Err(OperationFailure::before_work(FsError::MutationNotAllowed));
5896        }
5897        self.mutate_candidate(operations, budget, cancellation)
5898            .await
5899    }
5900
5901    /// Applies and durably publishes one direct-live mutation operation.
5902    ///
5903    /// Authority races are automatically rebased only when every exact read
5904    /// and mutation dependency remains unchanged. The stable operation ID is
5905    /// reused across those safe retries. A region conflict or exhausted retry
5906    /// bound leaves the sparse candidate available to [`Self::resume_live`] or
5907    /// [`Self::discard`].
5908    ///
5909    /// # Errors
5910    ///
5911    /// Rejects non-live modes, an existing unresolved candidate, zero bounds,
5912    /// and returns exact mutation, rebase, publication, cancellation, storage,
5913    /// or work-budget failures.
5914    pub async fn mutate_live(
5915        &mut self,
5916        operations: Vec<Mutation>,
5917        operation_id: OperationId,
5918        maximum_attempts: u32,
5919        maximum_conflicts: u32,
5920        budget: WorkBudget,
5921        cancellation: &CancellationToken,
5922    ) -> FsResult<LiveMutationOutcome> {
5923        self.validate_live_operation(maximum_attempts)?;
5924        if self.has_pending_mutations() {
5925            return Err(OperationFailure::before_work(FsError::PendingLiveMutation));
5926        }
5927        let mutation = self
5928            .mutate_candidate(operations, budget, cancellation)
5929            .await?;
5930        let mut work = mutation.work;
5931        let publication = self
5932            .resume_live(
5933                operation_id,
5934                maximum_attempts,
5935                maximum_conflicts,
5936                remaining(work, budget)?,
5937                cancellation,
5938            )
5939            .await
5940            .map_err(|failure| failure.map_with_prior_work(work, std::convert::identity))?;
5941        work = add(work, publication.work)?;
5942        Ok(FsReceipt {
5943            value: publication.value,
5944            work,
5945        })
5946    }
5947
5948    /// Compiles, atomically applies, and durably publishes one authored live transaction.
5949    ///
5950    /// This is the canonical byte-oriented direct-live orchestration boundary
5951    /// for language SDKs. It preserves generated file identities and one exact
5952    /// work receipt across content staging, sparse mutation, safe rebase, and
5953    /// authority publication.
5954    ///
5955    /// # Errors
5956    ///
5957    /// Returns the combined failures of [`Self::apply_authored_transaction`]
5958    /// and [`Self::resume_live`]. An unresolved publication retains the sparse
5959    /// candidate for an exact [`Self::resume_live`] retry.
5960    pub async fn apply_authored_live(
5961        &mut self,
5962        authored: Vec<AuthoredMutation>,
5963        operation_id: OperationId,
5964        maximum_attempts: u32,
5965        maximum_conflicts: u32,
5966        budget: WorkBudget,
5967        cancellation: &CancellationToken,
5968    ) -> FsResult<AuthoredLiveMutationResult> {
5969        self.validate_live_operation(maximum_attempts)?;
5970        if self.has_pending_mutations() {
5971            return Err(OperationFailure::before_work(FsError::PendingLiveMutation));
5972        }
5973        let transaction = self
5974            .apply_authored_transaction(authored, budget, cancellation)
5975            .await?;
5976        let mut work = transaction.work;
5977        let created_file_ids = transaction.value.created_file_ids;
5978        let publication = self
5979            .resume_live(
5980                operation_id,
5981                maximum_attempts,
5982                maximum_conflicts,
5983                remaining(work, budget)?,
5984                cancellation,
5985            )
5986            .await
5987            .map_err(|failure| failure.map_with_prior_work(work, std::convert::identity))?;
5988        work = add(work, publication.work)?;
5989        Ok(FsReceipt {
5990            value: AuthoredLiveMutationResult {
5991                created_file_ids,
5992                outcome: publication.value,
5993            },
5994            work,
5995        })
5996    }
5997
5998    /// Resumes publication of an unresolved direct-live candidate.
5999    ///
6000    /// The caller must reuse the original operation ID after an indeterminate
6001    /// transport result. Exact authority idempotency resolves whether that
6002    /// operation was already durable.
6003    ///
6004    /// # Errors
6005    ///
6006    /// Returns the same failures as [`Self::mutate_live`] and rejects a clean
6007    /// checkout.
6008    pub async fn resume_live(
6009        &mut self,
6010        operation_id: OperationId,
6011        maximum_attempts: u32,
6012        maximum_conflicts: u32,
6013        budget: WorkBudget,
6014        cancellation: &CancellationToken,
6015    ) -> FsResult<LiveMutationOutcome> {
6016        self.validate_live_operation(maximum_attempts)?;
6017        if !self.has_pending_mutations() {
6018            return Err(OperationFailure::before_work(FsError::NoPendingMutations));
6019        }
6020        match self.live_operation_id {
6021            Some(retained) if retained != operation_id => {
6022                return Err(OperationFailure::before_work(FsError::PendingLiveMutation));
6023            }
6024            Some(_) => {}
6025            None => self.live_operation_id = Some(operation_id),
6026        }
6027        let publication = self
6028            .publish_live(
6029                operation_id,
6030                maximum_attempts,
6031                maximum_conflicts,
6032                budget,
6033                cancellation,
6034            )
6035            .await?;
6036        if matches!(
6037            publication.value,
6038            LiveMutationOutcome::Committed { .. }
6039                | LiveMutationOutcome::AlreadyCommitted { .. }
6040                | LiveMutationOutcome::IdempotencyConflict { .. }
6041        ) {
6042            self.live_operation_id = None;
6043        }
6044        Ok(publication)
6045    }
6046
6047    fn validate_live_operation(
6048        &self,
6049        maximum_attempts: u32,
6050    ) -> Result<(), OperationFailure<FsError>> {
6051        if self.mode.access != AccessMode::ReadWrite
6052            || self.mode.consistency != ConsistencyMode::Live
6053            || self.mode.mutations != MutationMode::DirectLive
6054        {
6055            return Err(OperationFailure::before_work(FsError::MutationNotAllowed));
6056        }
6057        if maximum_attempts == 0 {
6058            return Err(OperationFailure::before_work(FsError::ZeroLiveRetryLimit));
6059        }
6060        Ok(())
6061    }
6062
6063    async fn publish_live(
6064        &mut self,
6065        operation_id: OperationId,
6066        maximum_attempts: u32,
6067        maximum_conflicts: u32,
6068        budget: WorkBudget,
6069        cancellation: &CancellationToken,
6070    ) -> FsResult<LiveMutationOutcome> {
6071        let mut work = WorkCounters::default();
6072        let mut state = LiveRetryState::new(maximum_attempts)
6073            .map_err(|error| OperationFailure::before_work(error.into()))?;
6074        loop {
6075            let publication = self
6076                .publish_pending(operation_id, None, remaining(work, budget)?, cancellation)
6077                .await
6078                .map_err(|failure| failure.map_with_prior_work(work, std::convert::identity))?;
6079            work = add(work, publication.work)?;
6080            let action = state
6081                .observe_publication(live_publication_observation(publication.value))
6082                .map_err(|error| OperationFailure::new(error.into(), work))?;
6083            match action {
6084                LiveRetryAction::Complete(outcome) => {
6085                    return Ok(FsReceipt {
6086                        value: outcome,
6087                        work,
6088                    });
6089                }
6090                LiveRetryAction::Publish => {
6091                    return Err(OperationFailure::new(
6092                        FsError::LiveRetry(LiveRetryError::InvalidTransition),
6093                        work,
6094                    ));
6095                }
6096                LiveRetryAction::Rebase => {
6097                    let rebase = self
6098                        .rebase_head_for_live_retry(
6099                            maximum_conflicts,
6100                            remaining(work, budget)?,
6101                            cancellation,
6102                        )
6103                        .await
6104                        .map_err(|failure| {
6105                            failure.map_with_prior_work(work, std::convert::identity)
6106                        })?;
6107                    work = add(work, rebase.work)?;
6108                    match state
6109                        .observe_rebase(rebase.value)
6110                        .map_err(|error| OperationFailure::new(error.into(), work))?
6111                    {
6112                        LiveRetryAction::Publish => {}
6113                        LiveRetryAction::Complete(outcome) => {
6114                            return Ok(FsReceipt {
6115                                value: outcome,
6116                                work,
6117                            });
6118                        }
6119                        LiveRetryAction::Rebase => {
6120                            return Err(OperationFailure::new(
6121                                FsError::LiveRetry(LiveRetryError::InvalidTransition),
6122                                work,
6123                            ));
6124                        }
6125                    }
6126                }
6127            }
6128        }
6129    }
6130
6131    async fn mutate_candidate(
6132        &mut self,
6133        mut operations: Vec<Mutation>,
6134        budget: WorkBudget,
6135        cancellation: &CancellationToken,
6136    ) -> FsResult<()> {
6137        if self.live_operation_id.is_some() {
6138            return Err(OperationFailure::before_work(FsError::PendingLiveMutation));
6139        }
6140        if self.prepared_merge_parent.is_some() {
6141            return Err(OperationFailure::before_work(FsError::PreparedMergePending));
6142        }
6143        let maximum = usize::try_from(self.volume.config.limits.maximum_mutations_per_batch)
6144            .unwrap_or(usize::MAX);
6145        if operations.capacity() > maximum {
6146            return Err(OperationFailure::before_work(
6147                FsError::ExcessiveMutationCapacity,
6148            ));
6149        }
6150        // Keep the public mutation future within the Windows default thread
6151        // stack. Each planning and mutation phase is boxed once per
6152        // transaction rather than growing one deeply nested future.
6153        let admitted =
6154            Box::pin(self.admit_mutation_names(&mut operations, budget, cancellation)).await?;
6155        let captured =
6156            Box::pin(self.capture_mutation_dependencies(&operations, budget, cancellation)).await?;
6157        let dependency_work = add(admitted.work, captured.work)?;
6158        let mutation_dependencies = captured.value;
6159        let mut next_dependencies = self.dependencies.clone();
6160        next_dependencies
6161            .extend_mutations(
6162                mutation_dependencies,
6163                self.volume.config.limits.maximum_checkout_dependencies,
6164            )
6165            .map_err(|error| OperationFailure::new(error.into(), dependency_work))?;
6166        let prior_file_table = self.root.file_table;
6167        let (receipt, _) = Box::pin(apply_generation_mutations_retaining_async(
6168            &self.volume.fs.inner.objects,
6169            &self.root,
6170            operations,
6171            self.volume.config,
6172            remaining(dependency_work, budget)?,
6173            cancellation,
6174        ))
6175        .await
6176        .map_err(|failure| failure.map_with_prior_work(dependency_work, FsError::Mutation))?;
6177        let work = add(dependency_work, receipt.work)?;
6178        if receipt.root.file_table != prior_file_table {
6179            if receipt.root.file_table == self.base_file_table {
6180                next_dependencies.clear_mutations();
6181            }
6182            self.dependencies = next_dependencies;
6183            self.root = receipt.root;
6184        }
6185        Ok(FsReceipt { value: (), work })
6186    }
6187
6188    /// Admits every namespace-binding destination in one ordered mutation
6189    /// batch before the generation kernel runs. The kernel works on exact
6190    /// byte-ordered pages and therefore cannot discover folded siblings from
6191    /// its sparse endpoint lookups alone; this boundary owns that policy while
6192    /// retaining a zero-work fast path for the default sensitive profile.
6193    #[allow(clippy::too_many_lines)]
6194    async fn admit_mutation_names(
6195        &mut self,
6196        operations: &mut [Mutation],
6197        budget: WorkBudget,
6198        cancellation: &CancellationToken,
6199    ) -> FsResult<()> {
6200        if self.volume.config.case_sensitivity != CaseSensitivity::ProfileFolded
6201            && self.volume.config.unicode != UnicodePolicy::RequireNfc
6202        {
6203            return Ok(FsReceipt {
6204                value: (),
6205                work: WorkCounters::default(),
6206            });
6207        }
6208
6209        let mut work = WorkCounters::default();
6210        let mut pending: Vec<(NamespacePath, FileId)> = Vec::new();
6211        let mut removed: Vec<(NamespacePath, FileId)> = Vec::new();
6212        pending
6213            .try_reserve_exact(operations.len())
6214            .map_err(|_| OperationFailure::before_work(FsError::PendingMutationAllocationFailed))?;
6215        removed
6216            .try_reserve_exact(operations.len())
6217            .map_err(|_| OperationFailure::before_work(FsError::PendingMutationAllocationFailed))?;
6218        for operation in operations.iter_mut() {
6219            cancellation
6220                .check()
6221                .map_err(|error| OperationFailure::new(error.into(), work))?;
6222            let canonicalized = Box::pin(self.canonicalize_existing_mutation(
6223                operation,
6224                &pending,
6225                remaining(work, budget)?,
6226                cancellation,
6227            ))
6228            .await
6229            .map_err(|failure| failure.map_with_prior_work(work, std::convert::identity))?;
6230            work = add(work, canonicalized.work)?;
6231            let parent_canonicalized = Box::pin(self.canonicalize_new_mutation_parents(
6232                operation,
6233                &pending,
6234                remaining(work, budget)?,
6235                cancellation,
6236            ))
6237            .await
6238            .map_err(|failure| failure.map_with_prior_work(work, std::convert::identity))?;
6239            work = add(work, parent_canonicalized.work)?;
6240            match operation {
6241                Mutation::Create { path, record } | Mutation::Restore { path, record } => {
6242                    let allowed = removed_binding_id(path, &removed);
6243                    let allowed_ids: &[FileId] = match allowed {
6244                        Some(ref file_id) => std::slice::from_ref(file_id),
6245                        None => &[],
6246                    };
6247                    let parent_pending = pending_parent_exists(
6248                        path,
6249                        &pending,
6250                        self.volume.config.case_sensitivity,
6251                        self.volume.config.limits,
6252                    );
6253                    let admission = Box::pin(self.admit_new_name(
6254                        path,
6255                        remaining(work, budget)?,
6256                        cancellation,
6257                        allowed_ids,
6258                        parent_pending,
6259                    ))
6260                    .await
6261                    .map_err(|failure| failure.map_with_prior_work(work, std::convert::identity))?;
6262                    work = add(work, admission.work)?;
6263                    if self.volume.config.case_sensitivity == CaseSensitivity::ProfileFolded
6264                        && pending_name_collision(path, &pending, allowed_ids)
6265                    {
6266                        return Err(OperationFailure::new(FsError::NameCollision, work));
6267                    }
6268                    pending.push((path.clone(), record.file_id));
6269                }
6270                Mutation::Link {
6271                    source,
6272                    destination,
6273                } => {
6274                    let source_id = self
6275                        .pending_or_existing_file_id(
6276                            source,
6277                            &pending,
6278                            remaining(work, budget)?,
6279                            cancellation,
6280                        )
6281                        .await
6282                        .map_err(|failure| {
6283                            failure.map_with_prior_work(work, std::convert::identity)
6284                        })?;
6285                    work = add(work, source_id.work)?;
6286                    let allowed = removed_binding_id(destination, &removed);
6287                    let allowed_ids: &[FileId] = match allowed {
6288                        Some(ref file_id) => std::slice::from_ref(file_id),
6289                        None => &[],
6290                    };
6291                    let parent_pending = pending_parent_exists(
6292                        destination,
6293                        &pending,
6294                        self.volume.config.case_sensitivity,
6295                        self.volume.config.limits,
6296                    );
6297                    let admission = Box::pin(self.admit_new_name(
6298                        destination,
6299                        remaining(work, budget)?,
6300                        cancellation,
6301                        allowed_ids,
6302                        parent_pending,
6303                    ))
6304                    .await
6305                    .map_err(|failure| failure.map_with_prior_work(work, std::convert::identity))?;
6306                    work = add(work, admission.work)?;
6307                    if self.volume.config.case_sensitivity == CaseSensitivity::ProfileFolded
6308                        && pending_name_collision(destination, &pending, allowed_ids)
6309                    {
6310                        return Err(OperationFailure::new(FsError::NameCollision, work));
6311                    }
6312                    pending.push((destination.clone(), source_id.value));
6313                }
6314                Mutation::Rename {
6315                    source,
6316                    destination,
6317                    replace,
6318                } => {
6319                    let source_id = self
6320                        .pending_or_existing_file_id(
6321                            source,
6322                            &pending,
6323                            remaining(work, budget)?,
6324                            cancellation,
6325                        )
6326                        .await
6327                        .map_err(|failure| {
6328                            failure.map_with_prior_work(work, std::convert::identity)
6329                        })?;
6330                    work = add(work, source_id.work)?;
6331                    let mut allowed_ids = Vec::with_capacity(2);
6332                    allowed_ids.push(source_id.value);
6333                    if let Some(file_id) = removed_binding_id(destination, &removed)
6334                        && file_id != source_id.value
6335                    {
6336                        allowed_ids.push(file_id);
6337                    }
6338                    let parent_pending = pending_parent_exists(
6339                        destination,
6340                        &pending,
6341                        self.volume.config.case_sensitivity,
6342                        self.volume.config.limits,
6343                    );
6344                    let admission = Box::pin(self.admit_new_name(
6345                        destination,
6346                        remaining(work, budget)?,
6347                        cancellation,
6348                        &allowed_ids,
6349                        parent_pending,
6350                    ))
6351                    .await
6352                    .map_err(|failure| failure.map_with_prior_work(work, std::convert::identity))?;
6353                    work = add(work, admission.work)?;
6354                    if self.volume.config.case_sensitivity == CaseSensitivity::ProfileFolded
6355                        && pending_name_collision(destination, &pending, &allowed_ids)
6356                    {
6357                        return Err(OperationFailure::new(FsError::NameCollision, work));
6358                    }
6359                    pending.retain(|(path, _)| path != source);
6360                    if *replace {
6361                        pending.retain(|(path, _)| path != destination);
6362                    }
6363                    removed.push((source.clone(), source_id.value));
6364                    pending.push((destination.clone(), source_id.value));
6365                }
6366                Mutation::Remove { path, .. } => {
6367                    if pending_binding_id(path, &pending, self.volume.config.case_sensitivity)
6368                        .is_none()
6369                    {
6370                        let removed_id = self
6371                            .pending_or_existing_file_id(
6372                                path,
6373                                &pending,
6374                                remaining(work, budget)?,
6375                                cancellation,
6376                            )
6377                            .await
6378                            .map_err(|failure| {
6379                                failure.map_with_prior_work(work, std::convert::identity)
6380                            })?;
6381                        work = add(work, removed_id.work)?;
6382                        removed.push((path.clone(), removed_id.value));
6383                    }
6384                    pending.retain(|(pending_path, _)| pending_path != path);
6385                }
6386                Mutation::SetMetadata { .. }
6387                | Mutation::Write { .. }
6388                | Mutation::ValidateRegular { .. }
6389                | Mutation::Resize { .. }
6390                | Mutation::ZeroRange { .. }
6391                | Mutation::Preallocate { .. }
6392                | Mutation::CloneRange { .. }
6393                | Mutation::File { .. }
6394                | Mutation::CloneFileRange { .. } => {}
6395            }
6396        }
6397        Ok(FsReceipt { value: (), work })
6398    }
6399
6400    async fn canonicalize_new_mutation_parents(
6401        &mut self,
6402        operation: &mut Mutation,
6403        pending: &[(NamespacePath, FileId)],
6404        budget: WorkBudget,
6405        cancellation: &CancellationToken,
6406    ) -> FsResult<()> {
6407        if self.volume.config.case_sensitivity != CaseSensitivity::ProfileFolded {
6408            return Ok(FsReceipt {
6409                value: (),
6410                work: WorkCounters::default(),
6411            });
6412        }
6413        let (Mutation::Create { path, .. }
6414        | Mutation::Link {
6415            destination: path, ..
6416        }
6417        | Mutation::Rename {
6418            destination: path, ..
6419        }) = operation
6420        else {
6421            return Ok(FsReceipt {
6422                value: (),
6423                work: WorkCounters::default(),
6424            });
6425        };
6426        let Some((parent, name)) = path.split_last() else {
6427            return Ok(FsReceipt {
6428                value: (),
6429                work: WorkCounters::default(),
6430            });
6431        };
6432        if parent.is_empty() {
6433            return Ok(FsReceipt {
6434                value: (),
6435                work: WorkCounters::default(),
6436            });
6437        }
6438        let resolved = self
6439            .canonicalize_path_components(parent, pending, budget, cancellation)
6440            .await?;
6441        let work = resolved.work;
6442        let Some(resolved_parent) = resolved.value else {
6443            return Ok(FsReceipt { value: (), work });
6444        };
6445        let mut components = resolved_parent.components().to_vec();
6446        components.push(name.clone());
6447        *path = NamespacePath::new(components, self.volume.config.limits).map_err(|error| {
6448            OperationFailure::new(FsError::Path(PathLookupError::NamespacePath(error)), work)
6449        })?;
6450        Ok(FsReceipt { value: (), work })
6451    }
6452
6453    async fn canonicalize_existing_mutation(
6454        &mut self,
6455        operation: &mut Mutation,
6456        pending: &[(NamespacePath, FileId)],
6457        budget: WorkBudget,
6458        cancellation: &CancellationToken,
6459    ) -> FsResult<()> {
6460        if self.volume.config.case_sensitivity != CaseSensitivity::ProfileFolded {
6461            return Ok(FsReceipt {
6462                value: (),
6463                work: WorkCounters::default(),
6464            });
6465        }
6466        let mut work = WorkCounters::default();
6467        let paths = match operation {
6468            Mutation::Remove { path, .. }
6469            | Mutation::SetMetadata { path, .. }
6470            | Mutation::Write { path, .. }
6471            | Mutation::ValidateRegular { path }
6472            | Mutation::Resize { path, .. }
6473            | Mutation::ZeroRange { path, .. }
6474            | Mutation::Preallocate { path, .. } => {
6475                let resolved = self
6476                    .canonical_existing_path(path, pending, remaining(work, budget)?, cancellation)
6477                    .await?;
6478                *path = resolved.value;
6479                work = add(work, resolved.work)?;
6480                return Ok(FsReceipt { value: (), work });
6481            }
6482            Mutation::Rename { source, .. } | Mutation::Link { source, .. } => {
6483                let resolved = self
6484                    .canonical_existing_path(
6485                        source,
6486                        pending,
6487                        remaining(work, budget)?,
6488                        cancellation,
6489                    )
6490                    .await?;
6491                *source = resolved.value;
6492                work = add(work, resolved.work)?;
6493                return Ok(FsReceipt { value: (), work });
6494            }
6495            Mutation::CloneRange {
6496                source,
6497                destination,
6498                ..
6499            } => Some((source, destination)),
6500            Mutation::Create { .. }
6501            | Mutation::Restore { .. }
6502            | Mutation::File { .. }
6503            | Mutation::CloneFileRange { .. } => None,
6504        };
6505        if let Some((source, destination)) = paths {
6506            let resolved_source = self
6507                .canonical_existing_path(source, pending, remaining(work, budget)?, cancellation)
6508                .await?;
6509            *source = resolved_source.value;
6510            work = add(work, resolved_source.work)?;
6511            let resolved_destination = self
6512                .canonical_existing_path(
6513                    destination,
6514                    pending,
6515                    remaining(work, budget)?,
6516                    cancellation,
6517                )
6518                .await?;
6519            *destination = resolved_destination.value;
6520            work = add(work, resolved_destination.work)?;
6521        }
6522        Ok(FsReceipt { value: (), work })
6523    }
6524
6525    async fn canonical_existing_path(
6526        &mut self,
6527        path: &NamespacePath,
6528        pending: &[(NamespacePath, FileId)],
6529        budget: WorkBudget,
6530        cancellation: &CancellationToken,
6531    ) -> FsResult<NamespacePath> {
6532        if let Some((exact, _)) =
6533            pending_binding_path(path, pending, self.volume.config.case_sensitivity)
6534        {
6535            return Ok(FsReceipt {
6536                value: exact,
6537                work: WorkCounters::default(),
6538            });
6539        }
6540        let mut work = WorkCounters::default();
6541        let exact = self
6542            .lookup_no_follow_at_current(path, budget, cancellation, false)
6543            .await?;
6544        work = add(work, exact.work)?;
6545        if exact.value.record.is_some()
6546            || self.volume.config.case_sensitivity != CaseSensitivity::ProfileFolded
6547        {
6548            if exact.value.record.is_some() {
6549                return Ok(FsReceipt {
6550                    value: path.clone(),
6551                    work,
6552                });
6553            }
6554            return Err(OperationFailure::new(FsError::NotFound, work));
6555        }
6556
6557        if path.is_root() {
6558            return Err(OperationFailure::new(FsError::NotFound, work));
6559        }
6560        let resolved = self
6561            .canonicalize_path_components(
6562                path.components(),
6563                pending,
6564                remaining(work, budget)?,
6565                cancellation,
6566            )
6567            .await?;
6568        work = add(work, resolved.work)?;
6569        let canonical = resolved
6570            .value
6571            .ok_or_else(|| OperationFailure::new(FsError::NotFound, work))?;
6572        Ok(FsReceipt {
6573            value: canonical,
6574            work,
6575        })
6576    }
6577
6578    async fn canonicalize_path_components(
6579        &mut self,
6580        requested_components: &[LogicalName],
6581        pending: &[(NamespacePath, FileId)],
6582        budget: WorkBudget,
6583        cancellation: &CancellationToken,
6584    ) -> FsResult<Option<NamespacePath>> {
6585        let mut work = WorkCounters::default();
6586        let mut canonical_components = Vec::new();
6587        for requested_component in requested_components {
6588            let candidate = NamespacePath::new(
6589                canonical_components
6590                    .iter()
6591                    .cloned()
6592                    .chain(std::iter::once(requested_component.clone()))
6593                    .collect(),
6594                self.volume.config.limits,
6595            )
6596            .map_err(|error| {
6597                OperationFailure::new(FsError::Path(PathLookupError::NamespacePath(error)), work)
6598            })?;
6599            if let Some((pending_path, _)) =
6600                pending_binding_path(&candidate, pending, self.volume.config.case_sensitivity)
6601            {
6602                canonical_components.push(
6603                    pending_path
6604                        .components()
6605                        .last()
6606                        .cloned()
6607                        .ok_or_else(|| OperationFailure::new(FsError::NotFound, work))?,
6608                );
6609                continue;
6610            }
6611            let probe = self
6612                .lookup_no_follow_at_current(
6613                    &candidate,
6614                    remaining(work, budget)?,
6615                    cancellation,
6616                    false,
6617                )
6618                .await?;
6619            work = add(work, probe.work)?;
6620            if probe.value.record.is_some() {
6621                canonical_components.push(requested_component.clone());
6622                continue;
6623            }
6624            let parent =
6625                NamespacePath::new(canonical_components.clone(), self.volume.config.limits)
6626                    .map_err(|error| {
6627                        OperationFailure::new(
6628                            FsError::Path(PathLookupError::NamespacePath(error)),
6629                            work,
6630                        )
6631                    })?;
6632            let siblings = self
6633                .find_case_folded_sibling(
6634                    parent.components(),
6635                    requested_component,
6636                    remaining(work, budget)?,
6637                    cancellation,
6638                    &[],
6639                )
6640                .await?;
6641            work = add(work, siblings.work)?;
6642            let Some(exact_name) = siblings.value else {
6643                return Ok(FsReceipt { value: None, work });
6644            };
6645            canonical_components.push(exact_name);
6646        }
6647        let canonical = NamespacePath::new(canonical_components, self.volume.config.limits)
6648            .map_err(|error| {
6649                OperationFailure::new(FsError::Path(PathLookupError::NamespacePath(error)), work)
6650            })?;
6651        Ok(FsReceipt {
6652            value: Some(canonical),
6653            work,
6654        })
6655    }
6656
6657    async fn pending_or_existing_file_id(
6658        &mut self,
6659        path: &NamespacePath,
6660        pending: &[(NamespacePath, FileId)],
6661        budget: WorkBudget,
6662        cancellation: &CancellationToken,
6663    ) -> FsResult<FileId> {
6664        if let Some(file_id) =
6665            pending_binding_id(path, pending, self.volume.config.case_sensitivity)
6666        {
6667            return Ok(FsReceipt {
6668                value: file_id,
6669                work: WorkCounters::default(),
6670            });
6671        }
6672        let lookup = self.lookup_no_follow(path, budget, cancellation).await?;
6673        let work = lookup.work;
6674        let record = lookup
6675            .value
6676            .record
6677            .ok_or_else(|| OperationFailure::new(FsError::NotFound, work))?;
6678        Ok(FsReceipt {
6679            value: record.file_id,
6680            work,
6681        })
6682    }
6683
6684    /// Compiles and atomically applies one complete caller-authored transaction.
6685    ///
6686    /// Immutable byte and metadata objects are admitted before the candidate
6687    /// root is replaced. Failure may leave unreachable immutable objects, but
6688    /// never a partially changed checkout candidate.
6689    ///
6690    /// # Errors
6691    ///
6692    /// Rejects excessive input, unsupported kinds, invalid ranges,
6693    /// non-writable checkouts, semantic conflicts, cancellation, storage
6694    /// failures, or work beyond the caller's exact budget.
6695    pub async fn apply_authored_transaction(
6696        &mut self,
6697        authored: Vec<AuthoredMutation>,
6698        budget: WorkBudget,
6699        cancellation: &CancellationToken,
6700    ) -> FsResult<AuthoredTransactionResult> {
6701        let maximum = usize::try_from(self.volume.config.limits.maximum_mutations_per_batch)
6702            .unwrap_or(usize::MAX);
6703        let operation_count = authored
6704            .iter()
6705            .try_fold(0_usize, |count, mutation| {
6706                count.checked_add(authored_mutation_operation_count(mutation))
6707            })
6708            .ok_or_else(|| OperationFailure::before_work(FsError::TooManyPendingMutations))?;
6709        if authored.len() > maximum || authored.capacity() > maximum || operation_count > maximum {
6710            return Err(OperationFailure::before_work(
6711                FsError::TooManyPendingMutations,
6712            ));
6713        }
6714        let mut work = WorkCounters::default();
6715        let mut operations = Vec::new();
6716        let mut created_file_ids = Vec::new();
6717        // Metadata objects are immutable. Reuse durable admissions throughout
6718        // this authored transaction, including across paths and file kinds.
6719        let mut staged_metadata = AuthoredMetadataCache::new();
6720        operations
6721            .try_reserve_exact(operation_count)
6722            .map_err(|_| OperationFailure::before_work(FsError::PendingMutationAllocationFailed))?;
6723        created_file_ids
6724            .try_reserve_exact(authored.len())
6725            .map_err(|_| OperationFailure::before_work(FsError::PendingMutationAllocationFailed))?;
6726
6727        for authored_mutation in authored {
6728            cancellation
6729                .check()
6730                .map_err(|error| OperationFailure::new(error.into(), work))?;
6731            let created = self
6732                .compile_authored_mutation(
6733                    authored_mutation,
6734                    &mut operations,
6735                    &mut work,
6736                    &mut staged_metadata,
6737                    budget,
6738                    cancellation,
6739                )
6740                .await?;
6741            created_file_ids.push(created);
6742        }
6743        if operations.is_empty() {
6744            return Ok(FsReceipt {
6745                value: AuthoredTransactionResult { created_file_ids },
6746                work,
6747            });
6748        }
6749        let mutation = self
6750            .mutate(operations, remaining(work, budget)?, cancellation)
6751            .await
6752            .map_err(|failure| failure.map_with_prior_work(work, std::convert::identity))?;
6753        work = add(work, mutation.work)?;
6754        Ok(FsReceipt {
6755            value: AuthoredTransactionResult { created_file_ids },
6756            work,
6757        })
6758    }
6759
6760    /// Applies a large ordered capture without exposing an intermediate candidate.
6761    /// Immutable staged objects may remain unreachable after failure, but the
6762    /// caller's checkout is replaced only after every bounded batch succeeds.
6763    pub(crate) async fn apply_authored_bulk_transaction(
6764        &mut self,
6765        authored: Vec<AuthoredMutation>,
6766        budget: WorkBudget,
6767        cancellation: &CancellationToken,
6768    ) -> FsResult<()> {
6769        let maximum = usize::try_from(self.volume.config.limits.maximum_mutations_per_batch)
6770            .unwrap_or(usize::MAX);
6771        if maximum == 0 {
6772            return Err(OperationFailure::before_work(
6773                FsError::TooManyPendingMutations,
6774            ));
6775        }
6776        let mut candidate = self.private_candidate();
6777        let mut work = WorkCounters::default();
6778        let mut batch = Vec::with_capacity(maximum.min(authored.len()));
6779        let mut operations = 0;
6780        for mutation in authored {
6781            let count = authored_mutation_operation_count(&mutation);
6782            if count > maximum {
6783                return Err(OperationFailure::new(
6784                    FsError::TooManyPendingMutations,
6785                    work,
6786                ));
6787            }
6788            if !batch.is_empty() && (batch.len() == maximum || operations + count > maximum) {
6789                let applied = candidate
6790                    .apply_authored_transaction(batch, remaining(work, budget)?, cancellation)
6791                    .await
6792                    .map_err(|failure| failure.map_with_prior_work(work, std::convert::identity))?;
6793                work = add(work, applied.work)?;
6794                batch = Vec::with_capacity(maximum.min(1024));
6795                operations = 0;
6796            }
6797            operations += count;
6798            batch.push(mutation);
6799        }
6800        if !batch.is_empty() {
6801            let applied = candidate
6802                .apply_authored_transaction(batch, remaining(work, budget)?, cancellation)
6803                .await
6804                .map_err(|failure| failure.map_with_prior_work(work, std::convert::identity))?;
6805            work = add(work, applied.work)?;
6806        }
6807        *self = candidate;
6808        Ok(FsReceipt { value: (), work })
6809    }
6810
6811    pub(crate) fn private_candidate(&self) -> Self {
6812        Self {
6813            volume: self.volume.clone(),
6814            base_generation_root: self.base_generation_root,
6815            generation_root: self.generation_root,
6816            base_file_table: self.base_file_table,
6817            base_root: self.base_root.clone(),
6818            root: self.root.clone(),
6819            authority_head: self.authority_head,
6820            authored_operation_id: self.authored_operation_id,
6821            live_operation_id: self.live_operation_id,
6822            last_commit: self.last_commit,
6823            prepared_merge_parent: self.prepared_merge_parent,
6824            dependencies: self.dependencies.clone(),
6825            mode: self.mode,
6826        }
6827    }
6828
6829    #[allow(clippy::too_many_lines)]
6830    async fn compile_authored_mutation(
6831        &self,
6832        authored: AuthoredMutation,
6833        operations: &mut Vec<Mutation>,
6834        work: &mut WorkCounters,
6835        staged_metadata: &mut AuthoredMetadataCache,
6836        budget: WorkBudget,
6837        cancellation: &CancellationToken,
6838    ) -> Result<Option<FileId>, OperationFailure<FsError>> {
6839        match authored {
6840            AuthoredMutation::Reidentify { path, file_id } => {
6841                let current = crate::kernel::lookup_path_async(
6842                    &self.volume.fs.inner.objects,
6843                    &self.root,
6844                    &path,
6845                    self.volume.config,
6846                    remaining(*work, budget)?,
6847                    cancellation,
6848                )
6849                .await
6850                .map_err(|failure| failure.map_with_prior_work(*work, FsError::Path))?;
6851                *work = add(*work, current.work)?;
6852                let current = current
6853                    .record
6854                    .ok_or_else(|| OperationFailure::new(FsError::NotFound, *work))?;
6855                if current.kind == FileKind::Directory {
6856                    return Err(OperationFailure::new(FsError::InvalidSpecialKind, *work));
6857                }
6858                if current.file_id == file_id {
6859                    return Ok(None);
6860                }
6861                let existing = self
6862                    .lookup_file_record_by_id(file_id, remaining(*work, budget)?, cancellation)
6863                    .await
6864                    .map_err(|failure| {
6865                        failure.map_with_prior_work(*work, std::convert::identity)
6866                    })?;
6867                *work = add(*work, existing.work)?;
6868                if existing.value.is_some_and(|existing| {
6869                    existing.kind != current.kind
6870                        || existing.metadata != current.metadata
6871                        || existing.payload != current.payload
6872                }) {
6873                    return Err(OperationFailure::new(FsError::FileIdentityCollision, *work));
6874                }
6875                operations.push(Mutation::Restore {
6876                    path,
6877                    record: FileRecord {
6878                        file_id,
6879                        link_count: 1,
6880                        ..current
6881                    },
6882                });
6883                Ok(None)
6884            }
6885            AuthoredMutation::CreateFile {
6886                path,
6887                bytes,
6888                metadata,
6889            } => {
6890                if u64::try_from(bytes.len()).unwrap_or(u64::MAX)
6891                    > self.volume.config.limits.maximum_read_bytes
6892                {
6893                    return Err(OperationFailure::new(
6894                        FsError::FileRead(FileRangeReadError::InvalidRange),
6895                        *work,
6896                    ));
6897                }
6898                let metadata = self
6899                    .stage_authored_metadata(
6900                        metadata,
6901                        staged_metadata,
6902                        remaining(*work, budget)?,
6903                        cancellation,
6904                    )
6905                    .await
6906                    .map_err(|failure| {
6907                        failure.map_with_prior_work(*work, std::convert::identity)
6908                    })?;
6909                *work = add(*work, metadata.work)?;
6910                let file_id = self.authored_file_id(FileKind::Regular, &path);
6911                if bytes.len() <= crate::kernel::MAXIMUM_INLINE_FILE_BYTES {
6912                    operations.push(Mutation::Create {
6913                        path,
6914                        record: FileRecord {
6915                            file_id,
6916                            kind: FileKind::Regular,
6917                            link_count: 1,
6918                            metadata: metadata.value,
6919                            payload: FilePayload::InlineRegular(
6920                                InlineFileData::new(&bytes)
6921                                    .map_err(|error| OperationFailure::new(error.into(), *work))?,
6922                            ),
6923                        },
6924                    });
6925                } else {
6926                    let blob = self
6927                        .stage_blob(bytes, remaining(*work, budget)?, cancellation)
6928                        .await
6929                        .map_err(|failure| {
6930                            failure.map_with_prior_work(*work, std::convert::identity)
6931                        })?;
6932                    *work = add(*work, blob.work)?;
6933                    operations.push(Mutation::Create {
6934                        path: path.clone(),
6935                        record: FileRecord {
6936                            file_id,
6937                            kind: FileKind::Regular,
6938                            link_count: 1,
6939                            metadata: metadata.value,
6940                            payload: FilePayload::InlineRegular(
6941                                InlineFileData::new(&[])
6942                                    .map_err(|error| OperationFailure::new(error.into(), *work))?,
6943                            ),
6944                        },
6945                    });
6946                    operations.push(Mutation::Write {
6947                        path,
6948                        offset: 0,
6949                        length: blob.value.logical_bytes,
6950                        content: blob.value.root,
6951                        content_offset: 0,
6952                    });
6953                }
6954                Ok(Some(file_id))
6955            }
6956            AuthoredMutation::CreateFileFromContent {
6957                path,
6958                content,
6959                metadata,
6960                file_id,
6961            } => {
6962                let metadata = self
6963                    .stage_authored_metadata(
6964                        metadata,
6965                        staged_metadata,
6966                        remaining(*work, budget)?,
6967                        cancellation,
6968                    )
6969                    .await
6970                    .map_err(|failure| {
6971                        failure.map_with_prior_work(*work, std::convert::identity)
6972                    })?;
6973                *work = add(*work, metadata.work)?;
6974                let file_id =
6975                    file_id.unwrap_or_else(|| self.authored_file_id(FileKind::Regular, &path));
6976                operations.push(Mutation::Create {
6977                    path: path.clone(),
6978                    record: FileRecord {
6979                        file_id,
6980                        kind: FileKind::Regular,
6981                        link_count: 1,
6982                        metadata: metadata.value,
6983                        payload: FilePayload::InlineRegular(
6984                            InlineFileData::new(&[])
6985                                .map_err(|error| OperationFailure::new(error.into(), *work))?,
6986                        ),
6987                    },
6988                });
6989                if content.logical_bytes != 0 {
6990                    operations.push(Mutation::Write {
6991                        path,
6992                        offset: 0,
6993                        length: content.logical_bytes,
6994                        content: content.root,
6995                        content_offset: 0,
6996                    });
6997                }
6998                Ok(Some(file_id))
6999            }
7000            AuthoredMutation::CreateDirectory { path, metadata } => {
7001                let tree = self
7002                    .stage_empty_tree(remaining(*work, budget)?, cancellation)
7003                    .await
7004                    .map_err(|failure| {
7005                        failure.map_with_prior_work(*work, std::convert::identity)
7006                    })?;
7007                *work = add(*work, tree.work)?;
7008                self.compile_record(
7009                    operations,
7010                    path,
7011                    FileKind::Directory,
7012                    FilePayload::Directory {
7013                        entries: tree.value,
7014                    },
7015                    metadata,
7016                    work,
7017                    staged_metadata,
7018                    budget,
7019                    cancellation,
7020                )
7021                .await
7022                .map(Some)
7023            }
7024            AuthoredMutation::CreateSymbolicLink {
7025                path,
7026                target,
7027                metadata,
7028            } => {
7029                let blob = self
7030                    .stage_blob(target, remaining(*work, budget)?, cancellation)
7031                    .await
7032                    .map_err(|failure| {
7033                        failure.map_with_prior_work(*work, std::convert::identity)
7034                    })?;
7035                *work = add(*work, blob.work)?;
7036                self.compile_record(
7037                    operations,
7038                    path,
7039                    FileKind::SymbolicLink,
7040                    FilePayload::SymbolicLink {
7041                        target_bytes: blob.value.logical_bytes,
7042                        target: blob.value.root,
7043                    },
7044                    metadata,
7045                    work,
7046                    staged_metadata,
7047                    budget,
7048                    cancellation,
7049                )
7050                .await
7051                .map(Some)
7052            }
7053            AuthoredMutation::CreateEmptySpecial {
7054                path,
7055                kind,
7056                metadata,
7057            } => {
7058                if !matches!(
7059                    kind,
7060                    FileKind::Fifo | FileKind::Socket | FileKind::MountBoundary
7061                ) {
7062                    return Err(OperationFailure::new(FsError::InvalidSpecialKind, *work));
7063                }
7064                self.validate_profile_kind(kind, *work)?;
7065                self.compile_record(
7066                    operations,
7067                    path,
7068                    kind,
7069                    FilePayload::Empty,
7070                    metadata,
7071                    work,
7072                    staged_metadata,
7073                    budget,
7074                    cancellation,
7075                )
7076                .await
7077                .map(Some)
7078            }
7079            AuthoredMutation::CreateDevice {
7080                path,
7081                kind,
7082                major,
7083                minor,
7084                metadata,
7085            } => {
7086                if !matches!(kind, FileKind::CharacterDevice | FileKind::BlockDevice) {
7087                    return Err(OperationFailure::new(FsError::InvalidSpecialKind, *work));
7088                }
7089                self.validate_profile_kind(kind, *work)?;
7090                self.compile_record(
7091                    operations,
7092                    path,
7093                    kind,
7094                    FilePayload::Device { major, minor },
7095                    metadata,
7096                    work,
7097                    staged_metadata,
7098                    budget,
7099                    cancellation,
7100                )
7101                .await
7102                .map(Some)
7103            }
7104            AuthoredMutation::CreateReparsePoint {
7105                path,
7106                payload,
7107                metadata,
7108            } => {
7109                self.validate_profile_kind(FileKind::ReparsePoint, *work)?;
7110                let blob = self
7111                    .stage_blob(payload, remaining(*work, budget)?, cancellation)
7112                    .await
7113                    .map_err(|failure| {
7114                        failure.map_with_prior_work(*work, std::convert::identity)
7115                    })?;
7116                *work = add(*work, blob.work)?;
7117                self.compile_record(
7118                    operations,
7119                    path,
7120                    FileKind::ReparsePoint,
7121                    FilePayload::ReparsePoint {
7122                        payload_bytes: blob.value.logical_bytes,
7123                        payload: blob.value.root,
7124                    },
7125                    metadata,
7126                    work,
7127                    staged_metadata,
7128                    budget,
7129                    cancellation,
7130                )
7131                .await
7132                .map(Some)
7133            }
7134            AuthoredMutation::Remove {
7135                path,
7136                expected_file_id,
7137            } => {
7138                operations.push(Mutation::Remove {
7139                    path,
7140                    expected_file_id: expected_file_id
7141                        .map_or(MetadataField::Unavailable, MetadataField::Value),
7142                });
7143                Ok(None)
7144            }
7145            AuthoredMutation::Rename {
7146                source,
7147                destination,
7148                replace,
7149            } => {
7150                operations.push(Mutation::Rename {
7151                    source,
7152                    destination,
7153                    replace,
7154                });
7155                Ok(None)
7156            }
7157            AuthoredMutation::HardLink {
7158                source,
7159                destination,
7160            } => {
7161                operations.push(Mutation::Link {
7162                    source,
7163                    destination,
7164                });
7165                Ok(None)
7166            }
7167            AuthoredMutation::Write {
7168                path,
7169                offset,
7170                bytes,
7171            } => {
7172                if bytes.is_empty() {
7173                    operations.push(Mutation::ValidateRegular { path });
7174                } else {
7175                    let blob = self
7176                        .stage_blob(bytes, remaining(*work, budget)?, cancellation)
7177                        .await
7178                        .map_err(|failure| {
7179                            failure.map_with_prior_work(*work, std::convert::identity)
7180                        })?;
7181                    *work = add(*work, blob.work)?;
7182                    operations.push(Mutation::Write {
7183                        path,
7184                        offset,
7185                        length: blob.value.logical_bytes,
7186                        content: blob.value.root,
7187                        content_offset: 0,
7188                    });
7189                }
7190                Ok(None)
7191            }
7192            AuthoredMutation::WriteFromContent {
7193                path,
7194                offset,
7195                content,
7196            } => {
7197                if content.logical_bytes == 0 {
7198                    operations.push(Mutation::ValidateRegular { path });
7199                } else {
7200                    operations.push(Mutation::Write {
7201                        path,
7202                        offset,
7203                        length: content.logical_bytes,
7204                        content: content.root,
7205                        content_offset: 0,
7206                    });
7207                }
7208                Ok(None)
7209            }
7210            AuthoredMutation::SetMetadata { path, metadata } => {
7211                let metadata = self
7212                    .stage_authored_metadata(
7213                        metadata,
7214                        staged_metadata,
7215                        remaining(*work, budget)?,
7216                        cancellation,
7217                    )
7218                    .await
7219                    .map_err(|failure| {
7220                        failure.map_with_prior_work(*work, std::convert::identity)
7221                    })?;
7222                *work = add(*work, metadata.work)?;
7223                operations.push(Mutation::SetMetadata {
7224                    path,
7225                    metadata: metadata.value,
7226                });
7227                Ok(None)
7228            }
7229            AuthoredMutation::Resize {
7230                path,
7231                logical_bytes,
7232            } => {
7233                operations.push(Mutation::Resize {
7234                    path,
7235                    logical_bytes,
7236                });
7237                Ok(None)
7238            }
7239            AuthoredMutation::ZeroRange {
7240                path,
7241                range,
7242                allocated,
7243                extend,
7244            } => {
7245                operations.push(Mutation::ZeroRange {
7246                    path,
7247                    offset: range.offset,
7248                    length: range.length,
7249                    allocated,
7250                    extend,
7251                });
7252                Ok(None)
7253            }
7254            AuthoredMutation::Preallocate {
7255                path,
7256                range,
7257                keep_size,
7258            } => {
7259                operations.push(Mutation::Preallocate {
7260                    path,
7261                    offset: range.offset,
7262                    length: range.length,
7263                    keep_size,
7264                });
7265                Ok(None)
7266            }
7267            AuthoredMutation::CloneRange(request) => {
7268                operations.push(Mutation::CloneRange {
7269                    source: request.source,
7270                    source_offset: request.source_offset,
7271                    destination: request.destination,
7272                    destination_offset: request.destination_offset,
7273                    length: request.length,
7274                });
7275                Ok(None)
7276            }
7277        }
7278    }
7279
7280    fn validate_profile_kind(
7281        &self,
7282        kind: FileKind,
7283        work: WorkCounters,
7284    ) -> Result<(), OperationFailure<FsError>> {
7285        if kind.is_supported_by_profile(self.volume.config.profile) {
7286            Ok(())
7287        } else {
7288            Err(OperationFailure::new(FsError::UnsupportedFileKind, work))
7289        }
7290    }
7291
7292    #[allow(clippy::too_many_arguments)]
7293    async fn compile_record(
7294        &self,
7295        operations: &mut Vec<Mutation>,
7296        path: NamespacePath,
7297        kind: FileKind,
7298        payload: FilePayload,
7299        metadata: FileMetadata,
7300        work: &mut WorkCounters,
7301        staged_metadata: &mut AuthoredMetadataCache,
7302        budget: WorkBudget,
7303        cancellation: &CancellationToken,
7304    ) -> Result<FileId, OperationFailure<FsError>> {
7305        let staged = self
7306            .stage_authored_metadata(
7307                metadata,
7308                staged_metadata,
7309                remaining(*work, budget)?,
7310                cancellation,
7311            )
7312            .await
7313            .map_err(|failure| failure.map_with_prior_work(*work, std::convert::identity))?;
7314        *work = add(*work, staged.work)?;
7315        let file_id = self.authored_file_id(kind, &path);
7316        operations.push(Mutation::Create {
7317            path,
7318            record: FileRecord {
7319                file_id,
7320                kind,
7321                link_count: 1,
7322                metadata: staged.value,
7323                payload,
7324            },
7325        });
7326        Ok(file_id)
7327    }
7328
7329    pub(crate) fn bind_authored_operation(&mut self, operation_id: OperationId) {
7330        self.authored_operation_id = Some(operation_id);
7331    }
7332
7333    fn authored_file_id(&self, kind: FileKind, path: &NamespacePath) -> FileId {
7334        let Some(operation_id) = self.authored_operation_id else {
7335            return FileId::new();
7336        };
7337        let mut hasher = blake3::Hasher::new();
7338        hasher.update(b"acyclic-fs-authored-file-id-v1\0");
7339        hasher.update(&operation_id.into_bytes());
7340        hasher.update(&[kind.tag()]);
7341        for component in path.components() {
7342            hasher.update(&[component.encoding().tag()]);
7343            hasher.update(
7344                &u64::try_from(component.as_bytes().len())
7345                    .unwrap_or(u64::MAX)
7346                    .to_le_bytes(),
7347            );
7348            hasher.update(component.as_bytes());
7349        }
7350        let digest = hasher.finalize();
7351        let mut bytes = [0_u8; 16];
7352        bytes.copy_from_slice(&digest.as_bytes()[..16]);
7353        FileId::from_bytes(bytes)
7354    }
7355
7356    async fn stage_metadata(
7357        &self,
7358        metadata: FileMetadata,
7359        budget: WorkBudget,
7360        cancellation: &CancellationToken,
7361    ) -> FsResult<ObjectId> {
7362        let encoded = encode_file_metadata(metadata)
7363            .map_err(|error| OperationFailure::before_work(error.into()))?;
7364        let (value, work) = self
7365            .volume
7366            .fs
7367            .put_encoded(
7368                ObjectKind::Metadata,
7369                encoded,
7370                WorkCounters::default(),
7371                budget,
7372                cancellation,
7373            )
7374            .await?;
7375        Ok(FsReceipt { value, work })
7376    }
7377
7378    async fn stage_authored_metadata(
7379        &self,
7380        metadata: FileMetadata,
7381        staged: &mut AuthoredMetadataCache,
7382        budget: WorkBudget,
7383        cancellation: &CancellationToken,
7384    ) -> FsResult<ObjectId> {
7385        cancellation
7386            .check()
7387            .map_err(|error| OperationFailure::before_work(error.into()))?;
7388        if let Some(value) = staged.get(metadata) {
7389            return Ok(FsReceipt {
7390                value,
7391                work: WorkCounters::default(),
7392            });
7393        }
7394        let receipt = self.stage_metadata(metadata, budget, cancellation).await?;
7395        staged.remember(metadata, receipt.value);
7396        Ok(receipt)
7397    }
7398
7399    async fn stage_empty_tree(
7400        &self,
7401        budget: WorkBudget,
7402        cancellation: &CancellationToken,
7403    ) -> FsResult<ObjectId> {
7404        let encoded = encode_tree_page(
7405            &TreePage::Leaf(Vec::new()),
7406            self.volume.config.limits.maximum_directory_page_entries,
7407        )
7408        .map_err(|error| OperationFailure::before_work(error.into()))?;
7409        let (value, work) = self
7410            .volume
7411            .fs
7412            .put_encoded(
7413                ObjectKind::TreePage,
7414                encoded,
7415                WorkCounters::default(),
7416                budget,
7417                cancellation,
7418            )
7419            .await?;
7420        Ok(FsReceipt { value, work })
7421    }
7422
7423    async fn stage_empty_attributes(
7424        &self,
7425        budget: WorkBudget,
7426        cancellation: &CancellationToken,
7427    ) -> FsResult<ObjectId> {
7428        let encoded = encode_attribute_page(
7429            &AttributePage::Leaf(Vec::new()),
7430            self.volume.config.limits.maximum_directory_page_entries,
7431        )
7432        .map_err(|error| OperationFailure::before_work(error.into()))?;
7433        let (value, work) = self
7434            .volume
7435            .fs
7436            .put_encoded(
7437                ObjectKind::AttributePage,
7438                encoded,
7439                WorkCounters::default(),
7440                budget,
7441                cancellation,
7442            )
7443            .await?;
7444        Ok(FsReceipt { value, work })
7445    }
7446
7447    /// Rejects a new namespace binding that violates the volume's name
7448    /// policy before any mutation is attempted: a name that is not canonical
7449    /// NFC under `UnicodePolicy::RequireNfc`, or a name that collides with an
7450    /// existing sibling once case-folded under
7451    /// `CaseSensitivity::ProfileFolded`. A no-op under the default
7452    /// `Sensitive`/`Preserve` policy, and for the volume root (which has no
7453    /// siblings to collide with).
7454    async fn admit_new_name(
7455        &mut self,
7456        path: &NamespacePath,
7457        budget: WorkBudget,
7458        cancellation: &CancellationToken,
7459        allowed_file_ids: &[FileId],
7460        parent_pending: bool,
7461    ) -> FsResult<()> {
7462        let mut work = WorkCounters::default();
7463        let Some((parent_components, name)) = path.split_last() else {
7464            return Ok(FsReceipt { value: (), work });
7465        };
7466        if self.volume.config.unicode == UnicodePolicy::RequireNfc
7467            && name.encoding() == NameEncoding::Utf8
7468        {
7469            let is_nfc =
7470                std::str::from_utf8(name.as_bytes()).is_ok_and(unicode_normalization::is_nfc);
7471            if !is_nfc {
7472                return Err(OperationFailure::new(FsError::NonNormalizedName, work));
7473            }
7474        }
7475        if self.volume.config.case_sensitivity == CaseSensitivity::ProfileFolded && !parent_pending
7476        {
7477            let found = self
7478                .find_case_folded_sibling(
7479                    parent_components,
7480                    name,
7481                    remaining(work, budget)?,
7482                    cancellation,
7483                    allowed_file_ids,
7484                )
7485                .await
7486                .map_err(|failure| failure.map_with_prior_work(work, std::convert::identity))?;
7487            work = add(work, found.work)?;
7488            if found.value.is_some() {
7489                return Err(OperationFailure::new(FsError::NameCollision, work));
7490            }
7491        }
7492        Ok(FsReceipt { value: (), work })
7493    }
7494
7495    /// Scans `parent`'s entries for one whose name is not byte-identical to
7496    /// `target` but shares its [`LogicalName::case_fold_key`], returning that
7497    /// sibling's exact canonical name. Used both to reject a colliding create
7498    /// ([`Self::admit_new_name`]) and to resolve a case-insensitive lookup
7499    /// that missed on an exact match.
7500    ///
7501    /// `parent_components` come from an already-admitted path's own prefix,
7502    /// so reconstructing it cannot exceed the same volume's bounds; if it
7503    /// somehow did, this returns `Ok(None)` rather than inventing an
7504    /// unrelated error — callers fall back to their own exact-match result.
7505    async fn find_case_folded_sibling(
7506        &mut self,
7507        parent_components: &[LogicalName],
7508        target: &LogicalName,
7509        budget: WorkBudget,
7510        cancellation: &CancellationToken,
7511        allowed_file_ids: &[FileId],
7512    ) -> FsResult<Option<LogicalName>> {
7513        let mut work = WorkCounters::default();
7514        let Ok(parent) = NamespacePath::new(parent_components.to_vec(), self.volume.config.limits)
7515        else {
7516            return Ok(FsReceipt { value: None, work });
7517        };
7518        let folded_target = target.case_fold_key();
7519        let mut after: Option<LogicalName> = None;
7520        loop {
7521            // Boxed: `list_directory` resolves its own path through
7522            // `lookup_no_follow_observed`, which calls back into this
7523            // function on an exact-match miss, so the future is otherwise
7524            // unboundedly self-referential at compile time.
7525            let page = Box::pin(self.list_directory(
7526                &parent,
7527                after.as_ref(),
7528                CASE_FOLD_COLLISION_SCAN_PAGE,
7529                remaining(work, budget)?,
7530                cancellation,
7531            ))
7532            .await
7533            .map_err(|failure| failure.map_with_prior_work(work, std::convert::identity))?;
7534            work = add(work, page.work)?;
7535            for entry in &page.value.entries {
7536                if entry.name != *target
7537                    && entry.name.case_fold_key() == folded_target
7538                    && !allowed_file_ids.contains(&entry.file_id)
7539                {
7540                    return Ok(FsReceipt {
7541                        value: Some(entry.name.clone()),
7542                        work,
7543                    });
7544                }
7545            }
7546            if !page.value.has_more {
7547                break;
7548            }
7549            after = page.value.entries.last().map(|entry| entry.name.clone());
7550        }
7551        Ok(FsReceipt { value: None, work })
7552    }
7553
7554    /// Creates a regular file, embedding tiny content and sparsely staging
7555    /// larger content in one atomic checkout mutation batch.
7556    ///
7557    /// # Errors
7558    ///
7559    /// Returns measured path, identity, encoding, blob, mutation, storage,
7560    /// cancellation, allocation, or bounded-work failures.
7561    pub async fn create_file(
7562        &mut self,
7563        path: NamespacePath,
7564        bytes: Bytes,
7565        budget: WorkBudget,
7566        cancellation: &CancellationToken,
7567    ) -> FsResult<FileId> {
7568        cancellation
7569            .check()
7570            .map_err(|error| OperationFailure::before_work(error.into()))?;
7571        if u64::try_from(bytes.len()).unwrap_or(u64::MAX)
7572            > self.volume.config.limits.maximum_read_bytes
7573        {
7574            return Err(OperationFailure::before_work(FsError::FileRead(
7575                FileRangeReadError::InvalidRange,
7576            )));
7577        }
7578        let mut work = WorkCounters::default();
7579        let metadata = self
7580            .volume
7581            .fs
7582            .put_encoded(
7583                ObjectKind::Metadata,
7584                encode_file_metadata(empty_metadata())
7585                    .map_err(|error| OperationFailure::new(error.into(), work))?,
7586                work,
7587                budget,
7588                cancellation,
7589            )
7590            .await?;
7591        work = metadata.1;
7592        let file_id = FileId::new();
7593        let mut operations = Vec::new();
7594        if bytes.len() <= crate::kernel::MAXIMUM_INLINE_FILE_BYTES {
7595            operations.push(Mutation::Create {
7596                path,
7597                record: FileRecord {
7598                    file_id,
7599                    kind: FileKind::Regular,
7600                    link_count: 1,
7601                    metadata: metadata.0,
7602                    payload: FilePayload::InlineRegular(
7603                        InlineFileData::new(&bytes)
7604                            .map_err(|error| OperationFailure::new(error.into(), work))?,
7605                    ),
7606                },
7607            });
7608        } else {
7609            let blob = self
7610                .stage_blob(bytes, remaining(work, budget)?, cancellation)
7611                .await
7612                .map_err(|failure| failure.map_with_prior_work(work, std::convert::identity))?;
7613            work = add(work, blob.work)?;
7614            let length = blob.value.logical_bytes;
7615            operations.push(Mutation::Create {
7616                path: path.clone(),
7617                record: FileRecord {
7618                    file_id,
7619                    kind: FileKind::Regular,
7620                    link_count: 1,
7621                    metadata: metadata.0,
7622                    payload: FilePayload::InlineRegular(
7623                        InlineFileData::new(&[])
7624                            .map_err(|error| OperationFailure::new(error.into(), work))?,
7625                    ),
7626                },
7627            });
7628            operations.push(Mutation::Write {
7629                path,
7630                offset: 0,
7631                length,
7632                content: blob.value.root,
7633                content_offset: 0,
7634            });
7635        }
7636        let mutation = self
7637            .mutate(operations, remaining(work, budget)?, cancellation)
7638            .await
7639            .map_err(|failure| failure.map_with_prior_work(work, std::convert::identity))?;
7640        work = add(work, mutation.work)?;
7641        Ok(FsReceipt {
7642            value: file_id,
7643            work,
7644        })
7645    }
7646
7647    /// Creates an empty directory without scanning its parent or siblings.
7648    ///
7649    /// # Errors
7650    ///
7651    /// Returns measured path, encoding, mutation, storage, cancellation,
7652    /// allocation, or bounded-work failures.
7653    pub async fn create_directory(
7654        &mut self,
7655        path: NamespacePath,
7656        budget: WorkBudget,
7657        cancellation: &CancellationToken,
7658    ) -> FsResult<FileId> {
7659        let mut work = WorkCounters::default();
7660        let metadata = self
7661            .volume
7662            .fs
7663            .put_encoded(
7664                ObjectKind::Metadata,
7665                encode_file_metadata(empty_metadata())
7666                    .map_err(|error| OperationFailure::new(error.into(), work))?,
7667                work,
7668                budget,
7669                cancellation,
7670            )
7671            .await?;
7672        work = metadata.1;
7673        let tree = self
7674            .volume
7675            .fs
7676            .put_encoded(
7677                ObjectKind::TreePage,
7678                encode_tree_page(
7679                    &TreePage::Leaf(Vec::new()),
7680                    self.volume.config.limits.maximum_directory_page_entries,
7681                )
7682                .map_err(|error| OperationFailure::new(error.into(), work))?,
7683                work,
7684                budget,
7685                cancellation,
7686            )
7687            .await?;
7688        work = tree.1;
7689        let file_id = FileId::new();
7690        let mutation = self
7691            .mutate(
7692                vec![Mutation::Create {
7693                    path,
7694                    record: FileRecord {
7695                        file_id,
7696                        kind: FileKind::Directory,
7697                        link_count: 1,
7698                        metadata: metadata.0,
7699                        payload: FilePayload::Directory { entries: tree.0 },
7700                    },
7701                }],
7702                remaining(work, budget)?,
7703                cancellation,
7704            )
7705            .await
7706            .map_err(|failure| failure.map_with_prior_work(work, std::convert::identity))?;
7707        work = add(work, mutation.work)?;
7708        Ok(FsReceipt {
7709            value: file_id,
7710            work,
7711        })
7712    }
7713
7714    /// Creates a symbolic link whose target bytes remain opaque and exact.
7715    ///
7716    /// # Errors
7717    ///
7718    /// Returns typed capability, path, blob, metadata, mutation, storage,
7719    /// cancellation, allocation, or bounded-work failures.
7720    pub async fn create_symbolic_link(
7721        &mut self,
7722        path: NamespacePath,
7723        target: Bytes,
7724        budget: WorkBudget,
7725        cancellation: &CancellationToken,
7726    ) -> FsResult<FileId> {
7727        let blob = self.stage_blob(target, budget, cancellation).await?;
7728        let mut work = blob.work;
7729        let created = self
7730            .create_record(
7731                path,
7732                FileKind::SymbolicLink,
7733                FilePayload::SymbolicLink {
7734                    target_bytes: blob.value.logical_bytes,
7735                    target: blob.value.root,
7736                },
7737                remaining(work, budget)?,
7738                cancellation,
7739            )
7740            .await
7741            .map_err(|failure| failure.map_with_prior_work(work, std::convert::identity))?;
7742        work = add(work, created.work)?;
7743        Ok(FsReceipt {
7744            value: created.value,
7745            work,
7746        })
7747    }
7748
7749    /// Reads exact opaque symbolic-link target bytes without following it.
7750    ///
7751    /// # Errors
7752    ///
7753    /// Returns typed absence, kind, corruption, cancellation, storage,
7754    /// allocation, or bounded-work failures.
7755    pub async fn read_symbolic_link(
7756        &mut self,
7757        path: &NamespacePath,
7758        budget: WorkBudget,
7759        cancellation: &CancellationToken,
7760    ) -> FsResult<Bytes> {
7761        let lookup = self.lookup_no_follow(path, budget, cancellation).await?;
7762        let mut work = lookup.work;
7763        let record = lookup
7764            .value
7765            .record
7766            .ok_or_else(|| OperationFailure::new(FsError::NotFound, work))?;
7767        let read =
7768            read_symbolic_link_record(&self.volume, record, remaining(work, budget)?, cancellation)
7769                .await
7770                .map_err(|failure| failure.map_with_prior_work(work, std::convert::identity))?;
7771        work = add(work, read.work)?;
7772        Ok(FsReceipt {
7773            value: read.value,
7774            work,
7775        })
7776    }
7777
7778    /// Creates a FIFO, socket, or explicit mounted-volume boundary.
7779    ///
7780    /// # Errors
7781    ///
7782    /// Rejects payload-bearing kinds and returns typed metadata, mutation,
7783    /// storage, cancellation, or bounded-work failures.
7784    pub async fn create_empty_special(
7785        &mut self,
7786        path: NamespacePath,
7787        kind: FileKind,
7788        budget: WorkBudget,
7789        cancellation: &CancellationToken,
7790    ) -> FsResult<FileId> {
7791        if !matches!(
7792            kind,
7793            FileKind::Fifo | FileKind::Socket | FileKind::MountBoundary
7794        ) {
7795            return Err(OperationFailure::before_work(FsError::InvalidSpecialKind));
7796        }
7797        self.validate_profile_kind(kind, WorkCounters::default())?;
7798        self.create_record(path, kind, FilePayload::Empty, budget, cancellation)
7799            .await
7800    }
7801
7802    /// Creates an exact POSIX character or block device identity.
7803    ///
7804    /// # Errors
7805    ///
7806    /// Rejects non-device kinds and returns typed metadata, mutation, storage,
7807    /// cancellation, or bounded-work failures.
7808    pub async fn create_device(
7809        &mut self,
7810        path: NamespacePath,
7811        kind: FileKind,
7812        major: u32,
7813        minor: u32,
7814        budget: WorkBudget,
7815        cancellation: &CancellationToken,
7816    ) -> FsResult<FileId> {
7817        if !matches!(kind, FileKind::CharacterDevice | FileKind::BlockDevice) {
7818            return Err(OperationFailure::before_work(FsError::InvalidSpecialKind));
7819        }
7820        self.validate_profile_kind(kind, WorkCounters::default())?;
7821        self.create_record(
7822            path,
7823            kind,
7824            FilePayload::Device { major, minor },
7825            budget,
7826            cancellation,
7827        )
7828        .await
7829    }
7830
7831    /// Creates an opaque Windows reparse-point payload.
7832    ///
7833    /// # Errors
7834    ///
7835    /// Returns typed path, blob, metadata, mutation, storage, cancellation,
7836    /// allocation, or bounded-work failures.
7837    pub async fn create_reparse_point(
7838        &mut self,
7839        path: NamespacePath,
7840        payload: Bytes,
7841        budget: WorkBudget,
7842        cancellation: &CancellationToken,
7843    ) -> FsResult<FileId> {
7844        self.validate_profile_kind(FileKind::ReparsePoint, WorkCounters::default())?;
7845        let blob = self.stage_blob(payload, budget, cancellation).await?;
7846        let mut work = blob.work;
7847        let created = self
7848            .create_record(
7849                path,
7850                FileKind::ReparsePoint,
7851                FilePayload::ReparsePoint {
7852                    payload_bytes: blob.value.logical_bytes,
7853                    payload: blob.value.root,
7854                },
7855                remaining(work, budget)?,
7856                cancellation,
7857            )
7858            .await
7859            .map_err(|failure| failure.map_with_prior_work(work, std::convert::identity))?;
7860        work = add(work, created.work)?;
7861        Ok(FsReceipt {
7862            value: created.value,
7863            work,
7864        })
7865    }
7866
7867    /// Reads exact opaque Windows reparse-point bytes without interpreting them.
7868    ///
7869    /// # Errors
7870    ///
7871    /// Returns typed absence, kind, corruption, cancellation, storage,
7872    /// allocation, or bounded-work failures.
7873    pub async fn read_reparse_point(
7874        &mut self,
7875        path: &NamespacePath,
7876        budget: WorkBudget,
7877        cancellation: &CancellationToken,
7878    ) -> FsResult<Bytes> {
7879        let lookup = self.lookup_no_follow(path, budget, cancellation).await?;
7880        let mut work = lookup.work;
7881        let record = lookup
7882            .value
7883            .record
7884            .ok_or_else(|| OperationFailure::new(FsError::NotFound, work))?;
7885        let FilePayload::ReparsePoint {
7886            payload_bytes,
7887            payload,
7888        } = record.payload
7889        else {
7890            return Err(OperationFailure::new(FsError::NotReparsePoint, work));
7891        };
7892        if payload_bytes > self.volume.config.limits.maximum_read_bytes {
7893            return Err(OperationFailure::new(
7894                FsError::FileRead(FileRangeReadError::InvalidRange),
7895                work,
7896            ));
7897        }
7898        if payload_bytes == 0 {
7899            return Ok(FsReceipt {
7900                value: Bytes::new(),
7901                work,
7902            });
7903        }
7904        let read = read_blob_range_async(
7905            &self.volume.fs.inner.objects,
7906            payload,
7907            ByteRange {
7908                offset: 0,
7909                length: payload_bytes,
7910            },
7911            decode_limits(self.volume.config),
7912            remaining(work, budget)?,
7913            cancellation,
7914        )
7915        .await
7916        .map_err(|failure| failure.map_with_prior_work(work, FsError::BlobRead))?;
7917        work = add(work, read.work)?;
7918        Ok(FsReceipt {
7919            value: read.bytes,
7920            work,
7921        })
7922    }
7923
7924    async fn create_record(
7925        &mut self,
7926        path: NamespacePath,
7927        kind: FileKind,
7928        payload: FilePayload,
7929        budget: WorkBudget,
7930        cancellation: &CancellationToken,
7931    ) -> FsResult<FileId> {
7932        let mut work = WorkCounters::default();
7933        let metadata = self
7934            .volume
7935            .fs
7936            .put_encoded(
7937                ObjectKind::Metadata,
7938                encode_file_metadata(empty_metadata())
7939                    .map_err(|error| OperationFailure::new(error.into(), work))?,
7940                work,
7941                budget,
7942                cancellation,
7943            )
7944            .await?;
7945        work = metadata.1;
7946        let file_id = FileId::new();
7947        let mutation = self
7948            .mutate(
7949                vec![Mutation::Create {
7950                    path,
7951                    record: FileRecord {
7952                        file_id,
7953                        kind,
7954                        link_count: 1,
7955                        metadata: metadata.0,
7956                        payload,
7957                    },
7958                }],
7959                remaining(work, budget)?,
7960                cancellation,
7961            )
7962            .await
7963            .map_err(|failure| failure.map_with_prior_work(work, std::convert::identity))?;
7964        work = add(work, mutation.work)?;
7965        Ok(FsReceipt {
7966            value: file_id,
7967            work,
7968        })
7969    }
7970
7971    /// Replaces one file range with caller bytes, preserving sparse untouched ranges.
7972    ///
7973    /// # Errors
7974    ///
7975    /// Returns measured path, blob, mutation, storage, cancellation,
7976    /// allocation, or bounded-work failures.
7977    pub async fn write_file(
7978        &mut self,
7979        path: NamespacePath,
7980        offset: u64,
7981        bytes: Bytes,
7982        budget: WorkBudget,
7983        cancellation: &CancellationToken,
7984    ) -> FsResult<()> {
7985        if bytes.is_empty() {
7986            return self
7987                .mutate(
7988                    vec![Mutation::ValidateRegular { path }],
7989                    budget,
7990                    cancellation,
7991                )
7992                .await;
7993        }
7994        if u64::try_from(bytes.len()).unwrap_or(u64::MAX)
7995            > self.volume.config.limits.maximum_read_bytes
7996        {
7997            return Err(OperationFailure::before_work(FsError::FileRead(
7998                FileRangeReadError::InvalidRange,
7999            )));
8000        }
8001        let blob = self.stage_blob(bytes, budget, cancellation).await?;
8002        let mut work = blob.work;
8003        let mutation = self
8004            .mutate(
8005                vec![Mutation::Write {
8006                    path,
8007                    offset,
8008                    length: blob.value.logical_bytes,
8009                    content: blob.value.root,
8010                    content_offset: 0,
8011                }],
8012                remaining(work, budget)?,
8013                cancellation,
8014            )
8015            .await
8016            .map_err(|failure| failure.map_with_prior_work(work, std::convert::identity))?;
8017        work = add(work, mutation.work)?;
8018        Ok(FsReceipt { value: (), work })
8019    }
8020
8021    /// Captures one regular file as an ephemeral path-independent open view.
8022    ///
8023    /// The returned file shares authenticated immutable objects with this
8024    /// volume. Its later mutations are visible only through copies of this
8025    /// detached value and are never published into a generation.
8026    ///
8027    /// # Errors
8028    ///
8029    /// Returns typed absence, non-regular kind, path, storage, cancellation,
8030    /// dependency, or bounded-work failures.
8031    pub async fn detach_regular_file(
8032        &mut self,
8033        path: &NamespacePath,
8034        budget: WorkBudget,
8035        cancellation: &CancellationToken,
8036    ) -> FsResult<DetachedFile<A, O>> {
8037        let lookup = self
8038            .lookup_no_follow_observed(path, budget, cancellation, true)
8039            .await?;
8040        let record = lookup
8041            .value
8042            .record
8043            .ok_or_else(|| OperationFailure::new(FsError::NotFound, lookup.work))?;
8044        if record.kind != FileKind::Regular {
8045            return Err(OperationFailure::new(
8046                FsError::FileRead(FileRangeReadError::NotRegular),
8047                lookup.work,
8048            ));
8049        }
8050        Ok(FsReceipt {
8051            value: DetachedFile {
8052                volume: self.volume.clone(),
8053                record,
8054            },
8055            work: lookup.work,
8056        })
8057    }
8058
8059    #[cfg(feature = "native-mount")]
8060    pub(crate) fn detached_from_record(&self, record: FileRecord) -> DetachedFile<A, O> {
8061        DetachedFile::from_record(self.volume.clone(), record)
8062    }
8063
8064    /// Reads one authenticated candidate file record by stable identity.
8065    ///
8066    /// Tracking modes retain a complete file-record dependency. More precise
8067    /// byte and metadata APIs below retain only their semantic regions.
8068    ///
8069    /// # Errors
8070    ///
8071    /// Returns typed absence, authentication, storage, cancellation,
8072    /// dependency, allocation, or bounded-work failures.
8073    pub async fn read_file_record_by_id(
8074        &mut self,
8075        file_id: FileId,
8076        budget: WorkBudget,
8077        cancellation: &CancellationToken,
8078    ) -> FsResult<FileRecord> {
8079        let lookup = self
8080            .lookup_file_record_by_id(file_id, budget, cancellation)
8081            .await?;
8082        let mut work = lookup.work;
8083        let record = lookup
8084            .value
8085            .ok_or_else(|| OperationFailure::new(FsError::NotFound, work))?;
8086        if self.mode.consistency != ConsistencyMode::Pinned {
8087            work = self
8088                .observe_identity_region(
8089                    DependencyRegion::FileRecord(file_id),
8090                    work,
8091                    budget,
8092                    cancellation,
8093                )
8094                .await?;
8095        }
8096        Ok(FsReceipt {
8097            value: record,
8098            work,
8099        })
8100    }
8101
8102    /// Reads one exact logical range by stable file identity.
8103    ///
8104    /// # Errors
8105    ///
8106    /// Returns measured absence, non-regular kind, range, authentication,
8107    /// dependency, cancellation, storage, allocation, or bounded-work failures.
8108    pub async fn read_file_range_by_id(
8109        &mut self,
8110        file_id: FileId,
8111        range: ByteRange,
8112        budget: WorkBudget,
8113        cancellation: &CancellationToken,
8114    ) -> FsResult<FileRangeRead> {
8115        self.read_file_by_id_range(file_id, range, false, budget, cancellation)
8116            .await
8117    }
8118
8119    /// Reads at most `maximum_bytes`, clipping to the current logical EOF in
8120    /// the same checkout view used to read content.
8121    ///
8122    /// # Errors
8123    ///
8124    /// Returns the same typed failures as [`Self::read_file_range_by_id`].
8125    pub async fn read_file_up_to_by_id(
8126        &mut self,
8127        file_id: FileId,
8128        offset: u64,
8129        maximum_bytes: u32,
8130        budget: WorkBudget,
8131        cancellation: &CancellationToken,
8132    ) -> FsResult<FileRangeRead> {
8133        self.read_file_by_id_range(
8134            file_id,
8135            ByteRange {
8136                offset,
8137                length: u64::from(maximum_bytes),
8138            },
8139            true,
8140            budget,
8141            cancellation,
8142        )
8143        .await
8144    }
8145
8146    async fn read_file_by_id_range(
8147        &mut self,
8148        file_id: FileId,
8149        mut range: ByteRange,
8150        clip_to_eof: bool,
8151        budget: WorkBudget,
8152        cancellation: &CancellationToken,
8153    ) -> FsResult<FileRangeRead> {
8154        let requested_range = range;
8155        if range.length > self.volume.config.limits.maximum_read_bytes {
8156            return Err(OperationFailure::before_work(FsError::FileRead(
8157                FileRangeReadError::InvalidRange,
8158            )));
8159        }
8160        let lookup = self
8161            .lookup_file_record_by_id(file_id, budget, cancellation)
8162            .await?;
8163        let mut work = lookup.work;
8164        let record = lookup
8165            .value
8166            .ok_or_else(|| OperationFailure::new(FsError::NotFound, work))?;
8167        if clip_to_eof {
8168            if record.kind != FileKind::Regular {
8169                return Err(OperationFailure::new(
8170                    FsError::FileRead(FileRangeReadError::NotRegular),
8171                    work,
8172                ));
8173            }
8174            let logical_bytes = match record.payload {
8175                FilePayload::InlineRegular(data) => {
8176                    u64::try_from(data.as_bytes().len()).unwrap_or(u64::MAX)
8177                }
8178                FilePayload::Regular { logical_bytes, .. } => logical_bytes,
8179                _ => {
8180                    return Err(OperationFailure::new(
8181                        FsError::FileRead(FileRangeReadError::NotRegular),
8182                        work,
8183                    ));
8184                }
8185            };
8186            range.length = range.length.min(logical_bytes.saturating_sub(range.offset));
8187            if range.length == 0 {
8188                if self.mode.consistency != ConsistencyMode::Pinned && requested_range.length != 0 {
8189                    work = self
8190                        .observe_base_regular_range(
8191                            file_id,
8192                            requested_range,
8193                            work,
8194                            budget,
8195                            cancellation,
8196                        )
8197                        .await?;
8198                }
8199                return Ok(FsReceipt {
8200                    value: FileRangeRead {
8201                        bytes: Bytes::new(),
8202                        work,
8203                    },
8204                    work,
8205                });
8206            }
8207        }
8208        let mut read = read_file_range_async(
8209            &self.volume.fs.inner.objects,
8210            FileRangeRequest {
8211                record,
8212                range,
8213                maximum_spans: self.volume.config.limits.maximum_directory_page_entries,
8214                limits: decode_limits(self.volume.config),
8215                budget: remaining(work, budget)?,
8216            },
8217            cancellation,
8218        )
8219        .await
8220        .map_err(|failure| failure.map_with_prior_work(work, FsError::FileRead))?;
8221        work = add(work, read.work)?;
8222        if self.mode.consistency != ConsistencyMode::Pinned && requested_range.length != 0 {
8223            work = self
8224                .observe_base_regular_range(file_id, requested_range, work, budget, cancellation)
8225                .await?;
8226        }
8227        read.work = work;
8228        Ok(FsReceipt { value: read, work })
8229    }
8230
8231    /// Plans one bounded sparse range by stable file identity without reading
8232    /// content blobs or resolving namespace bindings.
8233    ///
8234    /// `None` identifies inline regular content. Tracking modes retain the
8235    /// exact byte-range dependency represented by the returned plan.
8236    ///
8237    /// # Errors
8238    ///
8239    /// Returns measured absence, non-regular kind, invalid bounds,
8240    /// authentication, dependency, cancellation, allocation, storage, or
8241    /// bounded-work failures.
8242    pub async fn plan_file_extents_by_id(
8243        &mut self,
8244        file_id: FileId,
8245        range: ByteRange,
8246        maximum_spans: u32,
8247        budget: WorkBudget,
8248        cancellation: &CancellationToken,
8249    ) -> FsResult<Option<ExtentPlan>> {
8250        if maximum_spans == 0 || range.length > self.volume.config.limits.maximum_generation_bytes {
8251            return Err(OperationFailure::before_work(FsError::FileRead(
8252                FileRangeReadError::InvalidRange,
8253            )));
8254        }
8255        let lookup = self
8256            .lookup_file_record_by_id(file_id, budget, cancellation)
8257            .await?;
8258        let mut work = lookup.work;
8259        let record = lookup
8260            .value
8261            .ok_or_else(|| OperationFailure::new(FsError::NotFound, work))?;
8262        let (logical_bytes, extents) = match record.payload {
8263            FilePayload::InlineRegular(data) => {
8264                let logical_bytes = u64::try_from(data.as_bytes().len()).unwrap_or(u64::MAX);
8265                validate_planned_file_range(logical_bytes, range, work)?;
8266                if self.mode.consistency != ConsistencyMode::Pinned && range.length != 0 {
8267                    work = self
8268                        .observe_base_regular_range(file_id, range, work, budget, cancellation)
8269                        .await?;
8270                }
8271                return Ok(FsReceipt { value: None, work });
8272            }
8273            FilePayload::Regular {
8274                logical_bytes,
8275                extents,
8276            } => (logical_bytes, extents),
8277            FilePayload::Directory { .. }
8278            | FilePayload::SymbolicLink { .. }
8279            | FilePayload::Device { .. }
8280            | FilePayload::Empty
8281            | FilePayload::ReparsePoint { .. } => {
8282                return Err(OperationFailure::new(
8283                    FsError::FileRead(FileRangeReadError::NotRegular),
8284                    work,
8285                ));
8286            }
8287        };
8288        let mut plan = plan_extent_range_async(
8289            &self.volume.fs.inner.objects,
8290            ExtentRangeRequest {
8291                root: extents,
8292                file_size: logical_bytes,
8293                range,
8294                maximum_spans,
8295                limits: decode_limits(self.volume.config),
8296                budget: remaining(work, budget)?,
8297            },
8298            cancellation,
8299        )
8300        .await
8301        .map_err(|failure| {
8302            failure.map_with_prior_work(work, |error| {
8303                FsError::FileRead(FileRangeReadError::Extent(error))
8304            })
8305        })?;
8306        work = add(work, plan.work)?;
8307        if self.mode.consistency != ConsistencyMode::Pinned && range.length != 0 {
8308            work = self
8309                .observe_base_regular_range(file_id, range, work, budget, cancellation)
8310                .await?;
8311        }
8312        plan.work = work;
8313        Ok(FsReceipt {
8314            value: Some(plan),
8315            work,
8316        })
8317    }
8318
8319    /// Finds the first sparse data or hole boundary by stable file identity.
8320    ///
8321    /// # Errors
8322    ///
8323    /// Returns measured absence, non-regular kind, malformed extent,
8324    /// dependency, cancellation, storage, allocation, or bounded-work failures.
8325    pub async fn seek_file_extent_by_id(
8326        &mut self,
8327        file_id: FileId,
8328        offset: u64,
8329        target: ExtentSeekTarget,
8330        budget: WorkBudget,
8331        cancellation: &CancellationToken,
8332    ) -> FsResult<Option<u64>> {
8333        let lookup = self
8334            .lookup_file_record_by_id(file_id, budget, cancellation)
8335            .await?;
8336        let mut work = lookup.work;
8337        let record = lookup
8338            .value
8339            .ok_or_else(|| OperationFailure::new(FsError::NotFound, work))?;
8340        let value = match record.payload {
8341            FilePayload::InlineRegular(data) => {
8342                let logical_bytes = u64::try_from(data.as_bytes().len()).unwrap_or(u64::MAX);
8343                if offset > logical_bytes {
8344                    None
8345                } else {
8346                    match target {
8347                        ExtentSeekTarget::Data => (offset < logical_bytes).then_some(offset),
8348                        ExtentSeekTarget::Hole => Some(logical_bytes),
8349                    }
8350                }
8351            }
8352            FilePayload::Regular {
8353                logical_bytes,
8354                extents,
8355            } => {
8356                if offset > logical_bytes {
8357                    None
8358                } else {
8359                    let receipt = seek_extent_async(
8360                        &self.volume.fs.inner.objects,
8361                        ExtentSeekRequest {
8362                            root: extents,
8363                            file_size: logical_bytes,
8364                            offset,
8365                            target,
8366                            limits: decode_limits(self.volume.config),
8367                            budget: remaining(work, budget)?,
8368                        },
8369                        cancellation,
8370                    )
8371                    .await
8372                    .map_err(|failure| {
8373                        failure.map_with_prior_work(work, |error| {
8374                            FsError::FileRead(FileRangeReadError::Extent(error))
8375                        })
8376                    })?;
8377                    work = add(work, receipt.work)?;
8378                    receipt.value
8379                }
8380            }
8381            FilePayload::Directory { .. }
8382            | FilePayload::SymbolicLink { .. }
8383            | FilePayload::Empty
8384            | FilePayload::Device { .. }
8385            | FilePayload::ReparsePoint { .. } => {
8386                return Err(OperationFailure::new(
8387                    FsError::FileRead(FileRangeReadError::NotRegular),
8388                    work,
8389                ));
8390            }
8391        };
8392        if self.mode.consistency != ConsistencyMode::Pinned {
8393            work = self
8394                .observe_identity_region(
8395                    DependencyRegion::SparseSeek {
8396                        file_id,
8397                        offset,
8398                        target,
8399                    },
8400                    work,
8401                    budget,
8402                    cancellation,
8403                )
8404                .await?;
8405        }
8406        Ok(FsReceipt { value, work })
8407    }
8408
8409    /// Reads complete canonical metadata by stable file identity.
8410    ///
8411    /// # Errors
8412    ///
8413    /// Returns measured absence, authentication, decode, dependency, storage,
8414    /// cancellation, allocation, or bounded-work failures.
8415    pub async fn read_metadata_by_id(
8416        &mut self,
8417        file_id: FileId,
8418        budget: WorkBudget,
8419        cancellation: &CancellationToken,
8420    ) -> FsResult<FileMetadata> {
8421        let lookup = self
8422            .lookup_file_record_by_id(file_id, budget, cancellation)
8423            .await?;
8424        let mut work = lookup.work;
8425        let record = lookup
8426            .value
8427            .ok_or_else(|| OperationFailure::new(FsError::NotFound, work))?;
8428        let metadata = self
8429            .volume
8430            .fs
8431            .inner
8432            .objects
8433            .read(
8434                record.metadata,
8435                self.volume.config.limits.maximum_object_bytes,
8436                remaining(work, budget)?,
8437                cancellation,
8438            )
8439            .await
8440            .map_err(|failure| failure.map_with_prior_work(work, FsError::Object))?;
8441        work = add(work, metadata.work)?;
8442        let value = decode_file_metadata(&metadata.value, decode_limits(self.volume.config))
8443            .map_err(|error| OperationFailure::new(error.into(), work))?;
8444        if self.mode.consistency != ConsistencyMode::Pinned {
8445            work = self
8446                .observe_base_metadata_ids(
8447                    std::slice::from_ref(&file_id),
8448                    work,
8449                    budget,
8450                    cancellation,
8451                )
8452                .await?;
8453        }
8454        Ok(FsReceipt { value, work })
8455    }
8456
8457    /// Reads one exact named attribute by stable file identity.
8458    ///
8459    /// # Errors
8460    ///
8461    /// Returns measured metadata, attribute-tree, blob, storage,
8462    /// authentication, cancellation, allocation, or bounded-work failures.
8463    pub async fn read_named_attribute_by_id(
8464        &mut self,
8465        file_id: FileId,
8466        name: &AttributeName,
8467        budget: WorkBudget,
8468        cancellation: &CancellationToken,
8469    ) -> FsResult<Option<Bytes>> {
8470        let metadata = self
8471            .read_metadata_by_id(file_id, budget, cancellation)
8472            .await?;
8473        let mut work = metadata.work;
8474        let MetadataField::Value(root) = metadata.value.named_attributes else {
8475            return Ok(FsReceipt { value: None, work });
8476        };
8477        let lookup = lookup_attribute_async(
8478            &self.volume.fs.inner.objects,
8479            root,
8480            name,
8481            decode_limits(self.volume.config),
8482            remaining(work, budget)?,
8483            cancellation,
8484        )
8485        .await
8486        .map_err(|failure| failure.map_with_prior_work(work, FsError::AttributeLookup))?;
8487        work = add(work, lookup.work)?;
8488        let Some(entry) = lookup.entry else {
8489            return Ok(FsReceipt { value: None, work });
8490        };
8491        if entry.value_bytes > self.volume.config.limits.maximum_read_bytes {
8492            return Err(OperationFailure::new(
8493                FsError::FileRead(FileRangeReadError::InvalidRange),
8494                work,
8495            ));
8496        }
8497        let read = read_blob_range_async(
8498            &self.volume.fs.inner.objects,
8499            entry.value,
8500            ByteRange {
8501                offset: 0,
8502                length: entry.value_bytes,
8503            },
8504            decode_limits(self.volume.config),
8505            remaining(work, budget)?,
8506            cancellation,
8507        )
8508        .await
8509        .map_err(|failure| failure.map_with_prior_work(work, FsError::BlobRead))?;
8510        work = add(work, read.work)?;
8511        Ok(FsReceipt {
8512            value: Some(read.bytes),
8513            work,
8514        })
8515    }
8516
8517    /// Lists one bounded page of named attributes by stable file identity.
8518    ///
8519    /// # Errors
8520    ///
8521    /// Returns measured metadata, pagination, storage, authentication,
8522    /// cancellation, allocation, or bounded-work failures.
8523    pub async fn list_named_attributes_by_id(
8524        &mut self,
8525        file_id: FileId,
8526        after: Option<&AttributeName>,
8527        maximum_entries: u32,
8528        budget: WorkBudget,
8529        cancellation: &CancellationToken,
8530    ) -> FsResult<AttributeListing> {
8531        let metadata = self
8532            .read_metadata_by_id(file_id, budget, cancellation)
8533            .await?;
8534        let mut work = metadata.work;
8535        let MetadataField::Value(root) = metadata.value.named_attributes else {
8536            let value = AttributeListing {
8537                entries: Vec::new(),
8538                has_more: false,
8539                next_residency: None,
8540                work,
8541            };
8542            return Ok(FsReceipt { value, work });
8543        };
8544        let mut listing = list_attributes_async(
8545            &self.volume.fs.inner.objects,
8546            root,
8547            after,
8548            maximum_entries,
8549            decode_limits(self.volume.config),
8550            remaining(work, budget)?,
8551            cancellation,
8552        )
8553        .await
8554        .map_err(|failure| failure.map_with_prior_work(work, FsError::AttributeList))?;
8555        work = add(work, listing.work)?;
8556        listing.work = work;
8557        Ok(FsReceipt {
8558            value: listing,
8559            work,
8560        })
8561    }
8562
8563    /// Inserts or replaces one named attribute by stable file identity.
8564    ///
8565    /// # Errors
8566    ///
8567    /// Returns measured precondition, metadata, blob, attribute-tree, storage,
8568    /// mutation, cancellation, allocation, or bounded-work failures.
8569    pub async fn write_named_attribute_by_id(
8570        &mut self,
8571        file_id: FileId,
8572        name: AttributeName,
8573        value: Bytes,
8574        mode: NamedAttributeWriteMode,
8575        budget: WorkBudget,
8576        cancellation: &CancellationToken,
8577    ) -> FsResult<()> {
8578        let metadata = self
8579            .read_metadata_by_id(file_id, budget, cancellation)
8580            .await?;
8581        let mut work = metadata.work;
8582        let mut metadata_value = metadata.value;
8583        let root = match metadata_value.named_attributes {
8584            MetadataField::Unavailable => {
8585                let empty = self
8586                    .stage_empty_attributes(remaining(work, budget)?, cancellation)
8587                    .await
8588                    .map_err(|failure| failure.map_with_prior_work(work, std::convert::identity))?;
8589                work = add(work, empty.work)?;
8590                empty.value
8591            }
8592            MetadataField::Value(root) => root,
8593        };
8594        let current = lookup_attribute_async(
8595            &self.volume.fs.inner.objects,
8596            root,
8597            &name,
8598            decode_limits(self.volume.config),
8599            remaining(work, budget)?,
8600            cancellation,
8601        )
8602        .await
8603        .map_err(|failure| failure.map_with_prior_work(work, FsError::AttributeLookup))?;
8604        work = add(work, current.work)?;
8605        match (mode, current.entry.as_ref()) {
8606            (NamedAttributeWriteMode::Create, Some(_))
8607            | (NamedAttributeWriteMode::Replace, None) => {
8608                return Err(OperationFailure::new(FsError::CreationRejected, work));
8609            }
8610            _ => {}
8611        }
8612        let blob = self
8613            .stage_blob(value, remaining(work, budget)?, cancellation)
8614            .await
8615            .map_err(|failure| failure.map_with_prior_work(work, std::convert::identity))?;
8616        work = add(work, blob.work)?;
8617        let replacement = AttributeEntry {
8618            name,
8619            value_bytes: blob.value.logical_bytes,
8620            value: blob.value.root,
8621        };
8622        let mutation = match current.entry {
8623            None => AttributeMutation::Insert(replacement),
8624            Some(expected) => AttributeMutation::Replace {
8625                expected,
8626                replacement,
8627            },
8628        };
8629        let changed = apply_attribute_mutations_async(
8630            &self.volume.fs.inner.objects,
8631            root,
8632            vec![mutation],
8633            1,
8634            decode_limits(self.volume.config),
8635            remaining(work, budget)?,
8636            cancellation,
8637        )
8638        .await
8639        .map_err(|failure| failure.map_with_prior_work(work, FsError::AttributeMutation))?;
8640        work = add(work, changed.work)?;
8641        metadata_value.named_attributes = MetadataField::Value(changed.root);
8642        let changed_metadata = self
8643            .set_metadata_by_id(
8644                file_id,
8645                metadata_value,
8646                remaining(work, budget)?,
8647                cancellation,
8648            )
8649            .await
8650            .map_err(|failure| failure.map_with_prior_work(work, std::convert::identity))?;
8651        work = add(work, changed_metadata.work)?;
8652        Ok(FsReceipt { value: (), work })
8653    }
8654
8655    /// Removes one named attribute by stable file identity.
8656    ///
8657    /// # Errors
8658    ///
8659    /// Returns authenticated absence or the same measured failures as
8660    /// [`Self::write_named_attribute_by_id`].
8661    pub async fn remove_named_attribute_by_id(
8662        &mut self,
8663        file_id: FileId,
8664        name: AttributeName,
8665        budget: WorkBudget,
8666        cancellation: &CancellationToken,
8667    ) -> FsResult<()> {
8668        let metadata = self
8669            .read_metadata_by_id(file_id, budget, cancellation)
8670            .await?;
8671        let mut work = metadata.work;
8672        let mut metadata_value = metadata.value;
8673        let MetadataField::Value(root) = metadata_value.named_attributes else {
8674            return Err(OperationFailure::new(FsError::NotFound, work));
8675        };
8676        let current = lookup_attribute_async(
8677            &self.volume.fs.inner.objects,
8678            root,
8679            &name,
8680            decode_limits(self.volume.config),
8681            remaining(work, budget)?,
8682            cancellation,
8683        )
8684        .await
8685        .map_err(|failure| failure.map_with_prior_work(work, FsError::AttributeLookup))?;
8686        work = add(work, current.work)?;
8687        let expected = current
8688            .entry
8689            .ok_or_else(|| OperationFailure::new(FsError::NotFound, work))?;
8690        let changed = apply_attribute_mutations_async(
8691            &self.volume.fs.inner.objects,
8692            root,
8693            vec![AttributeMutation::Remove {
8694                name,
8695                expected: Some(expected),
8696            }],
8697            1,
8698            decode_limits(self.volume.config),
8699            remaining(work, budget)?,
8700            cancellation,
8701        )
8702        .await
8703        .map_err(|failure| failure.map_with_prior_work(work, FsError::AttributeMutation))?;
8704        work = add(work, changed.work)?;
8705        metadata_value.named_attributes = MetadataField::Value(changed.root);
8706        let changed_metadata = self
8707            .set_metadata_by_id(
8708                file_id,
8709                metadata_value,
8710                remaining(work, budget)?,
8711                cancellation,
8712            )
8713            .await
8714            .map_err(|failure| failure.map_with_prior_work(work, std::convert::identity))?;
8715        work = add(work, changed_metadata.work)?;
8716        Ok(FsReceipt { value: (), work })
8717    }
8718
8719    /// Replaces one range of an attached regular file by stable identity.
8720    ///
8721    /// # Errors
8722    ///
8723    /// Returns measured blob, mutation, storage, cancellation, allocation, or
8724    /// bounded-work failures. Authenticated absence never scans the namespace.
8725    pub async fn write_file_by_id(
8726        &mut self,
8727        file_id: FileId,
8728        offset: u64,
8729        bytes: Bytes,
8730        budget: WorkBudget,
8731        cancellation: &CancellationToken,
8732    ) -> FsResult<()> {
8733        if bytes.is_empty() {
8734            return self
8735                .mutate(
8736                    vec![Mutation::File {
8737                        file_id,
8738                        mutation: FileMutation::ValidateRegular,
8739                    }],
8740                    budget,
8741                    cancellation,
8742                )
8743                .await;
8744        }
8745        if u64::try_from(bytes.len()).unwrap_or(u64::MAX)
8746            > self.volume.config.limits.maximum_read_bytes
8747        {
8748            return Err(OperationFailure::before_work(FsError::FileRead(
8749                FileRangeReadError::InvalidRange,
8750            )));
8751        }
8752        let blob = self.stage_blob(bytes, budget, cancellation).await?;
8753        let mut work = blob.work;
8754        let mutation = self
8755            .mutate(
8756                vec![Mutation::File {
8757                    file_id,
8758                    mutation: FileMutation::Write {
8759                        offset,
8760                        length: blob.value.logical_bytes,
8761                        content: blob.value.root,
8762                        content_offset: 0,
8763                    },
8764                }],
8765                remaining(work, budget)?,
8766                cancellation,
8767            )
8768            .await
8769            .map_err(|failure| failure.map_with_prior_work(work, std::convert::identity))?;
8770        work = add(work, mutation.work)?;
8771        Ok(FsReceipt { value: (), work })
8772    }
8773
8774    /// Replaces complete canonical metadata by stable identity.
8775    ///
8776    /// # Errors
8777    ///
8778    /// Returns measured encoding, storage, mutation, cancellation, allocation,
8779    /// or bounded-work failures.
8780    pub async fn set_metadata_by_id(
8781        &mut self,
8782        file_id: FileId,
8783        metadata: FileMetadata,
8784        budget: WorkBudget,
8785        cancellation: &CancellationToken,
8786    ) -> FsResult<()> {
8787        let encoded = encode_file_metadata(metadata)
8788            .map_err(|error| OperationFailure::before_work(error.into()))?;
8789        let (metadata, mut work) = self
8790            .volume
8791            .fs
8792            .put_encoded(
8793                ObjectKind::Metadata,
8794                encoded,
8795                WorkCounters::default(),
8796                budget,
8797                cancellation,
8798            )
8799            .await?;
8800        let mutation = self
8801            .mutate(
8802                vec![Mutation::File {
8803                    file_id,
8804                    mutation: FileMutation::SetMetadata { metadata },
8805                }],
8806                remaining(work, budget)?,
8807                cancellation,
8808            )
8809            .await
8810            .map_err(|failure| failure.map_with_prior_work(work, std::convert::identity))?;
8811        work = add(work, mutation.work)?;
8812        Ok(FsReceipt { value: (), work })
8813    }
8814
8815    /// Replaces complete metadata and optionally logical size as one attached
8816    /// file-table transaction.
8817    ///
8818    /// # Errors
8819    ///
8820    /// Returns measured encoding, storage, sparse mutation, cancellation,
8821    /// allocation, or bounded-work failures. The candidate record changes only
8822    /// if the complete ordered batch succeeds.
8823    pub async fn set_attributes_by_id(
8824        &mut self,
8825        file_id: FileId,
8826        metadata: FileMetadata,
8827        logical_bytes: Option<u64>,
8828        budget: WorkBudget,
8829        cancellation: &CancellationToken,
8830    ) -> FsResult<()> {
8831        let encoded = encode_file_metadata(metadata)
8832            .map_err(|error| OperationFailure::before_work(error.into()))?;
8833        let (metadata, mut work) = self
8834            .volume
8835            .fs
8836            .put_encoded(
8837                ObjectKind::Metadata,
8838                encoded,
8839                WorkCounters::default(),
8840                budget,
8841                cancellation,
8842            )
8843            .await?;
8844        let mut operations = vec![Mutation::File {
8845            file_id,
8846            mutation: FileMutation::SetMetadata { metadata },
8847        }];
8848        if let Some(logical_bytes) = logical_bytes {
8849            operations.push(Mutation::File {
8850                file_id,
8851                mutation: FileMutation::Resize { logical_bytes },
8852            });
8853        }
8854        let mutation = self
8855            .mutate(operations, remaining(work, budget)?, cancellation)
8856            .await
8857            .map_err(|failure| failure.map_with_prior_work(work, std::convert::identity))?;
8858        work = add(work, mutation.work)?;
8859        Ok(FsReceipt { value: (), work })
8860    }
8861
8862    /// Changes logical length by stable identity.
8863    ///
8864    /// # Errors
8865    ///
8866    /// Returns the same measured failures as [`Self::mutate`].
8867    pub async fn resize_file_by_id(
8868        &mut self,
8869        file_id: FileId,
8870        logical_bytes: u64,
8871        budget: WorkBudget,
8872        cancellation: &CancellationToken,
8873    ) -> FsResult<()> {
8874        self.mutate(
8875            vec![Mutation::File {
8876                file_id,
8877                mutation: FileMutation::Resize { logical_bytes },
8878            }],
8879            budget,
8880            cancellation,
8881        )
8882        .await
8883    }
8884
8885    /// Punches a hole or writes allocated zeros by stable identity.
8886    ///
8887    /// # Errors
8888    ///
8889    /// Returns the same measured failures as [`Self::mutate`].
8890    pub async fn zero_file_range_by_id(
8891        &mut self,
8892        file_id: FileId,
8893        range: ByteRange,
8894        allocated: bool,
8895        extend: bool,
8896        budget: WorkBudget,
8897        cancellation: &CancellationToken,
8898    ) -> FsResult<()> {
8899        self.mutate(
8900            vec![Mutation::File {
8901                file_id,
8902                mutation: FileMutation::ZeroRange {
8903                    offset: range.offset,
8904                    length: range.length,
8905                    allocated,
8906                    extend,
8907                },
8908            }],
8909            budget,
8910            cancellation,
8911        )
8912        .await
8913    }
8914
8915    /// Allocates sparse holes by stable identity.
8916    ///
8917    /// # Errors
8918    ///
8919    /// Returns the same measured failures as [`Self::mutate`].
8920    pub async fn preallocate_file_by_id(
8921        &mut self,
8922        file_id: FileId,
8923        range: ByteRange,
8924        keep_size: bool,
8925        budget: WorkBudget,
8926        cancellation: &CancellationToken,
8927    ) -> FsResult<()> {
8928        self.mutate(
8929            vec![Mutation::File {
8930                file_id,
8931                mutation: FileMutation::Preallocate {
8932                    offset: range.offset,
8933                    length: range.length,
8934                    keep_size,
8935                },
8936            }],
8937            budget,
8938            cancellation,
8939        )
8940        .await
8941    }
8942
8943    /// Clones one sparse logical range between stable identities.
8944    ///
8945    /// # Errors
8946    ///
8947    /// Returns the same measured failures as [`Self::mutate`].
8948    #[allow(clippy::too_many_arguments)]
8949    pub async fn clone_file_range_by_id(
8950        &mut self,
8951        source_file_id: FileId,
8952        source_offset: u64,
8953        destination_file_id: FileId,
8954        destination_offset: u64,
8955        length: u64,
8956        budget: WorkBudget,
8957        cancellation: &CancellationToken,
8958    ) -> FsResult<()> {
8959        self.mutate(
8960            vec![Mutation::CloneFileRange {
8961                source_file_id,
8962                source_offset,
8963                destination_file_id,
8964                destination_offset,
8965                length,
8966            }],
8967            budget,
8968            cancellation,
8969        )
8970        .await
8971    }
8972
8973    /// Removes one namespace binding with an optional exact identity precondition.
8974    ///
8975    /// # Errors
8976    ///
8977    /// Returns the same measured failures as [`Self::mutate`].
8978    pub async fn remove(
8979        &mut self,
8980        path: NamespacePath,
8981        expected_file_id: Option<FileId>,
8982        budget: WorkBudget,
8983        cancellation: &CancellationToken,
8984    ) -> FsResult<()> {
8985        self.mutate(
8986            vec![Mutation::Remove {
8987                path,
8988                expected_file_id: expected_file_id
8989                    .map_or(MetadataField::Unavailable, MetadataField::Value),
8990            }],
8991            budget,
8992            cancellation,
8993        )
8994        .await
8995    }
8996
8997    /// Atomically renames one binding within this volume.
8998    ///
8999    /// # Errors
9000    ///
9001    /// Returns the same measured failures as [`Self::mutate`].
9002    pub async fn rename(
9003        &mut self,
9004        source: NamespacePath,
9005        destination: NamespacePath,
9006        replace: bool,
9007        budget: WorkBudget,
9008        cancellation: &CancellationToken,
9009    ) -> FsResult<()> {
9010        self.mutate(
9011            vec![Mutation::Rename {
9012                source,
9013                destination,
9014                replace,
9015            }],
9016            budget,
9017            cancellation,
9018        )
9019        .await
9020    }
9021
9022    /// Creates another binding to an existing non-directory file.
9023    ///
9024    /// # Errors
9025    ///
9026    /// Returns the same measured failures as [`Self::mutate`].
9027    pub async fn hard_link(
9028        &mut self,
9029        source: NamespacePath,
9030        destination: NamespacePath,
9031        budget: WorkBudget,
9032        cancellation: &CancellationToken,
9033    ) -> FsResult<()> {
9034        self.mutate(
9035            vec![Mutation::Link {
9036                source,
9037                destination,
9038            }],
9039            budget,
9040            cancellation,
9041        )
9042        .await
9043    }
9044
9045    /// Replaces the complete canonical metadata record for one existing file.
9046    ///
9047    /// The encoded metadata object is admitted before the checkout mutation;
9048    /// failed publication can leave only an unreachable immutable object.
9049    ///
9050    /// # Errors
9051    ///
9052    /// Returns measured metadata encoding, object-storage, cancellation,
9053    /// mutation, allocation, or bounded-work failures.
9054    pub async fn set_metadata(
9055        &mut self,
9056        path: NamespacePath,
9057        metadata: FileMetadata,
9058        budget: WorkBudget,
9059        cancellation: &CancellationToken,
9060    ) -> FsResult<()> {
9061        let mut work = WorkCounters::default();
9062        let encoded = encode_file_metadata(metadata)
9063            .map_err(|error| OperationFailure::new(error.into(), work))?;
9064        let stored = self
9065            .volume
9066            .fs
9067            .put_encoded(ObjectKind::Metadata, encoded, work, budget, cancellation)
9068            .await?;
9069        work = stored.1;
9070        let mutation = self
9071            .mutate(
9072                vec![Mutation::SetMetadata {
9073                    path,
9074                    metadata: stored.0,
9075                }],
9076                remaining(work, budget)?,
9077                cancellation,
9078            )
9079            .await
9080            .map_err(|failure| failure.map_with_prior_work(work, std::convert::identity))?;
9081        work = add(work, mutation.work)?;
9082        Ok(FsReceipt { value: (), work })
9083    }
9084
9085    /// Replaces complete metadata and optionally logical size as one path transaction.
9086    ///
9087    /// Immutable metadata may be staged first, but the candidate changes only
9088    /// when both the metadata and optional sparse resize validate and apply.
9089    ///
9090    /// # Errors
9091    ///
9092    /// Returns measured encoding, storage, sparse mutation, cancellation,
9093    /// allocation, or bounded-work failures.
9094    pub async fn set_attributes(
9095        &mut self,
9096        path: NamespacePath,
9097        metadata: FileMetadata,
9098        logical_bytes: Option<u64>,
9099        budget: WorkBudget,
9100        cancellation: &CancellationToken,
9101    ) -> FsResult<()> {
9102        let lookup = self
9103            .lookup_no_follow_observed(&path, budget, cancellation, true)
9104            .await?;
9105        let mut work = lookup.work;
9106        let file_id = lookup
9107            .value
9108            .record
9109            .ok_or_else(|| OperationFailure::new(FsError::NotFound, work))?
9110            .file_id;
9111        let mutation = self
9112            .set_attributes_by_id(
9113                file_id,
9114                metadata,
9115                logical_bytes,
9116                remaining(work, budget)?,
9117                cancellation,
9118            )
9119            .await
9120            .map_err(|failure| failure.map_with_prior_work(work, std::convert::identity))?;
9121        work = add(work, mutation.work)?;
9122        Ok(FsReceipt { value: (), work })
9123    }
9124
9125    /// Changes a regular file's logical length while preserving sparse semantics.
9126    ///
9127    /// # Errors
9128    ///
9129    /// Returns the same measured failures as [`Self::mutate`].
9130    pub async fn resize_file(
9131        &mut self,
9132        path: NamespacePath,
9133        logical_bytes: u64,
9134        budget: WorkBudget,
9135        cancellation: &CancellationToken,
9136    ) -> FsResult<()> {
9137        self.mutate(
9138            vec![Mutation::Resize {
9139                path,
9140                logical_bytes,
9141            }],
9142            budget,
9143            cancellation,
9144        )
9145        .await
9146    }
9147
9148    /// Punches a hole or preserves physical allocation as canonical zeros.
9149    ///
9150    /// # Errors
9151    ///
9152    /// Returns the same measured failures as [`Self::mutate`].
9153    pub async fn zero_file_range(
9154        &mut self,
9155        path: NamespacePath,
9156        range: ByteRange,
9157        allocated: bool,
9158        extend: bool,
9159        budget: WorkBudget,
9160        cancellation: &CancellationToken,
9161    ) -> FsResult<()> {
9162        self.mutate(
9163            vec![Mutation::ZeroRange {
9164                path,
9165                offset: range.offset,
9166                length: range.length,
9167                allocated,
9168                extend,
9169            }],
9170            budget,
9171            cancellation,
9172        )
9173        .await
9174    }
9175
9176    /// Allocates sparse holes while preserving all existing file content.
9177    ///
9178    /// With `keep_size = false`, the file grows to include the complete range.
9179    /// A keep-size request extending beyond EOF is rejected explicitly because
9180    /// invisible host allocation is not part of the portable authenticated format.
9181    ///
9182    /// # Errors
9183    ///
9184    /// Returns typed range, capability, sparse-mutation, storage, cancellation,
9185    /// or bounded-work failures.
9186    pub async fn preallocate_file(
9187        &mut self,
9188        path: NamespacePath,
9189        range: ByteRange,
9190        keep_size: bool,
9191        budget: WorkBudget,
9192        cancellation: &CancellationToken,
9193    ) -> FsResult<()> {
9194        self.mutate(
9195            vec![Mutation::Preallocate {
9196                path,
9197                offset: range.offset,
9198                length: range.length,
9199                keep_size,
9200            }],
9201            budget,
9202            cancellation,
9203        )
9204        .await
9205    }
9206
9207    /// Clones one logical range by immutable extent reference without reading bytes.
9208    ///
9209    /// # Errors
9210    ///
9211    /// Returns the same measured failures as [`Self::mutate`].
9212    pub async fn clone_file_range(
9213        &mut self,
9214        request: FileCloneRequest,
9215        budget: WorkBudget,
9216        cancellation: &CancellationToken,
9217    ) -> FsResult<()> {
9218        self.mutate(
9219            vec![Mutation::CloneRange {
9220                source: request.source,
9221                source_offset: request.source_offset,
9222                destination: request.destination,
9223                destination_offset: request.destination_offset,
9224                length: request.length,
9225            }],
9226            budget,
9227            cancellation,
9228        )
9229        .await
9230    }
9231
9232    /// Streams caller content into immutable authenticated chunks without
9233    /// materializing the complete source in memory.
9234    ///
9235    /// The returned reference can be used by
9236    /// [`AuthoredMutation::CreateFileFromContent`] or
9237    /// [`AuthoredMutation::WriteFromContent`] in one later atomic transaction.
9238    /// Unused staged content is harmless and collectible.
9239    ///
9240    /// # Errors
9241    ///
9242    /// Rejects zero or volume-exceeding source bounds and returns exact source,
9243    /// storage, cancellation, allocation, encoding, or work-budget failures.
9244    pub async fn stage_content<R: AsyncBlobSource>(
9245        &self,
9246        source: &mut R,
9247        maximum_source_bytes: u64,
9248        budget: WorkBudget,
9249        cancellation: &CancellationToken,
9250    ) -> FsResult<StagedContent> {
9251        stage_content_for_volume(
9252            &self.volume,
9253            source,
9254            maximum_source_bytes,
9255            budget,
9256            cancellation,
9257        )
9258        .await
9259    }
9260
9261    async fn stage_blob(
9262        &self,
9263        bytes: Bytes,
9264        budget: WorkBudget,
9265        cancellation: &CancellationToken,
9266    ) -> FsResult<crate::kernel::BlobBuild> {
9267        if u64::try_from(bytes.len()).unwrap_or(u64::MAX)
9268            > self.volume.config.limits.maximum_read_bytes
9269        {
9270            return Err(OperationFailure::before_work(FsError::FileRead(
9271                FileRangeReadError::InvalidRange,
9272            )));
9273        }
9274        let maximum_blob_bytes = u64::try_from(bytes.len()).unwrap_or(u64::MAX).max(1);
9275        let mut source = std::io::Cursor::new(bytes);
9276        self.stage_blob_source(&mut source, maximum_blob_bytes, budget, cancellation)
9277            .await
9278    }
9279
9280    async fn stage_blob_source<R: AsyncBlobSource>(
9281        &self,
9282        source: &mut R,
9283        maximum_blob_bytes: u64,
9284        budget: WorkBudget,
9285        cancellation: &CancellationToken,
9286    ) -> FsResult<crate::kernel::BlobBuild> {
9287        let staged = stage_content_for_volume(
9288            &self.volume,
9289            source,
9290            maximum_blob_bytes,
9291            budget,
9292            cancellation,
9293        )
9294        .await?;
9295        Ok(FsReceipt {
9296            work: staged.work,
9297            value: crate::kernel::BlobBuild {
9298                root: staged.value.root,
9299                logical_bytes: staged.value.logical_bytes,
9300                work: staged.work,
9301            },
9302        })
9303    }
9304
9305    /// Checkpoints, authenticates, and conditionally publishes this overlay.
9306    ///
9307    /// The caller supplies a stable operation identity and must reuse it only
9308    /// for an exact retry. Semantic authority conflicts are returned as values;
9309    /// failed or rejected publication leaves the private overlay intact.
9310    ///
9311    /// # Errors
9312    ///
9313    /// Rejects clean/non-writable checkouts and returns exact checkpoint,
9314    /// closure, authority, cancellation, or budget failures.
9315    pub async fn commit(
9316        &mut self,
9317        operation_id: OperationId,
9318        budget: WorkBudget,
9319        cancellation: &CancellationToken,
9320    ) -> FsResult<CheckoutCommitOutcome> {
9321        if self.mode.access != AccessMode::ReadWrite
9322            || self.mode.mutations != MutationMode::PrivateOverlay
9323        {
9324            return Err(OperationFailure::before_work(FsError::MutationNotAllowed));
9325        }
9326        self.publish_pending(operation_id, None, budget, cancellation)
9327            .await
9328    }
9329
9330    /// Publishes this overlay only while the supplied durable operation lease
9331    /// remains active at the authority linearization point.
9332    pub async fn commit_with_permit(
9333        &mut self,
9334        operation_id: OperationId,
9335        permit: PublicationPermit,
9336        budget: WorkBudget,
9337        cancellation: &CancellationToken,
9338    ) -> FsResult<CheckoutCommitOutcome> {
9339        if self.mode.access != AccessMode::ReadWrite
9340            || self.mode.mutations != MutationMode::PrivateOverlay
9341        {
9342            return Err(OperationFailure::before_work(FsError::MutationNotAllowed));
9343        }
9344        self.publish_pending_with_permit(operation_id, None, permit, budget, cancellation)
9345            .await
9346    }
9347
9348    /// Anchors a separately journaled lazy overlay to an exact checkout
9349    /// publication, even when the checkout's authored root is unchanged.
9350    #[cfg(feature = "native-mount")]
9351    pub(crate) async fn commit_with_permit_even_if_clean(
9352        &mut self,
9353        operation_id: OperationId,
9354        permit: PublicationPermit,
9355        budget: WorkBudget,
9356        cancellation: &CancellationToken,
9357    ) -> FsResult<CheckoutCommitOutcome> {
9358        if self.mode.access != AccessMode::ReadWrite
9359            || self.mode.mutations != MutationMode::PrivateOverlay
9360        {
9361            return Err(OperationFailure::before_work(FsError::MutationNotAllowed));
9362        }
9363        let expected = self
9364            .authority_head
9365            .ok_or_else(|| OperationFailure::before_work(FsError::WritableCheckoutRequiresHead))?;
9366        self.publish_pending_against(operation_id, None, permit, expected, budget, cancellation)
9367            .await
9368    }
9369
9370    #[cfg(all(feature = "native-watch", not(target_arch = "wasm32")))]
9371    pub(crate) async fn commit_with_context(
9372        &mut self,
9373        operation_id: OperationId,
9374        operation_context: Digest,
9375        budget: WorkBudget,
9376        cancellation: &CancellationToken,
9377    ) -> FsResult<CheckoutCommitOutcome> {
9378        if self.mode.access != AccessMode::ReadWrite
9379            || self.mode.mutations != MutationMode::PrivateOverlay
9380        {
9381            return Err(OperationFailure::before_work(FsError::MutationNotAllowed));
9382        }
9383        let expected = self
9384            .authority_head
9385            .ok_or_else(|| OperationFailure::before_work(FsError::WritableCheckoutRequiresHead))?;
9386        // Native-source operations must linearize even when capture produced no
9387        // mutations. Publishing the unchanged root is their atomic head fence.
9388        self.publish_pending_against(
9389            operation_id,
9390            Some(operation_context),
9391            PublicationPermit::Unrestricted,
9392            expected,
9393            budget,
9394            cancellation,
9395        )
9396        .await
9397    }
9398
9399    async fn publish_pending(
9400        &mut self,
9401        operation_id: OperationId,
9402        operation_context: Option<Digest>,
9403        budget: WorkBudget,
9404        cancellation: &CancellationToken,
9405    ) -> FsResult<CheckoutCommitOutcome> {
9406        self.publish_pending_with_permit(
9407            operation_id,
9408            operation_context,
9409            PublicationPermit::Unrestricted,
9410            budget,
9411            cancellation,
9412        )
9413        .await
9414    }
9415
9416    async fn publish_pending_with_permit(
9417        &mut self,
9418        operation_id: OperationId,
9419        operation_context: Option<Digest>,
9420        permit: PublicationPermit,
9421        budget: WorkBudget,
9422        cancellation: &CancellationToken,
9423    ) -> FsResult<CheckoutCommitOutcome> {
9424        if !self.has_pending_mutations() && permit == PublicationPermit::Unrestricted {
9425            return self
9426                .resolve_clean_commit(operation_id, budget, cancellation)
9427                .await;
9428        }
9429        let expected = self
9430            .authority_head
9431            .ok_or_else(|| OperationFailure::before_work(FsError::WritableCheckoutRequiresHead))?;
9432        self.publish_pending_against(
9433            operation_id,
9434            operation_context,
9435            permit,
9436            expected,
9437            budget,
9438            cancellation,
9439        )
9440        .await
9441    }
9442
9443    async fn publish_pending_against(
9444        &mut self,
9445        operation_id: OperationId,
9446        operation_context: Option<Digest>,
9447        permit: PublicationPermit,
9448        expected: Head,
9449        budget: WorkBudget,
9450        cancellation: &CancellationToken,
9451    ) -> FsResult<CheckoutCommitOutcome> {
9452        cancellation
9453            .check()
9454            .map_err(|error| OperationFailure::before_work(error.into()))?;
9455        let checkpoint = self.checkpoint_root(budget, cancellation).await?;
9456        let checkpoint_root = checkpoint.value;
9457        let mut work = checkpoint.work;
9458        let request = PublishGenerationRequest {
9459            authority_id: volume_authority_id(self.volume.id),
9460            volume_id: self.volume.id,
9461            epoch: expected.epoch,
9462            expected,
9463            operation_id,
9464            generation_root: checkpoint_root,
9465        };
9466        let publication = if permit != PublicationPermit::Unrestricted {
9467            publish_generation_async_with_permit(
9468                &self.volume.fs.inner.objects,
9469                &self.volume.fs.inner.authority,
9470                request,
9471                PublicationIntent {
9472                    operation_context,
9473                    permit,
9474                },
9475                closure_limits(self.volume.config),
9476                remaining(work, budget)?,
9477                cancellation,
9478            )
9479            .await
9480        } else if let Some(context) = operation_context {
9481            publish_generation_async_with_context(
9482                &self.volume.fs.inner.objects,
9483                &self.volume.fs.inner.authority,
9484                request,
9485                context,
9486                closure_limits(self.volume.config),
9487                remaining(work, budget)?,
9488                cancellation,
9489            )
9490            .await
9491        } else {
9492            publish_generation_async(
9493                &self.volume.fs.inner.objects,
9494                &self.volume.fs.inner.authority,
9495                request,
9496                closure_limits(self.volume.config),
9497                remaining(work, budget)?,
9498                cancellation,
9499            )
9500            .await
9501        }
9502        .map_err(|failure| failure.map_with_prior_work(work, Into::into))?;
9503        work = add(work, publication.work)?;
9504        let outcome = self.apply_publication(checkpoint_root, operation_id, publication);
9505        Ok(FsReceipt {
9506            value: outcome,
9507            work,
9508        })
9509    }
9510
9511    fn apply_publication(
9512        &mut self,
9513        checkpoint_root: ObjectId,
9514        operation_id: OperationId,
9515        publication: PublicationReceipt,
9516    ) -> CheckoutCommitOutcome {
9517        let generation_id = publication.proof.generation_id;
9518        match publication.outcome {
9519            AppendOutcome::Committed(commit) => {
9520                let head = durable_head(&commit);
9521                self.base_generation_root = checkpoint_root;
9522                self.generation_root = checkpoint_root;
9523                self.root = publication.proof.root;
9524                self.base_root = self.root.clone();
9525                self.base_file_table = self.root.file_table;
9526                self.authority_head = Some(head);
9527                self.prepared_merge_parent = None;
9528                self.dependencies.clear();
9529                self.last_commit = Some(LastCommit {
9530                    operation_id,
9531                    generation_id,
9532                    head,
9533                });
9534                CheckoutCommitOutcome::Committed {
9535                    generation_id,
9536                    head,
9537                }
9538            }
9539            AppendOutcome::AlreadyCommitted(commit) => {
9540                let head = durable_head(&commit);
9541                self.base_generation_root = checkpoint_root;
9542                self.generation_root = checkpoint_root;
9543                self.root = publication.proof.root;
9544                self.base_root = self.root.clone();
9545                self.base_file_table = self.root.file_table;
9546                self.authority_head = Some(head);
9547                self.prepared_merge_parent = None;
9548                self.dependencies.clear();
9549                self.last_commit = Some(LastCommit {
9550                    operation_id,
9551                    generation_id,
9552                    head,
9553                });
9554                CheckoutCommitOutcome::AlreadyCommitted {
9555                    generation_id,
9556                    head,
9557                }
9558            }
9559            AppendOutcome::Conflict { actual } => CheckoutCommitOutcome::Conflict { actual },
9560            AppendOutcome::Fenced { actual_epoch } => {
9561                CheckoutCommitOutcome::Fenced { actual_epoch }
9562            }
9563            AppendOutcome::IdempotencyConflict {
9564                committed_fingerprint,
9565            } => CheckoutCommitOutcome::IdempotencyConflict {
9566                committed_fingerprint,
9567            },
9568        }
9569    }
9570
9571    pub(crate) async fn retry_stale_commit(
9572        &mut self,
9573        operation_id: OperationId,
9574        permit: PublicationPermit,
9575        budget: WorkBudget,
9576        cancellation: &CancellationToken,
9577    ) -> FsResult<Option<CheckoutCommitOutcome>> {
9578        cancellation
9579            .check()
9580            .map_err(|error| OperationFailure::before_work(error.into()))?;
9581        let resolved = self
9582            .volume
9583            .fs
9584            .inner
9585            .authority
9586            .find_operation(
9587                volume_authority_id(self.volume.id),
9588                operation_id,
9589                budget,
9590                cancellation,
9591            )
9592            .await
9593            .map_err(|failure| {
9594                OperationFailure::new(FsError::Authority(failure.error), *failure.work)
9595            })?;
9596        let Some(commit) = resolved.value else {
9597            return Ok(FsReceipt {
9598                value: None,
9599                work: resolved.work,
9600            });
9601        };
9602        let previous_sequence = commit.sequence.get().checked_sub(1).ok_or_else(|| {
9603            OperationFailure::new(FsError::InvalidAuthorityHistory, resolved.work)
9604        })?;
9605        let expected = Head {
9606            epoch: commit.epoch,
9607            sequence: Sequence::new(previous_sequence),
9608            digest: commit.previous_digest,
9609        };
9610        let retried = self
9611            .publish_pending_against(
9612                operation_id,
9613                None,
9614                permit,
9615                expected,
9616                remaining(resolved.work, budget)?,
9617                cancellation,
9618            )
9619            .await
9620            .map_err(|failure| {
9621                failure.map_with_prior_work(resolved.work, std::convert::identity)
9622            })?;
9623        Ok(FsReceipt {
9624            value: Some(retried.value),
9625            work: add(resolved.work, retried.work)?,
9626        })
9627    }
9628
9629    async fn resolve_clean_commit(
9630        &self,
9631        operation_id: OperationId,
9632        budget: WorkBudget,
9633        cancellation: &CancellationToken,
9634    ) -> FsResult<CheckoutCommitOutcome> {
9635        if let Some(last) = self.last_commit {
9636            if last.operation_id != operation_id {
9637                return Err(OperationFailure::before_work(FsError::NoPendingMutations));
9638            }
9639            if last.generation_id == GenerationId::new(self.generation_root.digest) {
9640                return Ok(FsReceipt {
9641                    value: CheckoutCommitOutcome::AlreadyCommitted {
9642                        generation_id: last.generation_id,
9643                        head: last.head,
9644                    },
9645                    work: WorkCounters::default(),
9646                });
9647            }
9648        }
9649        cancellation
9650            .check()
9651            .map_err(|error| OperationFailure::before_work(error.into()))?;
9652        let head = self
9653            .authority_head
9654            .ok_or_else(|| OperationFailure::before_work(FsError::WritableCheckoutRequiresHead))?;
9655        let resolved = self
9656            .volume
9657            .fs
9658            .inner
9659            .authority
9660            .find_operation(
9661                volume_authority_id(self.volume.id),
9662                operation_id,
9663                budget,
9664                cancellation,
9665            )
9666            .await
9667            .map_err(|failure| {
9668                OperationFailure::new(FsError::Authority(failure.error), *failure.work)
9669            })?;
9670        let Some(commit) = resolved.value else {
9671            return Err(OperationFailure::new(
9672                FsError::NoPendingMutations,
9673                resolved.work,
9674            ));
9675        };
9676        let generation_root = generation_from_record(&commit, self.volume.id, resolved.work)?;
9677        let value = if generation_root == self.generation_root {
9678            CheckoutCommitOutcome::AlreadyCommitted {
9679                generation_id: GenerationId::new(generation_root.digest),
9680                head,
9681            }
9682        } else {
9683            CheckoutCommitOutcome::IdempotencyConflict {
9684                committed_fingerprint: commit.fingerprint,
9685            }
9686        };
9687        Ok(FsReceipt {
9688            value,
9689            work: resolved.work,
9690        })
9691    }
9692
9693    /// Resolves an exact path without following links or scanning unrelated subtrees.
9694    ///
9695    /// # Errors
9696    ///
9697    /// Returns a measured failure for invalid profile semantics, malformed
9698    /// authenticated routing, missing root records, non-directory traversal,
9699    /// cancellation, backend failure, or budget exhaustion.
9700    pub async fn lookup_no_follow(
9701        &mut self,
9702        path: &NamespacePath,
9703        budget: WorkBudget,
9704        cancellation: &CancellationToken,
9705    ) -> FsResult<PathLookup> {
9706        self.lookup_no_follow_observed(path, budget, cancellation, true)
9707            .await
9708    }
9709
9710    /// Resolves one exact path and decodes its authenticated metadata without
9711    /// repeating the namespace or file-table traversal.
9712    ///
9713    /// # Errors
9714    ///
9715    /// Returns the lookup failures plus bounded object-read or metadata-decode
9716    /// failures. Authenticated absence performs no metadata read.
9717    pub async fn lookup_no_follow_with_metadata(
9718        &mut self,
9719        path: &NamespacePath,
9720        budget: WorkBudget,
9721        cancellation: &CancellationToken,
9722    ) -> FsResult<Option<PathMetadataLookup>> {
9723        let lookup = self
9724            .lookup_no_follow_observed(path, budget, cancellation, false)
9725            .await?;
9726        let mut work = lookup.work;
9727        let Some(record) = lookup.value.record else {
9728            return Ok(FsReceipt { value: None, work });
9729        };
9730        let metadata = self
9731            .volume
9732            .fs
9733            .inner
9734            .objects
9735            .read(
9736                record.metadata,
9737                self.volume.config.limits.maximum_object_bytes,
9738                remaining(work, budget)?,
9739                cancellation,
9740            )
9741            .await
9742            .map_err(|failure| failure.map_with_prior_work(work, FsError::Object))?;
9743        work = add(work, metadata.work)?;
9744        let metadata = decode_file_metadata(&metadata.value, decode_limits(self.volume.config))
9745            .map_err(|error| OperationFailure::new(error.into(), work))?;
9746        if self.mode.consistency != ConsistencyMode::Pinned {
9747            work = self
9748                .observe_base_metadata_ids(
9749                    std::slice::from_ref(&record.file_id),
9750                    work,
9751                    budget,
9752                    cancellation,
9753                )
9754                .await?;
9755        }
9756        Ok(FsReceipt {
9757            value: Some(PathMetadataLookup { record, metadata }),
9758            work,
9759        })
9760    }
9761
9762    async fn lookup_no_follow_observed(
9763        &mut self,
9764        path: &NamespacePath,
9765        budget: WorkBudget,
9766        cancellation: &CancellationToken,
9767        capture_terminal: bool,
9768    ) -> FsResult<PathLookup> {
9769        let synchronized = self.synchronize_live(budget, cancellation).await?;
9770        let prior = synchronized.work;
9771        let mut lookup = Box::pin(self.lookup_no_follow_at_current(
9772            path,
9773            remaining(prior, budget)?,
9774            cancellation,
9775            capture_terminal,
9776        ))
9777        .await
9778        .map_err(|failure| failure.map_with_prior_work(prior, std::convert::identity))?;
9779        let mut total = add(prior, lookup.work)?;
9780        if lookup.value.record.is_none()
9781            && self.volume.config.case_sensitivity == CaseSensitivity::ProfileFolded
9782        {
9783            // Boxed: this folded lookup path (and everything it transitively
9784            // calls, including a retried lookup) is a cold path that would
9785            // otherwise inline into every caller's future, including ones
9786            // that never touch a `ProfileFolded` volume.
9787            let folded = Box::pin(self.resolve_profile_folded_path(
9788                path,
9789                remaining(total, budget)?,
9790                cancellation,
9791                capture_terminal,
9792            ))
9793            .await
9794            .map_err(|failure| failure.map_with_prior_work(total, std::convert::identity))?;
9795            total = add(total, folded.work)?;
9796            if let Some(resolved) = folded.value {
9797                lookup.value = resolved;
9798            }
9799        }
9800        lookup.work = total;
9801        lookup.value.work = total;
9802        Ok(lookup)
9803    }
9804
9805    /// Retries an exact-match lookup miss under `CaseSensitivity::ProfileFolded`
9806    /// by resolving `path`'s terminal component against its case-folded
9807    /// siblings. Returns `Ok(None)` when `path` is the root (no siblings to
9808    /// fold against) or no folded match exists, leaving the original
9809    /// exact-match result in place.
9810    async fn resolve_profile_folded_path(
9811        &mut self,
9812        path: &NamespacePath,
9813        budget: WorkBudget,
9814        cancellation: &CancellationToken,
9815        capture_terminal: bool,
9816    ) -> FsResult<Option<PathLookup>> {
9817        let mut work = WorkCounters::default();
9818        if path.is_root() {
9819            return Ok(FsReceipt { value: None, work });
9820        }
9821        let resolved = Box::pin(self.canonicalize_path_components(
9822            path.components(),
9823            &[],
9824            budget,
9825            cancellation,
9826        ))
9827        .await
9828        .map_err(|failure| failure.map_with_prior_work(work, std::convert::identity))?;
9829        work = add(work, resolved.work)?;
9830        let Some(corrected) = resolved.value else {
9831            return Ok(FsReceipt { value: None, work });
9832        };
9833        let retried = self
9834            .lookup_no_follow_at_current(
9835                &corrected,
9836                remaining(work, budget)?,
9837                cancellation,
9838                capture_terminal,
9839            )
9840            .await
9841            .map_err(|failure| failure.map_with_prior_work(work, std::convert::identity))?;
9842        work = add(work, retried.work)?;
9843        Ok(FsReceipt {
9844            value: Some(retried.value),
9845            work,
9846        })
9847    }
9848
9849    async fn lookup_no_follow_at_current(
9850        &mut self,
9851        path: &NamespacePath,
9852        budget: WorkBudget,
9853        cancellation: &CancellationToken,
9854        capture_terminal: bool,
9855    ) -> FsResult<PathLookup> {
9856        let lookup = if self.mode.consistency == ConsistencyMode::Pinned {
9857            crate::kernel::lookup_path_async(
9858                &self.volume.fs.inner.objects,
9859                &self.root,
9860                path,
9861                self.volume.config,
9862                budget,
9863                cancellation,
9864            )
9865            .await
9866            .map_err(|failure| OperationFailure::new(failure.error.into(), *failure.work))?
9867        } else {
9868            let candidate = if self.has_pending_mutations() {
9869                Some(
9870                    crate::kernel::lookup_path_async(
9871                        &self.volume.fs.inner.objects,
9872                        &self.root,
9873                        path,
9874                        self.volume.config,
9875                        budget,
9876                        cancellation,
9877                    )
9878                    .await
9879                    .map_err(|failure| {
9880                        OperationFailure::new(failure.error.into(), *failure.work)
9881                    })?,
9882                )
9883            } else {
9884                None
9885            };
9886            let prior = candidate
9887                .as_ref()
9888                .map_or(WorkCounters::default(), |value| value.work);
9889            let observed = self
9890                .observe_base_for_candidate(
9891                    path,
9892                    prior,
9893                    candidate.is_some(),
9894                    capture_terminal,
9895                    budget,
9896                    cancellation,
9897                )
9898                .await?;
9899            let combined = observed.work;
9900            self.dependencies
9901                .extend_observations(
9902                    observed.value.dependencies,
9903                    self.volume.config.limits.maximum_checkout_dependencies,
9904                )
9905                .map_err(|error| OperationFailure::new(error.into(), combined))?;
9906            candidate.map_or(
9907                PathLookup {
9908                    work: combined,
9909                    ..observed.value.lookup
9910                },
9911                |value| PathLookup {
9912                    work: combined,
9913                    ..value
9914                },
9915            )
9916        };
9917        Ok(FsReceipt {
9918            work: lookup.work,
9919            value: lookup,
9920        })
9921    }
9922
9923    async fn observe_base_for_candidate(
9924        &self,
9925        path: &NamespacePath,
9926        prior: WorkCounters,
9927        has_candidate: bool,
9928        capture_terminal: bool,
9929        budget: WorkBudget,
9930        cancellation: &CancellationToken,
9931    ) -> FsResult<crate::kernel::ObservedPathLookup> {
9932        let observed = if capture_terminal {
9933            crate::kernel::observe_path_async(
9934                &self.volume.fs.inner.objects,
9935                &self.base_root,
9936                path,
9937                self.volume.config,
9938                remaining(prior, budget)?,
9939                cancellation,
9940            )
9941            .await
9942        } else {
9943            crate::kernel::observe_path_edges_async(
9944                &self.volume.fs.inner.objects,
9945                &self.base_root,
9946                path,
9947                self.volume.config,
9948                remaining(prior, budget)?,
9949                cancellation,
9950            )
9951            .await
9952        };
9953        let (observed, observation_prior) = match observed {
9954            Ok(observed) => (observed, prior),
9955            Err(failure)
9956                if has_candidate && matches!(failure.error, PathLookupError::NotDirectory) =>
9957            {
9958                // The candidate can replace a base file with a directory.
9959                // The positive base edge to that file is the full dependency.
9960                let consumed = add(prior, *failure.work)?;
9961                let observed = crate::kernel::observe_path_edges_async(
9962                    &self.volume.fs.inner.objects,
9963                    &self.base_root,
9964                    path,
9965                    self.volume.config,
9966                    remaining(consumed, budget)?,
9967                    cancellation,
9968                )
9969                .await
9970                .map_err(|failure| failure.map_with_prior_work(consumed, FsError::Path))?;
9971                (observed, consumed)
9972            }
9973            Err(failure) => return Err(failure.map_with_prior_work(prior, FsError::Path)),
9974        };
9975        Ok(FsReceipt {
9976            work: add(observation_prior, observed.lookup.work)?,
9977            value: observed,
9978        })
9979    }
9980
9981    /// Resolves a bounded path batch while sharing directory and file-table frontiers.
9982    ///
9983    /// Input order and duplicates are preserved. The operation never follows
9984    /// links, reads file bodies, or scans unrelated namespace subtrees.
9985    ///
9986    /// # Errors
9987    ///
9988    /// Returns the same measured fail-closed outcomes as [`Self::lookup_no_follow`],
9989    /// plus explicit empty and excessive-batch rejection.
9990    pub async fn lookup_batch_no_follow(
9991        &mut self,
9992        paths: &[NamespacePath],
9993        budget: WorkBudget,
9994        cancellation: &CancellationToken,
9995    ) -> FsResult<PathBatchLookup> {
9996        let synchronized = self.synchronize_live(budget, cancellation).await?;
9997        let prior = synchronized.work;
9998        let mut lookup = crate::kernel::lookup_paths_async(
9999            &self.volume.fs.inner.objects,
10000            &self.root,
10001            paths,
10002            self.volume.config,
10003            remaining(prior, budget)?,
10004            cancellation,
10005        )
10006        .await
10007        .map_err(|failure| failure.map_with_prior_work(prior, FsError::Path))?;
10008        lookup.work = add(prior, lookup.work)?;
10009        if self.mode.consistency != ConsistencyMode::Pinned {
10010            let mut work = lookup.work;
10011            for path in paths {
10012                let observed = crate::kernel::observe_path_async(
10013                    &self.volume.fs.inner.objects,
10014                    &self.base_root,
10015                    path,
10016                    self.volume.config,
10017                    remaining(work, budget)?,
10018                    cancellation,
10019                )
10020                .await
10021                .map_err(|failure| failure.map_with_prior_work(work, FsError::Path))?;
10022                work = add(work, observed.lookup.work)?;
10023                self.dependencies
10024                    .extend_observations(
10025                        observed.dependencies,
10026                        self.volume.config.limits.maximum_checkout_dependencies,
10027                    )
10028                    .map_err(|error| OperationFailure::new(error.into(), work))?;
10029            }
10030            lookup.work = work;
10031        }
10032        Ok(FsReceipt {
10033            work: lookup.work,
10034            value: lookup,
10035        })
10036    }
10037
10038    /// Returns one bounded authenticated page from a directory.
10039    ///
10040    /// Tracking checkouts retain the exact cursor interval as an observation,
10041    /// preventing an automatic rebase from crossing inserted or removed names.
10042    ///
10043    /// # Errors
10044    ///
10045    /// Returns measured path, kind, pagination, dependency, storage,
10046    /// cancellation, or bounded-work failures.
10047    pub async fn list_directory(
10048        &mut self,
10049        path: &NamespacePath,
10050        after: Option<&LogicalName>,
10051        maximum_entries: u32,
10052        budget: WorkBudget,
10053        cancellation: &CancellationToken,
10054    ) -> FsResult<DirectoryPage> {
10055        self.list_directory_with_bound(path, after, false, maximum_entries, budget, cancellation)
10056            .await
10057    }
10058
10059    async fn list_directory_with_bound(
10060        &mut self,
10061        path: &NamespacePath,
10062        after: Option<&LogicalName>,
10063        inclusive: bool,
10064        maximum_entries: u32,
10065        budget: WorkBudget,
10066        cancellation: &CancellationToken,
10067    ) -> FsResult<DirectoryPage> {
10068        let lookup = self
10069            .lookup_no_follow_observed(path, budget, cancellation, false)
10070            .await?;
10071        let mut work = lookup.work;
10072        let record = lookup
10073            .value
10074            .record
10075            .ok_or_else(|| OperationFailure::new(FsError::NotFound, work))?;
10076        let FilePayload::Directory { entries } = record.payload else {
10077            return Err(OperationFailure::new(FsError::NotDirectory, work));
10078        };
10079        if record.kind != FileKind::Directory {
10080            return Err(OperationFailure::new(FsError::NotDirectory, work));
10081        }
10082        let mut page = if let Some(at) = after.filter(|_| inclusive) {
10083            list_tree_entries_at_or_after_async(
10084                &self.volume.fs.inner.objects,
10085                entries,
10086                at,
10087                maximum_entries,
10088                decode_limits(self.volume.config),
10089                remaining(work, budget)?,
10090                cancellation,
10091            )
10092            .await
10093        } else {
10094            list_tree_entries_async(
10095                &self.volume.fs.inner.objects,
10096                entries,
10097                after,
10098                maximum_entries,
10099                decode_limits(self.volume.config),
10100                remaining(work, budget)?,
10101                cancellation,
10102            )
10103            .await
10104        }
10105        .map_err(|failure| failure.map_with_prior_work(work, FsError::Directory))?;
10106        work = add(work, page.work)?;
10107        if self.mode.consistency != ConsistencyMode::Pinned {
10108            let region = DependencyRegion::DirectoryRange {
10109                directory_id: record.file_id,
10110                after: after.cloned(),
10111                maximum_entries,
10112            };
10113            let probe = AuthenticatedGenerationProbe::new(
10114                &self.volume.fs.inner.objects,
10115                probe_limits(self.volume.config),
10116            )
10117            .map_err(|error| OperationFailure::new(error.into(), work))?;
10118            let state = probe
10119                .probe_async(
10120                    GenerationId::new(self.base_generation_root.digest),
10121                    &region,
10122                    remaining(work, budget)?,
10123                    cancellation,
10124                )
10125                .await
10126                .map_err(|failure| failure.map_with_prior_work(work, FsError::Probe))?;
10127            work = add(work, state.work)?;
10128            self.dependencies
10129                .extend_observations(
10130                    vec![Dependency {
10131                        region,
10132                        expected: state.value,
10133                    }],
10134                    self.volume.config.limits.maximum_checkout_dependencies,
10135                )
10136                .map_err(|error| OperationFailure::new(error.into(), work))?;
10137        }
10138        page.work = work;
10139        Ok(FsReceipt { value: page, work })
10140    }
10141
10142    /// Returns one directory page with all child records fetched in one shared
10143    /// authenticated file-table batch.
10144    ///
10145    /// This is the native-mount enumeration surface: it avoids one path lookup
10146    /// per child while preserving the same cursor observation as
10147    /// [`Self::list_directory`].
10148    ///
10149    /// # Errors
10150    ///
10151    /// Returns the listing failures plus authenticated file-table failures or
10152    /// a typed namespace/file-table consistency error.
10153    pub async fn list_directory_records(
10154        &mut self,
10155        path: &NamespacePath,
10156        after: Option<&LogicalName>,
10157        maximum_entries: u32,
10158        budget: WorkBudget,
10159        cancellation: &CancellationToken,
10160    ) -> FsResult<DirectoryRecordPage> {
10161        self.list_directory_records_with_bound(
10162            path,
10163            after,
10164            false,
10165            maximum_entries,
10166            budget,
10167            cancellation,
10168        )
10169        .await
10170    }
10171
10172    pub(crate) async fn list_directory_records_at_or_after(
10173        &mut self,
10174        path: &NamespacePath,
10175        at: &LogicalName,
10176        maximum_entries: u32,
10177        budget: WorkBudget,
10178        cancellation: &CancellationToken,
10179    ) -> FsResult<DirectoryRecordPage> {
10180        if self.mode.consistency != ConsistencyMode::Pinned {
10181            return Err(OperationFailure::before_work(FsError::UnsupportedCheckout));
10182        }
10183        self.list_directory_records_with_bound(
10184            path,
10185            Some(at),
10186            true,
10187            maximum_entries,
10188            budget,
10189            cancellation,
10190        )
10191        .await
10192    }
10193
10194    async fn list_directory_records_with_bound(
10195        &mut self,
10196        path: &NamespacePath,
10197        after: Option<&LogicalName>,
10198        inclusive: bool,
10199        maximum_entries: u32,
10200        budget: WorkBudget,
10201        cancellation: &CancellationToken,
10202    ) -> FsResult<DirectoryRecordPage> {
10203        let listing = self
10204            .list_directory_with_bound(
10205                path,
10206                after,
10207                inclusive,
10208                maximum_entries,
10209                budget,
10210                cancellation,
10211            )
10212            .await?;
10213        let mut work = listing.work;
10214        let page = listing.value;
10215        if page.entries.is_empty() {
10216            return Ok(FsReceipt {
10217                value: DirectoryRecordPage {
10218                    entries: Vec::new(),
10219                    has_more: page.has_more,
10220                },
10221                work,
10222            });
10223        }
10224        let retained = u64::try_from(page.entries.len())
10225            .unwrap_or(u64::MAX)
10226            .saturating_mul(u64::try_from(size_of::<FileId>()).unwrap_or(u64::MAX));
10227        work = add(
10228            work,
10229            WorkCounters {
10230                allocation_operations: 1,
10231                peak_allocation_bytes: retained,
10232                ..WorkCounters::default()
10233            },
10234        )?;
10235        work.verify(budget)
10236            .map_err(|error| OperationFailure::new(error.into(), work))?;
10237        let mut file_ids = Vec::new();
10238        file_ids
10239            .try_reserve_exact(page.entries.len())
10240            .map_err(|_| OperationFailure::new(FsError::PendingMutationAllocationFailed, work))?;
10241        file_ids.extend(page.entries.iter().map(|entry| entry.file_id));
10242        let records = lookup_file_records_async(
10243            &self.volume.fs.inner.objects,
10244            self.root.file_table,
10245            &file_ids,
10246            maximum_entries,
10247            decode_limits(self.volume.config),
10248            remaining(work, budget)?,
10249            cancellation,
10250        )
10251        .await
10252        .map_err(|failure| failure.map_with_prior_work(work, FsError::FileRecord))?;
10253        work = add(work, records.work)?;
10254        drop(file_ids);
10255        let mut named_records = Vec::new();
10256        named_records
10257            .try_reserve_exact(page.entries.len())
10258            .map_err(|_| OperationFailure::new(FsError::PendingMutationAllocationFailed, work))?;
10259        for (binding, record) in page.entries.into_iter().zip(records.records) {
10260            let record = record
10261                .ok_or_else(|| OperationFailure::new(FsError::InvalidDirectoryRecord, work))?;
10262            if record.file_id != binding.file_id || record.kind != binding.kind {
10263                return Err(OperationFailure::new(FsError::InvalidDirectoryRecord, work));
10264            }
10265            named_records.push((binding.name, record));
10266        }
10267        self.attach_directory_metadata(named_records, page.has_more, work, budget, cancellation)
10268            .await
10269    }
10270
10271    async fn attach_directory_metadata(
10272        &mut self,
10273        named_records: Vec<(LogicalName, FileRecord)>,
10274        has_more: bool,
10275        mut work: WorkCounters,
10276        budget: WorkBudget,
10277        cancellation: &CancellationToken,
10278    ) -> FsResult<DirectoryRecordPage> {
10279        let mut requests = Vec::new();
10280        requests
10281            .try_reserve_exact(named_records.len())
10282            .map_err(|_| OperationFailure::new(FsError::PendingMutationAllocationFailed, work))?;
10283        requests.extend(named_records.iter().map(|(_, record)| ObjectReadRequest {
10284            object_id: record.metadata,
10285            maximum_bytes: self.volume.config.limits.maximum_object_bytes,
10286        }));
10287        let metadata_reads = self
10288            .volume
10289            .fs
10290            .inner
10291            .objects
10292            .read_many(&requests, remaining(work, budget)?, cancellation)
10293            .await
10294            .map_err(|failure| failure.map_with_prior_work(work, FsError::Object))?;
10295        work = add(work, metadata_reads.work)?;
10296        let mut entries = Vec::new();
10297        entries
10298            .try_reserve_exact(named_records.len())
10299            .map_err(|_| OperationFailure::new(FsError::PendingMutationAllocationFailed, work))?;
10300        for ((name, record), metadata) in named_records.into_iter().zip(metadata_reads.value) {
10301            let metadata = decode_file_metadata(&metadata, decode_limits(self.volume.config))
10302                .map_err(|error| OperationFailure::new(error.into(), work))?;
10303            entries.push(DirectoryRecordEntry {
10304                name,
10305                record,
10306                metadata,
10307            });
10308        }
10309        if self.mode.consistency != ConsistencyMode::Pinned && !entries.is_empty() {
10310            let mut file_ids = Vec::new();
10311            file_ids.try_reserve_exact(entries.len()).map_err(|_| {
10312                OperationFailure::new(FsError::PendingMutationAllocationFailed, work)
10313            })?;
10314            file_ids.extend(entries.iter().map(|entry| entry.record.file_id));
10315            work = self
10316                .observe_base_metadata_ids(&file_ids, work, budget, cancellation)
10317                .await?;
10318        }
10319        Ok(FsReceipt {
10320            value: DirectoryRecordPage { entries, has_more },
10321            work,
10322        })
10323    }
10324
10325    /// Reads complete canonical metadata for one exact path.
10326    ///
10327    /// Tracking checkouts retain the metadata identity independently from file
10328    /// content, allowing unrelated content changes to rebase safely.
10329    ///
10330    /// # Errors
10331    ///
10332    /// Returns measured absence, path, decode, dependency, storage,
10333    /// cancellation, or bounded-work failures.
10334    pub async fn read_metadata(
10335        &mut self,
10336        path: &NamespacePath,
10337        budget: WorkBudget,
10338        cancellation: &CancellationToken,
10339    ) -> FsResult<FileMetadata> {
10340        let lookup = self
10341            .lookup_no_follow_with_metadata(path, budget, cancellation)
10342            .await?;
10343        let value = lookup
10344            .value
10345            .ok_or_else(|| OperationFailure::new(FsError::NotFound, lookup.work))?
10346            .metadata;
10347        Ok(FsReceipt {
10348            value,
10349            work: lookup.work,
10350        })
10351    }
10352
10353    /// Reads one exact named attribute without reading unrelated attribute
10354    /// payloads or pages.
10355    ///
10356    /// # Errors
10357    ///
10358    /// Returns measured path, metadata, attribute-tree, blob, cancellation,
10359    /// storage, allocation, or bounded-work failures.
10360    pub async fn read_named_attribute(
10361        &mut self,
10362        path: &NamespacePath,
10363        name: &AttributeName,
10364        budget: WorkBudget,
10365        cancellation: &CancellationToken,
10366    ) -> FsResult<Option<Bytes>> {
10367        let metadata = self.read_metadata(path, budget, cancellation).await?;
10368        let mut work = metadata.work;
10369        let MetadataField::Value(root) = metadata.value.named_attributes else {
10370            return Ok(FsReceipt { value: None, work });
10371        };
10372        let lookup = lookup_attribute_async(
10373            &self.volume.fs.inner.objects,
10374            root,
10375            name,
10376            decode_limits(self.volume.config),
10377            remaining(work, budget)?,
10378            cancellation,
10379        )
10380        .await
10381        .map_err(|failure| failure.map_with_prior_work(work, FsError::AttributeLookup))?;
10382        work = add(work, lookup.work)?;
10383        let Some(entry) = lookup.entry else {
10384            return Ok(FsReceipt { value: None, work });
10385        };
10386        if entry.value_bytes > self.volume.config.limits.maximum_read_bytes {
10387            return Err(OperationFailure::new(
10388                FsError::FileRead(FileRangeReadError::InvalidRange),
10389                work,
10390            ));
10391        }
10392        let read = read_blob_range_async(
10393            &self.volume.fs.inner.objects,
10394            entry.value,
10395            ByteRange {
10396                offset: 0,
10397                length: entry.value_bytes,
10398            },
10399            decode_limits(self.volume.config),
10400            remaining(work, budget)?,
10401            cancellation,
10402        )
10403        .await
10404        .map_err(|failure| failure.map_with_prior_work(work, FsError::BlobRead))?;
10405        work = add(work, read.work)?;
10406        Ok(FsReceipt {
10407            value: Some(read.bytes),
10408            work,
10409        })
10410    }
10411
10412    /// Lists one bounded page of exact named-attribute identities without
10413    /// reading any attribute payload bytes.
10414    ///
10415    /// # Errors
10416    ///
10417    /// Returns measured path, metadata, pagination, cancellation, storage,
10418    /// allocation, or bounded-work failures.
10419    pub async fn list_named_attributes(
10420        &mut self,
10421        path: &NamespacePath,
10422        after: Option<&AttributeName>,
10423        maximum_entries: u32,
10424        budget: WorkBudget,
10425        cancellation: &CancellationToken,
10426    ) -> FsResult<AttributeListing> {
10427        let metadata = self.read_metadata(path, budget, cancellation).await?;
10428        let mut work = metadata.work;
10429        let MetadataField::Value(root) = metadata.value.named_attributes else {
10430            return Ok(FsReceipt {
10431                value: AttributeListing {
10432                    entries: Vec::new(),
10433                    has_more: false,
10434                    next_residency: None,
10435                    work,
10436                },
10437                work,
10438            });
10439        };
10440        let mut listing = list_attributes_async(
10441            &self.volume.fs.inner.objects,
10442            root,
10443            after,
10444            maximum_entries,
10445            decode_limits(self.volume.config),
10446            remaining(work, budget)?,
10447            cancellation,
10448        )
10449        .await
10450        .map_err(|failure| failure.map_with_prior_work(work, FsError::AttributeList))?;
10451        work = add(work, listing.work)?;
10452        listing.work = work;
10453        Ok(FsReceipt {
10454            value: listing,
10455            work,
10456        })
10457    }
10458
10459    /// Inserts or replaces one named attribute and atomically updates the
10460    /// path's metadata reference in the checkout candidate.
10461    ///
10462    /// Immutable payload and attribute pages are staged first; a rejected
10463    /// precondition can leave only unreachable authenticated objects.
10464    ///
10465    /// # Errors
10466    ///
10467    /// Returns measured precondition, path, blob, attribute-tree, metadata,
10468    /// mutation, cancellation, storage, allocation, or bounded-work failures.
10469    pub async fn write_named_attribute(
10470        &mut self,
10471        path: NamespacePath,
10472        name: AttributeName,
10473        value: Bytes,
10474        mode: NamedAttributeWriteMode,
10475        budget: WorkBudget,
10476        cancellation: &CancellationToken,
10477    ) -> FsResult<()> {
10478        let metadata = self.read_metadata(&path, budget, cancellation).await?;
10479        let mut work = metadata.work;
10480        let mut metadata_value = metadata.value;
10481        let root = match metadata_value.named_attributes {
10482            MetadataField::Unavailable => {
10483                let empty = self
10484                    .stage_empty_attributes(remaining(work, budget)?, cancellation)
10485                    .await
10486                    .map_err(|failure| failure.map_with_prior_work(work, std::convert::identity))?;
10487                work = add(work, empty.work)?;
10488                empty.value
10489            }
10490            MetadataField::Value(root) => root,
10491        };
10492        let current = lookup_attribute_async(
10493            &self.volume.fs.inner.objects,
10494            root,
10495            &name,
10496            decode_limits(self.volume.config),
10497            remaining(work, budget)?,
10498            cancellation,
10499        )
10500        .await
10501        .map_err(|failure| failure.map_with_prior_work(work, FsError::AttributeLookup))?;
10502        work = add(work, current.work)?;
10503        match (mode, current.entry.as_ref()) {
10504            (NamedAttributeWriteMode::Create, Some(_))
10505            | (NamedAttributeWriteMode::Replace, None) => {
10506                return Err(OperationFailure::new(FsError::CreationRejected, work));
10507            }
10508            _ => {}
10509        }
10510        let blob = self
10511            .stage_blob(value, remaining(work, budget)?, cancellation)
10512            .await
10513            .map_err(|failure| failure.map_with_prior_work(work, std::convert::identity))?;
10514        work = add(work, blob.work)?;
10515        let replacement = AttributeEntry {
10516            name,
10517            value_bytes: blob.value.logical_bytes,
10518            value: blob.value.root,
10519        };
10520        let mutation = match current.entry {
10521            None => AttributeMutation::Insert(replacement),
10522            Some(expected) => AttributeMutation::Replace {
10523                expected,
10524                replacement,
10525            },
10526        };
10527        let changed = apply_attribute_mutations_async(
10528            &self.volume.fs.inner.objects,
10529            root,
10530            vec![mutation],
10531            1,
10532            decode_limits(self.volume.config),
10533            remaining(work, budget)?,
10534            cancellation,
10535        )
10536        .await
10537        .map_err(|failure| failure.map_with_prior_work(work, FsError::AttributeMutation))?;
10538        work = add(work, changed.work)?;
10539        metadata_value.named_attributes = MetadataField::Value(changed.root);
10540        let changed_metadata = self
10541            .set_metadata(path, metadata_value, remaining(work, budget)?, cancellation)
10542            .await
10543            .map_err(|failure| failure.map_with_prior_work(work, std::convert::identity))?;
10544        work = add(work, changed_metadata.work)?;
10545        Ok(FsReceipt { value: (), work })
10546    }
10547
10548    /// Removes one existing named attribute and atomically updates metadata.
10549    ///
10550    /// # Errors
10551    ///
10552    /// Returns [`FsError::NotFound`] for authenticated absence and otherwise
10553    /// the same bounded failures as [`Self::write_named_attribute`].
10554    pub async fn remove_named_attribute(
10555        &mut self,
10556        path: NamespacePath,
10557        name: AttributeName,
10558        budget: WorkBudget,
10559        cancellation: &CancellationToken,
10560    ) -> FsResult<()> {
10561        let metadata = self.read_metadata(&path, budget, cancellation).await?;
10562        let mut work = metadata.work;
10563        let mut metadata_value = metadata.value;
10564        let MetadataField::Value(root) = metadata_value.named_attributes else {
10565            return Err(OperationFailure::new(FsError::NotFound, work));
10566        };
10567        let current = lookup_attribute_async(
10568            &self.volume.fs.inner.objects,
10569            root,
10570            &name,
10571            decode_limits(self.volume.config),
10572            remaining(work, budget)?,
10573            cancellation,
10574        )
10575        .await
10576        .map_err(|failure| failure.map_with_prior_work(work, FsError::AttributeLookup))?;
10577        work = add(work, current.work)?;
10578        let expected = current
10579            .entry
10580            .ok_or_else(|| OperationFailure::new(FsError::NotFound, work))?;
10581        let changed = apply_attribute_mutations_async(
10582            &self.volume.fs.inner.objects,
10583            root,
10584            vec![AttributeMutation::Remove {
10585                name,
10586                expected: Some(expected),
10587            }],
10588            1,
10589            decode_limits(self.volume.config),
10590            remaining(work, budget)?,
10591            cancellation,
10592        )
10593        .await
10594        .map_err(|failure| failure.map_with_prior_work(work, FsError::AttributeMutation))?;
10595        work = add(work, changed.work)?;
10596        metadata_value.named_attributes = MetadataField::Value(changed.root);
10597        let changed_metadata = self
10598            .set_metadata(path, metadata_value, remaining(work, budget)?, cancellation)
10599            .await
10600            .map_err(|failure| failure.map_with_prior_work(work, std::convert::identity))?;
10601        work = add(work, changed_metadata.work)?;
10602        Ok(FsReceipt { value: (), work })
10603    }
10604
10605    /// Plans the sparse physical representation of one exact regular-file range.
10606    ///
10607    /// `None` identifies tiny inline content; callers should use
10608    /// [`Self::read_file_range`] for that bounded body. A returned plan contains
10609    /// holes, allocated-zero spans, and immutable-content spans without reading
10610    /// content blobs or unrelated extent pages.
10611    ///
10612    /// # Errors
10613    ///
10614    /// Returns measured path, kind, range, authentication, dependency,
10615    /// cancellation, storage, allocation, excessive-span, or bounded-work
10616    /// failures.
10617    pub async fn plan_file_extents(
10618        &mut self,
10619        path: &NamespacePath,
10620        range: ByteRange,
10621        maximum_spans: u32,
10622        budget: WorkBudget,
10623        cancellation: &CancellationToken,
10624    ) -> FsResult<Option<ExtentPlan>> {
10625        if maximum_spans == 0 || range.length > self.volume.config.limits.maximum_generation_bytes {
10626            return Err(OperationFailure::before_work(FsError::FileRead(
10627                FileRangeReadError::InvalidRange,
10628            )));
10629        }
10630        let lookup = self
10631            .lookup_no_follow_observed(path, budget, cancellation, false)
10632            .await?;
10633        let mut work = lookup.work;
10634        let record = lookup
10635            .value
10636            .record
10637            .ok_or_else(|| OperationFailure::new(FsError::NotFound, work))?;
10638        let (logical_bytes, extents) = match record.payload {
10639            FilePayload::InlineRegular(data) => {
10640                let logical_bytes = u64::try_from(data.as_bytes().len()).unwrap_or(u64::MAX);
10641                validate_planned_file_range(logical_bytes, range, work)?;
10642                if self.mode.consistency != ConsistencyMode::Pinned && range.length != 0 {
10643                    work = self
10644                        .observe_base_regular_range(
10645                            record.file_id,
10646                            range,
10647                            work,
10648                            budget,
10649                            cancellation,
10650                        )
10651                        .await?;
10652                }
10653                return Ok(FsReceipt { value: None, work });
10654            }
10655            FilePayload::Regular {
10656                logical_bytes,
10657                extents,
10658            } => (logical_bytes, extents),
10659            FilePayload::Directory { .. }
10660            | FilePayload::SymbolicLink { .. }
10661            | FilePayload::Device { .. }
10662            | FilePayload::Empty
10663            | FilePayload::ReparsePoint { .. } => {
10664                return Err(OperationFailure::new(
10665                    FsError::FileRead(FileRangeReadError::NotRegular),
10666                    work,
10667                ));
10668            }
10669        };
10670        let mut plan = plan_extent_range_async(
10671            &self.volume.fs.inner.objects,
10672            ExtentRangeRequest {
10673                root: extents,
10674                file_size: logical_bytes,
10675                range,
10676                maximum_spans,
10677                limits: decode_limits(self.volume.config),
10678                budget: remaining(work, budget)?,
10679            },
10680            cancellation,
10681        )
10682        .await
10683        .map_err(|failure| {
10684            failure.map_with_prior_work(work, |error| {
10685                FsError::FileRead(FileRangeReadError::Extent(error))
10686            })
10687        })?;
10688        work = add(work, plan.work)?;
10689        if self.mode.consistency != ConsistencyMode::Pinned && range.length != 0 {
10690            work = self
10691                .observe_base_regular_range(record.file_id, range, work, budget, cancellation)
10692                .await?;
10693        }
10694        plan.work = work;
10695        Ok(FsReceipt {
10696            value: Some(plan),
10697            work,
10698        })
10699    }
10700
10701    /// Finds the first sparse data or hole boundary after one logical offset.
10702    ///
10703    /// Namespace and extent roots are resolved once. The extent traversal then
10704    /// advances monotonically across authenticated leaf frontiers without
10705    /// reading file bodies or restarting at the root.
10706    ///
10707    /// # Errors
10708    ///
10709    /// Returns measured absence, non-regular kind, invalid offset, malformed
10710    /// extent graph, cancellation, backend, dependency, or bounded-work
10711    /// failures.
10712    pub async fn seek_file_extent(
10713        &mut self,
10714        path: &NamespacePath,
10715        offset: u64,
10716        target: ExtentSeekTarget,
10717        budget: WorkBudget,
10718        cancellation: &CancellationToken,
10719    ) -> FsResult<Option<u64>> {
10720        let lookup = self
10721            .lookup_no_follow_observed(path, budget, cancellation, false)
10722            .await?;
10723        let mut work = lookup.work;
10724        let record = lookup
10725            .value
10726            .record
10727            .ok_or_else(|| OperationFailure::new(FsError::NotFound, work))?;
10728        let value = match record.payload {
10729            FilePayload::InlineRegular(data) => {
10730                let logical_bytes = u64::try_from(data.as_bytes().len()).unwrap_or(u64::MAX);
10731                if offset > logical_bytes {
10732                    None
10733                } else {
10734                    match target {
10735                        ExtentSeekTarget::Data => (offset < logical_bytes).then_some(offset),
10736                        ExtentSeekTarget::Hole => Some(logical_bytes),
10737                    }
10738                }
10739            }
10740            FilePayload::Regular {
10741                logical_bytes,
10742                extents,
10743            } => {
10744                if offset > logical_bytes {
10745                    None
10746                } else {
10747                    let receipt = seek_extent_async(
10748                        &self.volume.fs.inner.objects,
10749                        ExtentSeekRequest {
10750                            root: extents,
10751                            file_size: logical_bytes,
10752                            offset,
10753                            target,
10754                            limits: decode_limits(self.volume.config),
10755                            budget: remaining(work, budget)?,
10756                        },
10757                        cancellation,
10758                    )
10759                    .await
10760                    .map_err(|failure| {
10761                        failure.map_with_prior_work(work, |error| {
10762                            FsError::FileRead(FileRangeReadError::Extent(error))
10763                        })
10764                    })?;
10765                    work = add(work, receipt.work)?;
10766                    receipt.value
10767                }
10768            }
10769            FilePayload::Directory { .. }
10770            | FilePayload::SymbolicLink { .. }
10771            | FilePayload::Empty
10772            | FilePayload::Device { .. }
10773            | FilePayload::ReparsePoint { .. } => {
10774                return Err(OperationFailure::new(
10775                    FsError::FileRead(FileRangeReadError::NotRegular),
10776                    work,
10777                ));
10778            }
10779        };
10780        if self.mode.consistency != ConsistencyMode::Pinned {
10781            let region = DependencyRegion::SparseSeek {
10782                file_id: record.file_id,
10783                offset,
10784                target,
10785            };
10786            let probe = AuthenticatedGenerationProbe::new(
10787                &self.volume.fs.inner.objects,
10788                probe_limits(self.volume.config),
10789            )
10790            .map_err(|error| OperationFailure::new(error.into(), work))?;
10791            let state = probe
10792                .probe_async(
10793                    GenerationId::new(self.base_generation_root.digest),
10794                    &region,
10795                    remaining(work, budget)?,
10796                    cancellation,
10797                )
10798                .await
10799                .map_err(|failure| failure.map_with_prior_work(work, FsError::Probe))?;
10800            work = add(work, state.work)?;
10801            self.dependencies
10802                .extend_observations(
10803                    vec![Dependency {
10804                        region,
10805                        expected: state.value,
10806                    }],
10807                    self.volume.config.limits.maximum_checkout_dependencies,
10808                )
10809                .map_err(|error| OperationFailure::new(error.into(), work))?;
10810        }
10811        Ok(FsReceipt { value, work })
10812    }
10813
10814    /// Reads one exact logical regular-file range without materializing holes
10815    /// or unrelated extent and blob subtrees.
10816    ///
10817    /// # Errors
10818    ///
10819    /// Returns measured path, kind, range, authentication, dependency,
10820    /// cancellation, storage, allocation, or bounded-work failures.
10821    pub async fn read_file_range(
10822        &mut self,
10823        path: &NamespacePath,
10824        range: ByteRange,
10825        budget: WorkBudget,
10826        cancellation: &CancellationToken,
10827    ) -> FsResult<FileRangeRead> {
10828        if range.length > self.volume.config.limits.maximum_read_bytes {
10829            return Err(OperationFailure::before_work(FsError::FileRead(
10830                FileRangeReadError::InvalidRange,
10831            )));
10832        }
10833        let lookup = self
10834            .lookup_no_follow_observed(path, budget, cancellation, false)
10835            .await?;
10836        let mut work = lookup.work;
10837        let record = lookup
10838            .value
10839            .record
10840            .ok_or_else(|| OperationFailure::new(FsError::NotFound, work))?;
10841        let mut read = read_file_range_async(
10842            &self.volume.fs.inner.objects,
10843            FileRangeRequest {
10844                record,
10845                range,
10846                maximum_spans: self.volume.config.limits.maximum_directory_page_entries,
10847                limits: decode_limits(self.volume.config),
10848                budget: remaining(work, budget)?,
10849            },
10850            cancellation,
10851        )
10852        .await
10853        .map_err(|failure| failure.map_with_prior_work(work, FsError::FileRead))?;
10854        work = add(work, read.work)?;
10855        if self.mode.consistency != ConsistencyMode::Pinned && range.length != 0 {
10856            work = self
10857                .observe_base_regular_range(record.file_id, range, work, budget, cancellation)
10858                .await?;
10859        }
10860        read.work = work;
10861        Ok(FsReceipt { value: read, work })
10862    }
10863    async fn lookup_file_record_by_id(
10864        &self,
10865        file_id: FileId,
10866        budget: WorkBudget,
10867        cancellation: &CancellationToken,
10868    ) -> FsResult<Option<FileRecord>> {
10869        let lookup = lookup_file_record_async(
10870            &self.volume.fs.inner.objects,
10871            self.root.file_table,
10872            file_id,
10873            decode_limits(self.volume.config),
10874            budget,
10875            cancellation,
10876        )
10877        .await
10878        .map_err(|failure| {
10879            OperationFailure::new(FsError::FileRecord(failure.error), *failure.work)
10880        })?;
10881        Ok(FsReceipt {
10882            value: lookup.record,
10883            work: lookup.work,
10884        })
10885    }
10886
10887    async fn observe_identity_region(
10888        &mut self,
10889        region: DependencyRegion,
10890        work: WorkCounters,
10891        budget: WorkBudget,
10892        cancellation: &CancellationToken,
10893    ) -> Result<WorkCounters, OperationFailure<FsError>> {
10894        self.observe_identity_regions(vec![region], work, budget, cancellation)
10895            .await
10896    }
10897
10898    async fn observe_identity_regions(
10899        &mut self,
10900        regions: Vec<DependencyRegion>,
10901        mut work: WorkCounters,
10902        budget: WorkBudget,
10903        cancellation: &CancellationToken,
10904    ) -> Result<WorkCounters, OperationFailure<FsError>> {
10905        let captured = capture_dependencies_async(
10906            &self.volume.fs.inner.objects,
10907            &self.base_generation_root,
10908            self.volume.config,
10909            regions,
10910            remaining(work, budget)?,
10911            cancellation,
10912        )
10913        .await
10914        .map_err(|failure| failure.map_with_prior_work(work, std::convert::identity))?;
10915        work = add(work, captured.work)?;
10916        self.dependencies
10917            .extend_observations(
10918                captured.value,
10919                self.volume.config.limits.maximum_checkout_dependencies,
10920            )
10921            .map_err(|error| OperationFailure::new(error.into(), work))?;
10922        Ok(work)
10923    }
10924
10925    async fn observe_base_regular_range(
10926        &mut self,
10927        file_id: FileId,
10928        range: ByteRange,
10929        mut work: WorkCounters,
10930        budget: WorkBudget,
10931        cancellation: &CancellationToken,
10932    ) -> Result<WorkCounters, OperationFailure<FsError>> {
10933        let lookup = lookup_file_record_async(
10934            &self.volume.fs.inner.objects,
10935            self.base_root.file_table,
10936            file_id,
10937            decode_limits(self.volume.config),
10938            remaining(work, budget)?,
10939            cancellation,
10940        )
10941        .await
10942        .map_err(|failure| failure.map_with_prior_work(work, FsError::FileRecord))?;
10943        work = add(work, lookup.work)?;
10944        let regions = regular_range_regions(lookup.record, range.offset, range.length)
10945            .into_iter()
10946            .flatten()
10947            .collect();
10948        self.observe_identity_regions(regions, work, budget, cancellation)
10949            .await
10950    }
10951
10952    async fn observe_base_metadata_ids(
10953        &mut self,
10954        file_ids: &[FileId],
10955        mut work: WorkCounters,
10956        budget: WorkBudget,
10957        cancellation: &CancellationToken,
10958    ) -> Result<WorkCounters, OperationFailure<FsError>> {
10959        if file_ids.is_empty() {
10960            return Ok(work);
10961        }
10962        let maximum = u32::try_from(file_ids.len()).unwrap_or(u32::MAX);
10963        let records = lookup_file_records_async(
10964            &self.volume.fs.inner.objects,
10965            self.base_root.file_table,
10966            file_ids,
10967            maximum,
10968            decode_limits(self.volume.config),
10969            remaining(work, budget)?,
10970            cancellation,
10971        )
10972        .await
10973        .map_err(|failure| failure.map_with_prior_work(work, FsError::FileRecord))?;
10974        work = add(work, records.work)?;
10975        let mut dependencies = Vec::new();
10976        dependencies
10977            .try_reserve_exact(file_ids.len())
10978            .map_err(|_| OperationFailure::new(FsError::PendingMutationAllocationFailed, work))?;
10979        dependencies.extend(
10980            file_ids
10981                .iter()
10982                .zip(records.records)
10983                .map(|(file_id, record)| Dependency {
10984                    region: DependencyRegion::Metadata(*file_id),
10985                    expected: record.map_or(DependencyState::Absent, |record| {
10986                        DependencyState::Present(record.metadata.digest)
10987                    }),
10988                }),
10989        );
10990        self.dependencies
10991            .extend_observations(
10992                dependencies,
10993                self.volume.config.limits.maximum_checkout_dependencies,
10994            )
10995            .map_err(|error| OperationFailure::new(error.into(), work))?;
10996        Ok(work)
10997    }
10998}
10999
11000async fn stage_content_for_volume<
11001    A: AsyncAuthorityStore,
11002    O: AsyncObjectStore,
11003    R: AsyncBlobSource,
11004>(
11005    volume: &Volume<A, O>,
11006    source: &mut R,
11007    maximum_source_bytes: u64,
11008    budget: WorkBudget,
11009    cancellation: &CancellationToken,
11010) -> FsResult<StagedContent> {
11011    if maximum_source_bytes == 0
11012        || maximum_source_bytes > volume.config.limits.maximum_generation_bytes
11013    {
11014        return Err(OperationFailure::before_work(FsError::FileRead(
11015            FileRangeReadError::InvalidRange,
11016        )));
11017    }
11018    let chunk_bytes = u32::try_from(volume.config.limits.maximum_object_bytes.min(1024 * 1024))
11019        .unwrap_or(u32::MAX)
11020        .max(1);
11021    let page_bytes = u32::try_from(volume.config.limits.maximum_object_bytes)
11022        .unwrap_or(u32::MAX)
11023        .max(1);
11024    let blob = build_blob_async(
11025        &volume.fs.inner.objects,
11026        source,
11027        BlobBuildOptions {
11028            chunk_bytes,
11029            page_items: volume.config.limits.maximum_directory_page_entries,
11030            page_bytes,
11031            maximum_blob_bytes: maximum_source_bytes,
11032        },
11033        budget,
11034        cancellation,
11035    )
11036    .await
11037    .map_err(|failure| OperationFailure::new(failure.error.into(), *failure.work))?;
11038    Ok(FsReceipt {
11039        value: StagedContent {
11040            root: blob.root,
11041            logical_bytes: blob.logical_bytes,
11042        },
11043        work: blob.work,
11044    })
11045}
11046
11047impl<A: AsyncAuthorityStore, O: AsyncObjectStore> DetachedFile<A, O> {
11048    pub(crate) fn from_record(volume: Volume<A, O>, record: FileRecord) -> Self {
11049        Self { volume, record }
11050    }
11051
11052    #[cfg(feature = "native-mount")]
11053    pub(crate) const fn record(&self) -> FileRecord {
11054        self.record
11055    }
11056
11057    pub(crate) async fn read_symbolic_link(
11058        &self,
11059        budget: WorkBudget,
11060        cancellation: &CancellationToken,
11061    ) -> FsResult<Bytes> {
11062        read_symbolic_link_record(&self.volume, self.record, budget, cancellation).await
11063    }
11064    /// Stable file identity retained across last-link removal.
11065    #[must_use]
11066    pub const fn file_id(&self) -> FileId {
11067        self.record.file_id
11068    }
11069
11070    /// Current detached logical file length.
11071    #[must_use]
11072    pub fn logical_bytes(&self) -> u64 {
11073        match self.record.payload {
11074            FilePayload::InlineRegular(data) => {
11075                u64::try_from(data.as_bytes().len()).unwrap_or(u64::MAX)
11076            }
11077            FilePayload::Regular { logical_bytes, .. } => logical_bytes,
11078            _ => 0,
11079        }
11080    }
11081
11082    /// Finds the first sparse data or hole boundary without a namespace binding.
11083    ///
11084    /// # Errors
11085    ///
11086    /// Returns measured malformed-extent, cancellation, backend, allocation,
11087    /// or bounded-work failures.
11088    pub async fn seek(
11089        &self,
11090        offset: u64,
11091        target: ExtentSeekTarget,
11092        budget: WorkBudget,
11093        cancellation: &CancellationToken,
11094    ) -> FsResult<Option<u64>> {
11095        let value = match self.record.payload {
11096            FilePayload::InlineRegular(data) => {
11097                let logical_bytes = u64::try_from(data.as_bytes().len()).unwrap_or(u64::MAX);
11098                if offset > logical_bytes {
11099                    None
11100                } else {
11101                    match target {
11102                        ExtentSeekTarget::Data => (offset < logical_bytes).then_some(offset),
11103                        ExtentSeekTarget::Hole => Some(logical_bytes),
11104                    }
11105                }
11106            }
11107            FilePayload::Regular {
11108                logical_bytes,
11109                extents,
11110            } => {
11111                if offset > logical_bytes {
11112                    None
11113                } else {
11114                    return seek_extent_async(
11115                        &self.volume.fs.inner.objects,
11116                        ExtentSeekRequest {
11117                            root: extents,
11118                            file_size: logical_bytes,
11119                            offset,
11120                            target,
11121                            limits: decode_limits(self.volume.config),
11122                            budget,
11123                        },
11124                        cancellation,
11125                    )
11126                    .await
11127                    .map_err(|failure| {
11128                        OperationFailure::new(
11129                            FsError::FileRead(FileRangeReadError::Extent(failure.error)),
11130                            *failure.work,
11131                        )
11132                    });
11133                }
11134            }
11135            FilePayload::Directory { .. }
11136            | FilePayload::SymbolicLink { .. }
11137            | FilePayload::Empty
11138            | FilePayload::Device { .. }
11139            | FilePayload::ReparsePoint { .. } => {
11140                return Err(OperationFailure::before_work(FsError::FileRead(
11141                    FileRangeReadError::NotRegular,
11142                )));
11143            }
11144        };
11145        Ok(FsReceipt {
11146            value,
11147            work: WorkCounters::default(),
11148        })
11149    }
11150
11151    /// Replaces complete metadata on this detached file only.
11152    ///
11153    /// # Errors
11154    ///
11155    /// Returns measured encoding, storage, cancellation, allocation, or
11156    /// bounded-work failures. A failed operation leaves the detached record
11157    /// unchanged.
11158    pub async fn set_metadata(
11159        &mut self,
11160        metadata: FileMetadata,
11161        budget: WorkBudget,
11162        cancellation: &CancellationToken,
11163    ) -> FsResult<()> {
11164        let encoded = encode_file_metadata(metadata)
11165            .map_err(|error| OperationFailure::before_work(error.into()))?;
11166        let (metadata, work) = self
11167            .volume
11168            .fs
11169            .put_encoded(
11170                ObjectKind::Metadata,
11171                encoded,
11172                WorkCounters::default(),
11173                budget,
11174                cancellation,
11175            )
11176            .await?;
11177        self.record.metadata = metadata;
11178        Ok(FsReceipt { value: (), work })
11179    }
11180
11181    /// Replaces complete metadata and optionally logical size as one detached
11182    /// record transition.
11183    ///
11184    /// Immutable metadata bytes may be staged before validation finishes, but
11185    /// the visible detached record changes only after every requested update
11186    /// succeeds.
11187    ///
11188    /// # Errors
11189    ///
11190    /// Returns measured encoding, storage, sparse mutation, cancellation,
11191    /// allocation, or bounded-work failures.
11192    pub async fn set_attributes(
11193        &mut self,
11194        metadata: FileMetadata,
11195        logical_bytes: Option<u64>,
11196        budget: WorkBudget,
11197        cancellation: &CancellationToken,
11198    ) -> FsResult<()> {
11199        let encoded = encode_file_metadata(metadata)
11200            .map_err(|error| OperationFailure::before_work(error.into()))?;
11201        let (metadata_id, mut work) = self
11202            .volume
11203            .fs
11204            .put_encoded(
11205                ObjectKind::Metadata,
11206                encoded,
11207                WorkCounters::default(),
11208                budget,
11209                cancellation,
11210            )
11211            .await?;
11212        let payload = if let Some(logical_bytes) = logical_bytes {
11213            let resized = apply_regular_mutation_async(
11214                &self.volume.fs.inner.objects,
11215                self.record.payload,
11216                RegularMutation::Resize { logical_bytes },
11217                self.volume.config,
11218                remaining(work, budget)?,
11219                cancellation,
11220            )
11221            .await
11222            .map_err(|failure| failure.map_with_prior_work(work, FsError::DetachedMutation))?;
11223            work = add(work, resized.work)?;
11224            resized.payload
11225        } else {
11226            self.record.payload
11227        };
11228        self.record.metadata = metadata_id;
11229        self.record.payload = payload;
11230        Ok(FsReceipt { value: (), work })
11231    }
11232
11233    /// Reads one exact named attribute from the detached metadata frontier.
11234    ///
11235    /// # Errors
11236    ///
11237    /// Returns measured metadata, attribute-tree, blob, storage,
11238    /// authentication, cancellation, allocation, or bounded-work failures.
11239    pub async fn read_named_attribute(
11240        &self,
11241        name: &AttributeName,
11242        budget: WorkBudget,
11243        cancellation: &CancellationToken,
11244    ) -> FsResult<Option<Bytes>> {
11245        let metadata = self.read_metadata(budget, cancellation).await?;
11246        let mut work = metadata.work;
11247        let MetadataField::Value(root) = metadata.value.named_attributes else {
11248            return Ok(FsReceipt { value: None, work });
11249        };
11250        let lookup = lookup_attribute_async(
11251            &self.volume.fs.inner.objects,
11252            root,
11253            name,
11254            decode_limits(self.volume.config),
11255            remaining(work, budget)?,
11256            cancellation,
11257        )
11258        .await
11259        .map_err(|failure| failure.map_with_prior_work(work, FsError::AttributeLookup))?;
11260        work = add(work, lookup.work)?;
11261        let Some(entry) = lookup.entry else {
11262            return Ok(FsReceipt { value: None, work });
11263        };
11264        if entry.value_bytes > self.volume.config.limits.maximum_read_bytes {
11265            return Err(OperationFailure::new(
11266                FsError::FileRead(FileRangeReadError::InvalidRange),
11267                work,
11268            ));
11269        }
11270        let read = read_blob_range_async(
11271            &self.volume.fs.inner.objects,
11272            entry.value,
11273            ByteRange {
11274                offset: 0,
11275                length: entry.value_bytes,
11276            },
11277            decode_limits(self.volume.config),
11278            remaining(work, budget)?,
11279            cancellation,
11280        )
11281        .await
11282        .map_err(|failure| failure.map_with_prior_work(work, FsError::BlobRead))?;
11283        work = add(work, read.work)?;
11284        Ok(FsReceipt {
11285            value: Some(read.bytes),
11286            work,
11287        })
11288    }
11289
11290    /// Lists one bounded page of detached named-attribute identities.
11291    ///
11292    /// # Errors
11293    ///
11294    /// Returns measured metadata, pagination, storage, authentication,
11295    /// cancellation, allocation, or bounded-work failures.
11296    pub async fn list_named_attributes(
11297        &self,
11298        after: Option<&AttributeName>,
11299        maximum_entries: u32,
11300        budget: WorkBudget,
11301        cancellation: &CancellationToken,
11302    ) -> FsResult<AttributeListing> {
11303        let metadata = self.read_metadata(budget, cancellation).await?;
11304        let mut work = metadata.work;
11305        let MetadataField::Value(root) = metadata.value.named_attributes else {
11306            let value = AttributeListing {
11307                entries: Vec::new(),
11308                has_more: false,
11309                next_residency: None,
11310                work,
11311            };
11312            return Ok(FsReceipt { value, work });
11313        };
11314        let mut listing = list_attributes_async(
11315            &self.volume.fs.inner.objects,
11316            root,
11317            after,
11318            maximum_entries,
11319            decode_limits(self.volume.config),
11320            remaining(work, budget)?,
11321            cancellation,
11322        )
11323        .await
11324        .map_err(|failure| failure.map_with_prior_work(work, FsError::AttributeList))?;
11325        work = add(work, listing.work)?;
11326        listing.work = work;
11327        Ok(FsReceipt {
11328            value: listing,
11329            work,
11330        })
11331    }
11332
11333    /// Inserts or replaces one detached named attribute atomically.
11334    ///
11335    /// # Errors
11336    ///
11337    /// Returns measured precondition, metadata, blob, attribute-tree, storage,
11338    /// cancellation, allocation, or bounded-work failures.
11339    pub async fn write_named_attribute(
11340        &mut self,
11341        name: AttributeName,
11342        value: Bytes,
11343        mode: NamedAttributeWriteMode,
11344        budget: WorkBudget,
11345        cancellation: &CancellationToken,
11346    ) -> FsResult<FileMetadata> {
11347        let metadata = self.read_metadata(budget, cancellation).await?;
11348        let mut work = metadata.work;
11349        let mut metadata_value = metadata.value;
11350        let root = match metadata_value.named_attributes {
11351            MetadataField::Unavailable => {
11352                let encoded = encode_attribute_page(
11353                    &AttributePage::Leaf(Vec::new()),
11354                    self.volume.config.limits.maximum_directory_page_entries,
11355                )
11356                .map_err(|error| OperationFailure::new(error.into(), work))?;
11357                let stored = self
11358                    .volume
11359                    .fs
11360                    .put_encoded(
11361                        ObjectKind::AttributePage,
11362                        encoded,
11363                        work,
11364                        budget,
11365                        cancellation,
11366                    )
11367                    .await?;
11368                work = stored.1;
11369                stored.0
11370            }
11371            MetadataField::Value(root) => root,
11372        };
11373        let current = lookup_attribute_async(
11374            &self.volume.fs.inner.objects,
11375            root,
11376            &name,
11377            decode_limits(self.volume.config),
11378            remaining(work, budget)?,
11379            cancellation,
11380        )
11381        .await
11382        .map_err(|failure| failure.map_with_prior_work(work, FsError::AttributeLookup))?;
11383        work = add(work, current.work)?;
11384        match (mode, current.entry.as_ref()) {
11385            (NamedAttributeWriteMode::Create, Some(_))
11386            | (NamedAttributeWriteMode::Replace, None) => {
11387                return Err(OperationFailure::new(FsError::CreationRejected, work));
11388            }
11389            _ => {}
11390        }
11391        let blob = self
11392            .stage_blob(value, remaining(work, budget)?, cancellation)
11393            .await
11394            .map_err(|failure| failure.map_with_prior_work(work, std::convert::identity))?;
11395        work = add(work, blob.work)?;
11396        let replacement = AttributeEntry {
11397            name,
11398            value_bytes: blob.value.logical_bytes,
11399            value: blob.value.root,
11400        };
11401        let mutation = match current.entry {
11402            None => AttributeMutation::Insert(replacement),
11403            Some(expected) => AttributeMutation::Replace {
11404                expected,
11405                replacement,
11406            },
11407        };
11408        let changed = apply_attribute_mutations_async(
11409            &self.volume.fs.inner.objects,
11410            root,
11411            vec![mutation],
11412            1,
11413            decode_limits(self.volume.config),
11414            remaining(work, budget)?,
11415            cancellation,
11416        )
11417        .await
11418        .map_err(|failure| failure.map_with_prior_work(work, FsError::AttributeMutation))?;
11419        work = add(work, changed.work)?;
11420        metadata_value.named_attributes = MetadataField::Value(changed.root);
11421        let metadata = self
11422            .set_metadata(metadata_value, remaining(work, budget)?, cancellation)
11423            .await
11424            .map_err(|failure| failure.map_with_prior_work(work, std::convert::identity))?;
11425        work = add(work, metadata.work)?;
11426        Ok(FsReceipt {
11427            value: metadata_value,
11428            work,
11429        })
11430    }
11431
11432    /// Removes one existing detached named attribute atomically.
11433    ///
11434    /// # Errors
11435    ///
11436    /// Returns authenticated absence or the same measured failures as
11437    /// [`Self::write_named_attribute`].
11438    pub async fn remove_named_attribute(
11439        &mut self,
11440        name: AttributeName,
11441        budget: WorkBudget,
11442        cancellation: &CancellationToken,
11443    ) -> FsResult<FileMetadata> {
11444        let metadata = self.read_metadata(budget, cancellation).await?;
11445        let mut work = metadata.work;
11446        let mut metadata_value = metadata.value;
11447        let MetadataField::Value(root) = metadata_value.named_attributes else {
11448            return Err(OperationFailure::new(FsError::NotFound, work));
11449        };
11450        let current = lookup_attribute_async(
11451            &self.volume.fs.inner.objects,
11452            root,
11453            &name,
11454            decode_limits(self.volume.config),
11455            remaining(work, budget)?,
11456            cancellation,
11457        )
11458        .await
11459        .map_err(|failure| failure.map_with_prior_work(work, FsError::AttributeLookup))?;
11460        work = add(work, current.work)?;
11461        let expected = current
11462            .entry
11463            .ok_or_else(|| OperationFailure::new(FsError::NotFound, work))?;
11464        let changed = apply_attribute_mutations_async(
11465            &self.volume.fs.inner.objects,
11466            root,
11467            vec![AttributeMutation::Remove {
11468                name,
11469                expected: Some(expected),
11470            }],
11471            1,
11472            decode_limits(self.volume.config),
11473            remaining(work, budget)?,
11474            cancellation,
11475        )
11476        .await
11477        .map_err(|failure| failure.map_with_prior_work(work, FsError::AttributeMutation))?;
11478        work = add(work, changed.work)?;
11479        metadata_value.named_attributes = MetadataField::Value(changed.root);
11480        let metadata = self
11481            .set_metadata(metadata_value, remaining(work, budget)?, cancellation)
11482            .await
11483            .map_err(|failure| failure.map_with_prior_work(work, std::convert::identity))?;
11484        work = add(work, metadata.work)?;
11485        Ok(FsReceipt {
11486            value: metadata_value,
11487            work,
11488        })
11489    }
11490
11491    /// Reads complete authenticated metadata for this detached file.
11492    ///
11493    /// # Errors
11494    ///
11495    /// Returns measured storage, authentication, cancellation, allocation, or
11496    /// bounded-work failures.
11497    pub async fn read_metadata(
11498        &self,
11499        budget: WorkBudget,
11500        cancellation: &CancellationToken,
11501    ) -> FsResult<FileMetadata> {
11502        let metadata = self
11503            .volume
11504            .fs
11505            .inner
11506            .objects
11507            .read(
11508                self.record.metadata,
11509                self.volume.config.limits.maximum_object_bytes,
11510                budget,
11511                cancellation,
11512            )
11513            .await
11514            .map_err(|failure| {
11515                OperationFailure::new(FsError::Object(failure.error), *failure.work)
11516            })?;
11517        let value = decode_file_metadata(&metadata.value, decode_limits(self.volume.config))
11518            .map_err(|error| OperationFailure::new(error.into(), metadata.work))?;
11519        Ok(FsReceipt {
11520            value,
11521            work: metadata.work,
11522        })
11523    }
11524
11525    async fn stage_blob(
11526        &self,
11527        bytes: Bytes,
11528        budget: WorkBudget,
11529        cancellation: &CancellationToken,
11530    ) -> FsResult<crate::kernel::BlobBuild> {
11531        if u64::try_from(bytes.len()).unwrap_or(u64::MAX)
11532            > self.volume.config.limits.maximum_read_bytes
11533        {
11534            return Err(OperationFailure::before_work(FsError::FileRead(
11535                FileRangeReadError::InvalidRange,
11536            )));
11537        }
11538        let maximum_blob_bytes = u64::try_from(bytes.len()).unwrap_or(u64::MAX).max(1);
11539        let mut source = std::io::Cursor::new(bytes);
11540        let staged = stage_content_for_volume(
11541            &self.volume,
11542            &mut source,
11543            maximum_blob_bytes,
11544            budget,
11545            cancellation,
11546        )
11547        .await?;
11548        Ok(FsReceipt {
11549            work: staged.work,
11550            value: crate::kernel::BlobBuild {
11551                root: staged.value.root,
11552                logical_bytes: staged.value.logical_bytes,
11553                work: staged.work,
11554            },
11555        })
11556    }
11557
11558    /// Reads one bounded sparse range without requiring a namespace binding.
11559    ///
11560    /// # Errors
11561    ///
11562    /// Returns typed range, storage, authentication, cancellation, allocation,
11563    /// or bounded-work failures.
11564    pub async fn read_range(
11565        &self,
11566        range: ByteRange,
11567        budget: WorkBudget,
11568        cancellation: &CancellationToken,
11569    ) -> FsResult<FileRangeRead> {
11570        if range.length > self.volume.config.limits.maximum_read_bytes {
11571            return Err(OperationFailure::before_work(FsError::FileRead(
11572                FileRangeReadError::InvalidRange,
11573            )));
11574        }
11575        let read = read_file_range_async(
11576            &self.volume.fs.inner.objects,
11577            FileRangeRequest {
11578                record: self.record,
11579                range,
11580                maximum_spans: self.volume.config.limits.maximum_mutations_per_batch,
11581                limits: decode_limits(self.volume.config),
11582                budget,
11583            },
11584            cancellation,
11585        )
11586        .await
11587        .map_err(|failure| OperationFailure::new(failure.error.into(), *failure.work))?;
11588        Ok(FsReceipt {
11589            work: read.work,
11590            value: read,
11591        })
11592    }
11593
11594    /// Writes caller bytes into the detached sparse file.
11595    ///
11596    /// # Errors
11597    ///
11598    /// Returns typed range, blob, mutation, storage, cancellation, allocation,
11599    /// or bounded-work failures.
11600    pub async fn write_range(
11601        &mut self,
11602        offset: u64,
11603        bytes: Bytes,
11604        budget: WorkBudget,
11605        cancellation: &CancellationToken,
11606    ) -> FsResult<()> {
11607        if bytes.is_empty() {
11608            return Ok(FsReceipt {
11609                value: (),
11610                work: WorkCounters::default(),
11611            });
11612        }
11613        if u64::try_from(bytes.len()).unwrap_or(u64::MAX)
11614            > self.volume.config.limits.maximum_read_bytes
11615        {
11616            return Err(OperationFailure::before_work(FsError::FileRead(
11617                FileRangeReadError::InvalidRange,
11618            )));
11619        }
11620        let maximum_blob_bytes = u64::try_from(bytes.len()).unwrap_or(u64::MAX);
11621        let mut source = std::io::Cursor::new(bytes);
11622        let blob = stage_content_for_volume(
11623            &self.volume,
11624            &mut source,
11625            maximum_blob_bytes,
11626            budget,
11627            cancellation,
11628        )
11629        .await?;
11630        let mut work = blob.work;
11631        let mutation = self
11632            .mutate_regular(
11633                RegularMutation::Write {
11634                    offset,
11635                    length: blob.value.logical_bytes,
11636                    content: blob.value.root,
11637                    content_offset: 0,
11638                },
11639                remaining(work, budget)?,
11640                cancellation,
11641            )
11642            .await
11643            .map_err(|failure| failure.map_with_prior_work(work, std::convert::identity))?;
11644        work = add(work, mutation.work)?;
11645        Ok(FsReceipt { value: (), work })
11646    }
11647
11648    /// Changes detached logical file length.
11649    ///
11650    /// # Errors
11651    ///
11652    /// Returns typed mutation, storage, cancellation, allocation, or work failures.
11653    pub async fn resize(
11654        &mut self,
11655        logical_bytes: u64,
11656        budget: WorkBudget,
11657        cancellation: &CancellationToken,
11658    ) -> FsResult<()> {
11659        self.mutate_regular(
11660            RegularMutation::Resize { logical_bytes },
11661            budget,
11662            cancellation,
11663        )
11664        .await
11665    }
11666
11667    /// Replaces one detached range with a hole or allocated zeros.
11668    ///
11669    /// # Errors
11670    ///
11671    /// Returns typed mutation, storage, cancellation, allocation, or work failures.
11672    pub async fn zero_range(
11673        &mut self,
11674        range: ByteRange,
11675        allocated: bool,
11676        extend: bool,
11677        budget: WorkBudget,
11678        cancellation: &CancellationToken,
11679    ) -> FsResult<()> {
11680        self.mutate_regular(
11681            RegularMutation::ZeroRange {
11682                offset: range.offset,
11683                length: range.length,
11684                allocated,
11685                extend,
11686            },
11687            budget,
11688            cancellation,
11689        )
11690        .await
11691    }
11692
11693    /// Allocates detached sparse holes while preserving content.
11694    ///
11695    /// # Errors
11696    ///
11697    /// Returns typed unsupported keep-size, mutation, storage, cancellation,
11698    /// allocation, or work failures.
11699    pub async fn preallocate(
11700        &mut self,
11701        range: ByteRange,
11702        keep_size: bool,
11703        budget: WorkBudget,
11704        cancellation: &CancellationToken,
11705    ) -> FsResult<()> {
11706        self.mutate_regular(
11707            RegularMutation::Preallocate {
11708                offset: range.offset,
11709                length: range.length,
11710                keep_size,
11711            },
11712            budget,
11713            cancellation,
11714        )
11715        .await
11716    }
11717
11718    async fn mutate_regular(
11719        &mut self,
11720        mutation: RegularMutation,
11721        budget: WorkBudget,
11722        cancellation: &CancellationToken,
11723    ) -> FsResult<()> {
11724        let receipt = apply_regular_mutation_async(
11725            &self.volume.fs.inner.objects,
11726            self.record.payload,
11727            mutation,
11728            self.volume.config,
11729            budget,
11730            cancellation,
11731        )
11732        .await
11733        .map_err(|failure| {
11734            OperationFailure::new(FsError::DetachedMutation(failure.error), *failure.work)
11735        })?;
11736        self.record.payload = receipt.payload;
11737        Ok(FsReceipt {
11738            value: (),
11739            work: receipt.work,
11740        })
11741    }
11742}
11743
11744pub(crate) async fn read_generation_root<O: AsyncObjectStore>(
11745    objects: &O,
11746    generation_root: ObjectId,
11747    config: VolumeConfig,
11748    budget: WorkBudget,
11749    cancellation: &CancellationToken,
11750) -> Result<(GenerationRoot, WorkCounters), OperationFailure<FsError>> {
11751    cancellation
11752        .check()
11753        .map_err(|error| OperationFailure::before_work(error.into()))?;
11754    let receipt = objects
11755        .read(
11756            generation_root,
11757            MAXIMUM_GENERATION_ROOT_BYTES.min(config.limits.maximum_object_bytes),
11758            budget,
11759            cancellation,
11760        )
11761        .await
11762        .map_err(|failure| OperationFailure::new(failure.error.into(), *failure.work))?;
11763    let retained_bytes = match receipt.value.retention {
11764        ObjectReadRetention::Shared => 0,
11765        ObjectReadRetention::Owned { logical_bytes } => logical_bytes,
11766    };
11767    let decode = decode_limits(config);
11768    let parent_count = generation_root_parent_count(&receipt.value, decode)
11769        .map_err(|error| OperationFailure::new(error.into(), receipt.work))?;
11770    let parent_bytes = parent_count
11771        .checked_mul(size_of::<GenerationId>())
11772        .map(crate::foundation::usize_to_u64)
11773        .ok_or_else(|| OperationFailure::new(FsError::Work(WorkError::Overflow), receipt.work))?;
11774    let simultaneous = retained_bytes
11775        .checked_add(parent_bytes)
11776        .ok_or_else(|| OperationFailure::new(FsError::Work(WorkError::Overflow), receipt.work))?;
11777    let mut work = receipt
11778        .work
11779        .checked_add(WorkCounters {
11780            allocation_operations: u64::from(parent_count != 0),
11781            bytes_copied: parent_bytes,
11782            ..WorkCounters::default()
11783        })
11784        .map_err(|error| OperationFailure::new(error.into(), receipt.work))?;
11785    work.peak_allocation_bytes = work.peak_allocation_bytes.max(simultaneous);
11786    work.verify(budget)
11787        .map_err(|error| OperationFailure::new(error.into(), receipt.work))?;
11788    let root = crate::kernel::decode_generation_root(&receipt.value, decode)
11789        .map_err(|error| OperationFailure::new(error.into(), work))?;
11790    Ok((root, work))
11791}
11792
11793fn exact_mutation_regions(
11794    mutation: &Mutation,
11795    first: Option<FileRecord>,
11796    second: Option<FileRecord>,
11797) -> [Option<(Option<FileRecord>, DependencyRegion)>; 4] {
11798    let mut regions = [None, None, None, None];
11799    match mutation {
11800        Mutation::Create { .. } | Mutation::Link { .. } => {}
11801        Mutation::Restore { .. } => {
11802            regions[0] =
11803                first.map(|record| (Some(record), DependencyRegion::FileRecord(record.file_id)));
11804        }
11805        Mutation::Remove { .. } => {
11806            regions[0] =
11807                first.map(|record| (Some(record), DependencyRegion::FileRecord(record.file_id)));
11808        }
11809        Mutation::Rename { .. } => {
11810            regions[0] =
11811                second.map(|record| (Some(record), DependencyRegion::FileRecord(record.file_id)));
11812        }
11813        Mutation::SetMetadata { .. } => {
11814            regions[0] =
11815                first.map(|record| (Some(record), DependencyRegion::Metadata(record.file_id)));
11816        }
11817        Mutation::Write { offset, length, .. } | Mutation::ZeroRange { offset, length, .. } => {
11818            let [content, file_length] = regular_range_regions(first, *offset, *length);
11819            regions[0] = content.map(|region| (first, region));
11820            regions[1] = file_length.map(|region| (first, region));
11821        }
11822        Mutation::ValidateRegular { .. } => {
11823            regions[0] =
11824                first.map(|record| (Some(record), DependencyRegion::FileRecord(record.file_id)));
11825        }
11826        Mutation::Preallocate { .. } => {
11827            regions[0] =
11828                first.map(|record| (Some(record), DependencyRegion::FileLength(record.file_id)));
11829        }
11830        Mutation::Resize { .. } => {
11831            // Resize can discard an arbitrarily large suffix. Until a compact
11832            // authenticated extent-summary region exists, the complete record
11833            // is the only bounded proof that cannot miss delete-vs-modify.
11834            regions[0] =
11835                first.map(|record| (Some(record), DependencyRegion::FileRecord(record.file_id)));
11836        }
11837        Mutation::CloneRange {
11838            source_offset,
11839            destination_offset,
11840            length,
11841            ..
11842        } => {
11843            let [source_content, source_length] =
11844                regular_range_regions(first, *source_offset, *length);
11845            let [destination_content, destination_length] =
11846                regular_range_regions(second, *destination_offset, *length);
11847            regions[0] = source_content.map(|region| (first, region));
11848            regions[1] = source_length.map(|region| (first, region));
11849            regions[2] = destination_content.map(|region| (second, region));
11850            regions[3] = destination_length.map(|region| (second, region));
11851        }
11852        Mutation::File { file_id, mutation } => match mutation {
11853            FileMutation::SetMetadata { .. } => {
11854                regions[0] = Some((first, DependencyRegion::Metadata(*file_id)));
11855            }
11856            FileMutation::Write { offset, length, .. }
11857            | FileMutation::ZeroRange { offset, length, .. } => {
11858                let [content, file_length] = regular_range_regions(first, *offset, *length);
11859                regions[0] = content.map(|region| (first, region));
11860                regions[1] = file_length.map(|region| (first, region));
11861            }
11862            FileMutation::Preallocate { .. } => {
11863                regions[0] = Some((first, DependencyRegion::FileLength(*file_id)));
11864            }
11865            FileMutation::ReplaceRecord { .. }
11866            | FileMutation::ValidateRegular
11867            | FileMutation::Resize { .. } => {
11868                regions[0] = Some((first, DependencyRegion::FileRecord(*file_id)));
11869            }
11870        },
11871        Mutation::CloneFileRange {
11872            source_file_id,
11873            source_offset,
11874            destination_file_id,
11875            destination_offset,
11876            length,
11877        } => {
11878            let [source_content, source_length] =
11879                regular_range_regions(first, *source_offset, *length);
11880            let [destination_content, destination_length] =
11881                regular_range_regions(second, *destination_offset, *length);
11882            regions[0] = source_content.map(|region| (first, region));
11883            regions[1] = source_length.map(|region| (first, region));
11884            regions[2] = destination_content.map(|region| (second, region));
11885            regions[3] = destination_length.map(|region| (second, region));
11886            debug_assert!(first.is_none_or(|record| record.file_id == *source_file_id));
11887            debug_assert!(second.is_none_or(|record| record.file_id == *destination_file_id));
11888        }
11889    }
11890    regions
11891}
11892
11893fn regular_range_regions(
11894    record: Option<FileRecord>,
11895    offset: u64,
11896    length: u64,
11897) -> [Option<DependencyRegion>; 2] {
11898    let Some(record) = record else {
11899        return [None, None];
11900    };
11901    let logical_bytes = match record.payload {
11902        FilePayload::InlineRegular(data) => {
11903            u64::try_from(data.as_bytes().len()).unwrap_or(u64::MAX)
11904        }
11905        FilePayload::Regular { logical_bytes, .. } => logical_bytes,
11906        FilePayload::Directory { .. }
11907        | FilePayload::SymbolicLink { .. }
11908        | FilePayload::Device { .. }
11909        | FilePayload::Empty
11910        | FilePayload::ReparsePoint { .. } => {
11911            return [Some(DependencyRegion::FileRecord(record.file_id)), None];
11912        }
11913    };
11914    let overlap = logical_bytes.saturating_sub(offset).min(length);
11915    let content = (overlap != 0).then_some(DependencyRegion::ContentRange {
11916        file_id: record.file_id,
11917        offset,
11918        length: overlap,
11919    });
11920    let extends = offset
11921        .checked_add(length)
11922        .is_none_or(|end| end > logical_bytes);
11923    let file_length = extends.then_some(DependencyRegion::FileLength(record.file_id));
11924    [content, file_length]
11925}
11926
11927fn validate_planned_file_range(
11928    logical_bytes: u64,
11929    range: ByteRange,
11930    work: WorkCounters,
11931) -> Result<(), OperationFailure<FsError>> {
11932    let end = range.offset.checked_add(range.length).ok_or_else(|| {
11933        OperationFailure::new(FsError::FileRead(FileRangeReadError::InvalidRange), work)
11934    })?;
11935    if end > logical_bytes {
11936        return Err(OperationFailure::new(
11937            FsError::FileRead(FileRangeReadError::InvalidRange),
11938            work,
11939        ));
11940    }
11941    Ok(())
11942}
11943
11944async fn capture_dependencies_async<O: AsyncObjectStore>(
11945    objects: &O,
11946    generation_root: &ObjectId,
11947    config: VolumeConfig,
11948    regions: Vec<DependencyRegion>,
11949    budget: WorkBudget,
11950    cancellation: &CancellationToken,
11951) -> FsResult<Vec<Dependency>> {
11952    let probe = AuthenticatedGenerationProbe::new(objects, probe_limits(config))
11953        .map_err(|error| OperationFailure::before_work(error.into()))?;
11954    probe
11955        .capture_many_async(
11956            GenerationId::new(generation_root.digest),
11957            regions,
11958            config.limits.maximum_checkout_dependencies,
11959            budget,
11960            cancellation,
11961        )
11962        .await
11963        .map_err(|failure| failure.map_with_prior_work(WorkCounters::default(), FsError::Probe))
11964}
11965
11966async fn capture_known_dependencies_async<O: AsyncObjectStore>(
11967    objects: &O,
11968    config: VolumeConfig,
11969    regions: Vec<(Option<FileRecord>, DependencyRegion)>,
11970    budget: WorkBudget,
11971    cancellation: &CancellationToken,
11972) -> FsResult<Vec<Dependency>> {
11973    let probe = AuthenticatedGenerationProbe::new(objects, probe_limits(config))
11974        .map_err(|error| OperationFailure::before_work(error.into()))?;
11975    probe
11976        .capture_records_many_async(
11977            regions,
11978            config.limits.maximum_checkout_dependencies,
11979            budget,
11980            cancellation,
11981        )
11982        .await
11983        .map_err(|failure| failure.map_with_prior_work(WorkCounters::default(), FsError::Probe))
11984}
11985
11986fn generation_from_record(
11987    record: &DurableCommit,
11988    volume_id: VolumeId,
11989    work: WorkCounters,
11990) -> Result<ObjectId, OperationFailure<FsError>> {
11991    if record.sequence == Sequence::new(1) {
11992        let creation = decode_volume_created(&record.payload, MAXIMUM_VOLUME_EVENT_BYTES)
11993            .map_err(|error| OperationFailure::new(error.into(), work))?;
11994        if creation.volume_id != volume_id {
11995            return Err(OperationFailure::new(FsError::VolumeMismatch, work));
11996        }
11997        Ok(creation.initial_generation_root)
11998    } else {
11999        let publication = decode_published_generation(&record.payload, MAXIMUM_VOLUME_EVENT_BYTES)
12000            .map_err(|error| OperationFailure::new(error.into(), work))?;
12001        if publication.volume_id != volume_id {
12002            return Err(OperationFailure::new(FsError::VolumeMismatch, work));
12003        }
12004        Ok(publication.generation_root)
12005    }
12006}
12007
12008#[allow(clippy::too_many_arguments)]
12009async fn diff_generation_file_tables<O: AsyncObjectStore>(
12010    objects: &O,
12011    before: ObjectId,
12012    after: ObjectId,
12013    maximum_changes: u32,
12014    config: VolumeConfig,
12015    mut work: WorkCounters,
12016    budget: WorkBudget,
12017    cancellation: &CancellationToken,
12018) -> FsResult<GenerationDiff> {
12019    let records = diff_file_records_async(
12020        objects,
12021        Some(before),
12022        Some(after),
12023        maximum_changes,
12024        decode_limits(config),
12025        remaining(work, budget)?,
12026        cancellation,
12027    )
12028    .await
12029    .map_err(|failure| map_diff_failure(failure, work))?;
12030    work = add(work, records.work)?;
12031    let maximum = usize::try_from(maximum_changes).unwrap_or(usize::MAX);
12032    let mut files = Vec::new();
12033    files
12034        .try_reserve(records.changes.len())
12035        .map_err(|_| OperationFailure::new(FsError::DiffAllocationFailed, work))?;
12036    let mut bindings = Vec::new();
12037    let mut truncated = records.truncated;
12038    for change in records.changes {
12039        if files.len() + bindings.len() >= maximum {
12040            truncated = true;
12041            break;
12042        }
12043        let before_entries = directory_entries(change.before);
12044        let after_entries = directory_entries(change.after);
12045        files.push(FileRecordChange {
12046            file_id: change.key,
12047            before: change.before,
12048            after: change.after,
12049        });
12050        if before_entries == after_entries {
12051            continue;
12052        }
12053        let remaining_changes = maximum.saturating_sub(files.len() + bindings.len());
12054        if remaining_changes == 0 {
12055            truncated = true;
12056            continue;
12057        }
12058        let entries = diff_tree_entries_async(
12059            objects,
12060            before_entries,
12061            after_entries,
12062            u32::try_from(remaining_changes).unwrap_or(u32::MAX),
12063            decode_limits(config),
12064            remaining(work, budget)?,
12065            cancellation,
12066        )
12067        .await
12068        .map_err(|failure| map_diff_failure(failure, work))?;
12069        work = add(work, entries.work)?;
12070        truncated |= entries.truncated;
12071        bindings.extend(
12072            entries
12073                .changes
12074                .into_iter()
12075                .map(|entry| DirectoryBindingChange {
12076                    directory_id: change.key,
12077                    name: entry.key,
12078                    before: entry.before,
12079                    after: entry.after,
12080                }),
12081        );
12082    }
12083    Ok(OperationReceipt {
12084        value: GenerationDiff {
12085            files,
12086            bindings,
12087            truncated,
12088        },
12089        work,
12090    })
12091}
12092
12093async fn collect_generation_ancestors<O: AsyncObjectStore>(
12094    objects: &O,
12095    start: GenerationId,
12096    config: VolumeConfig,
12097    maximum_generations: u32,
12098    cancellation: &CancellationToken,
12099) -> Result<(BTreeMap<GenerationId, Option<VolumeId>>, bool), crate::workspace::WorkspaceError> {
12100    let maximum = usize::try_from(maximum_generations).unwrap_or(usize::MAX);
12101    let mut visited = BTreeMap::new();
12102    let mut pending = VecDeque::from([start]);
12103    while let Some(generation) = pending.pop_front() {
12104        if visited.contains_key(&generation) {
12105            continue;
12106        }
12107        if visited.len() == maximum {
12108            for frontier in pending {
12109                visited.entry(frontier).or_insert(None);
12110            }
12111            visited.entry(generation).or_insert(None);
12112            return Ok((visited, true));
12113        }
12114        let (root, _) = read_generation_root(
12115            objects,
12116            ObjectId {
12117                kind: ObjectKind::GenerationRoot,
12118                digest: generation.digest(),
12119            },
12120            config,
12121            WorkBudget::UNBOUNDED,
12122            cancellation,
12123        )
12124        .await
12125        .map_err(crate::workspace::WorkspaceError::engine)?;
12126        visited.insert(generation, Some(root.volume_id));
12127        pending.extend(root.parents);
12128    }
12129    Ok((visited, false))
12130}
12131
12132async fn find_first_generation_ancestor<O: AsyncObjectStore>(
12133    objects: &O,
12134    start: GenerationId,
12135    config: VolumeConfig,
12136    maximum_generations: u32,
12137    candidates: &BTreeMap<GenerationId, Option<VolumeId>>,
12138    candidates_truncated: bool,
12139    cancellation: &CancellationToken,
12140) -> Result<WorkspaceCommonAncestor, crate::workspace::WorkspaceError> {
12141    let maximum = usize::try_from(maximum_generations).unwrap_or(usize::MAX);
12142    let mut visited = BTreeSet::new();
12143    let mut pending = VecDeque::from([start]);
12144    while let Some(generation) = pending.pop_front() {
12145        if let Some(Some(volume_id)) = candidates.get(&generation) {
12146            return Ok(WorkspaceCommonAncestor {
12147                id: generation,
12148                volume_id: *volume_id,
12149            });
12150        }
12151        let is_candidate_frontier = matches!(candidates.get(&generation), Some(None));
12152        if visited.contains(&generation) {
12153            continue;
12154        }
12155        if visited.len() == maximum {
12156            return Err(crate::workspace::WorkspaceError::LineageLimit);
12157        }
12158        let (root, _) = read_generation_root(
12159            objects,
12160            ObjectId {
12161                kind: ObjectKind::GenerationRoot,
12162                digest: generation.digest(),
12163            },
12164            config,
12165            WorkBudget::UNBOUNDED,
12166            cancellation,
12167        )
12168        .await
12169        .map_err(crate::workspace::WorkspaceError::engine)?;
12170        visited.insert(generation);
12171        if is_candidate_frontier {
12172            return Ok(WorkspaceCommonAncestor {
12173                id: generation,
12174                volume_id: root.volume_id,
12175            });
12176        }
12177        pending.extend(root.parents);
12178    }
12179    if candidates_truncated {
12180        Err(crate::workspace::WorkspaceError::LineageLimit)
12181    } else {
12182        Err(crate::workspace::WorkspaceError::NoCommonAncestor)
12183    }
12184}
12185
12186fn durable_head(commit: &DurableCommit) -> Head {
12187    Head {
12188        epoch: commit.epoch,
12189        sequence: commit.sequence,
12190        digest: commit.digest,
12191    }
12192}
12193
12194fn validate_volume_capabilities(
12195    config: VolumeConfig,
12196    capabilities: EmbeddedCapabilities,
12197) -> Result<(), OperationFailure<FsError>> {
12198    config
12199        .validate()
12200        .map_err(|error| OperationFailure::before_work(error.into()))?;
12201    if config.lifecycle == Lifecycle::Durable && !capabilities.durable {
12202        return Err(OperationFailure::before_work(
12203            FsError::UnsupportedDurability,
12204        ));
12205    }
12206    Ok(())
12207}
12208
12209fn validate_checkout(
12210    mode: CheckoutMode,
12211    config: VolumeConfig,
12212) -> Result<(), OperationFailure<FsError>> {
12213    mode.validate()
12214        .map_err(|error| OperationFailure::before_work(error.into()))?;
12215    match (mode.access, mode.consistency, mode.mutations) {
12216        (
12217            AccessMode::ReadOnly,
12218            ConsistencyMode::Pinned
12219            | ConsistencyMode::Manual
12220            | ConsistencyMode::TrackingSafe
12221            | ConsistencyMode::Live,
12222            MutationMode::None,
12223        )
12224        | (
12225            AccessMode::ReadWrite,
12226            ConsistencyMode::Pinned
12227            | ConsistencyMode::Manual
12228            | ConsistencyMode::TrackingSafe
12229            | ConsistencyMode::Live,
12230            MutationMode::PrivateOverlay,
12231        ) => Ok(()),
12232        (AccessMode::ReadWrite, ConsistencyMode::Live, MutationMode::DirectLive)
12233            if config.concurrency == ConcurrencyMode::SerializedAuthority =>
12234        {
12235            Ok(())
12236        }
12237        (AccessMode::ReadWrite, ConsistencyMode::Live, MutationMode::DirectLive) => Err(
12238            OperationFailure::before_work(FsError::LiveRequiresSerializedAuthority),
12239        ),
12240        _ => Err(OperationFailure::before_work(FsError::UnsupportedCheckout)),
12241    }
12242}
12243
12244fn closure_limits(config: VolumeConfig) -> ClosureLimits {
12245    ClosureLimits {
12246        decode: decode_limits(config),
12247        maximum_objects: config.limits.maximum_objects_per_generation,
12248        maximum_files: config.limits.maximum_files_per_generation,
12249        maximum_object_bytes: config.limits.maximum_generation_bytes,
12250        profile: config.profile,
12251        symbolic_links: config.symbolic_links,
12252        hard_links: config.hard_links,
12253        sparse_files: config.sparse_files,
12254    }
12255}
12256
12257fn decode_limits(config: VolumeConfig) -> DecodeLimits {
12258    DecodeLimits {
12259        maximum_object_bytes: config.limits.maximum_object_bytes,
12260        maximum_name_bytes: config.limits.maximum_component_bytes,
12261        maximum_page_items: config.limits.maximum_directory_page_entries,
12262        maximum_page_bytes: u32::try_from(config.limits.maximum_object_bytes).unwrap_or(u32::MAX),
12263        maximum_page_height: config.limits.maximum_page_height,
12264        maximum_visited_pages: u32::try_from(config.limits.maximum_objects_per_generation)
12265            .unwrap_or(u32::MAX),
12266    }
12267}
12268
12269fn directory_entries(record: Option<FileRecord>) -> Option<ObjectId> {
12270    let record = record?;
12271    match record.payload {
12272        FilePayload::Directory { entries } => Some(entries),
12273        _ => None,
12274    }
12275}
12276
12277fn map_diff_failure(
12278    failure: OperationFailure<PersistentDiffError>,
12279    prior: WorkCounters,
12280) -> OperationFailure<FsError> {
12281    failure.map_with_prior_work(prior, |error| match error {
12282        PersistentDiffError::WrongRootKind | PersistentDiffError::InvalidLimit => {
12283            FsError::InvalidDiff
12284        }
12285        PersistentDiffError::AllocationFailed => FsError::DiffAllocationFailed,
12286        PersistentDiffError::Storage(error) => FsError::Object(error),
12287        PersistentDiffError::Decode(error) => FsError::Decode(error),
12288        PersistentDiffError::Work(error) => FsError::Work(error),
12289        PersistentDiffError::Cancelled => FsError::Cancelled(CancellationError),
12290    })
12291}
12292
12293fn map_merge_failure(
12294    failure: OperationFailure<MergeGenerationError>,
12295    prior: WorkCounters,
12296) -> OperationFailure<FsError> {
12297    failure.map_with_prior_work(prior, |error| match error {
12298        MergeGenerationError::InvalidDiff => FsError::InvalidDiff,
12299        MergeGenerationError::ChangeLimit => FsError::MergeChangeLimit,
12300        MergeGenerationError::AllocationFailed => FsError::DiffAllocationFailed,
12301        MergeGenerationError::Object(error) => FsError::Object(error),
12302        MergeGenerationError::Decode(error) => FsError::Decode(error),
12303        MergeGenerationError::FileTable(error) => FsError::MergeFileTable(error),
12304        MergeGenerationError::Tree(error) => FsError::MergeTree(error),
12305        MergeGenerationError::ExtentRead(error) => {
12306            FsError::FileRead(FileRangeReadError::Extent(error))
12307        }
12308        MergeGenerationError::ExtentMutation(error) => {
12309            FsError::DetachedMutation(RegularMutationError::Extent(error))
12310        }
12311        MergeGenerationError::Checkpoint(error) => FsError::Checkpoint(error),
12312        MergeGenerationError::Cancelled(error) => FsError::Cancelled(error),
12313        MergeGenerationError::Work(error) => FsError::Work(error),
12314    })
12315}
12316
12317const fn live_publication_observation(
12318    outcome: CheckoutCommitOutcome,
12319) -> LivePublicationObservation {
12320    match outcome {
12321        CheckoutCommitOutcome::Committed {
12322            generation_id,
12323            head,
12324        } => LivePublicationObservation::Committed {
12325            generation_id,
12326            head,
12327        },
12328        CheckoutCommitOutcome::AlreadyCommitted {
12329            generation_id,
12330            head,
12331        } => LivePublicationObservation::AlreadyCommitted {
12332            generation_id,
12333            head,
12334        },
12335        CheckoutCommitOutcome::Conflict { actual } => {
12336            LivePublicationObservation::Conflict { actual }
12337        }
12338        CheckoutCommitOutcome::Fenced { actual_epoch } => {
12339            LivePublicationObservation::Fenced { actual_epoch }
12340        }
12341        CheckoutCommitOutcome::IdempotencyConflict {
12342            committed_fingerprint,
12343        } => LivePublicationObservation::IdempotencyConflict {
12344            committed_fingerprint,
12345        },
12346    }
12347}
12348
12349fn probe_limits(config: VolumeConfig) -> ProbeLimits {
12350    ProbeLimits {
12351        decode: decode_limits(config),
12352        maximum_cached_generations: 2,
12353        maximum_cached_records: config.limits.maximum_checkout_dependencies,
12354        maximum_extent_spans: config.limits.maximum_directory_page_entries,
12355        maximum_content_payload_bytes: config.limits.maximum_read_bytes,
12356        maximum_directory_entries: config.limits.maximum_directory_page_entries,
12357    }
12358}
12359
12360fn empty_metadata() -> FileMetadata {
12361    FileMetadata::default()
12362}
12363
12364fn derived_file_id(volume_id: VolumeId) -> (FileId, WorkCounters) {
12365    let digest = derived_digest(ROOT_FILE_DOMAIN, volume_id);
12366    let mut bytes = [0_u8; 16];
12367    bytes.copy_from_slice(&digest.as_bytes()[..16]);
12368    (
12369        FileId::from_bytes(bytes),
12370        identity_hash_work(ROOT_FILE_DOMAIN),
12371    )
12372}
12373
12374fn derived_operation_id(volume_id: VolumeId) -> (OperationId, WorkCounters) {
12375    let digest = derived_digest(CREATION_OPERATION_DOMAIN, volume_id);
12376    let mut bytes = [0_u8; 16];
12377    bytes.copy_from_slice(&digest.as_bytes()[..16]);
12378    (
12379        OperationId::from_bytes(bytes),
12380        identity_hash_work(CREATION_OPERATION_DOMAIN),
12381    )
12382}
12383
12384fn derived_digest(domain: &[u8], volume_id: VolumeId) -> Digest {
12385    let mut hasher = blake3::Hasher::new();
12386    hasher.update(domain);
12387    hasher.update(&volume_id.into_bytes());
12388    Digest::from_bytes(*hasher.finalize().as_bytes())
12389}
12390
12391fn identity_hash_work(domain: &[u8]) -> WorkCounters {
12392    WorkCounters {
12393        bytes_hashed: u64::try_from(domain.len()).unwrap_or(u64::MAX) + 16,
12394        ..WorkCounters::default()
12395    }
12396}
12397
12398fn creation_commit(operation_id: OperationId, payload: Vec<u8>) -> (ProposedCommit, WorkCounters) {
12399    let mut hasher = blake3::Hasher::new();
12400    hasher.update(CREATION_FINGERPRINT_DOMAIN);
12401    hasher.update(&payload);
12402    let length = u64::try_from(payload.len()).unwrap_or(u64::MAX);
12403    let capacity = u64::try_from(payload.capacity()).unwrap_or(u64::MAX);
12404    (
12405        ProposedCommit {
12406            operation_id,
12407            fingerprint: Digest::from_bytes(*hasher.finalize().as_bytes()),
12408            payload: Bytes::from(payload),
12409        },
12410        WorkCounters {
12411            bytes_encoded: length,
12412            bytes_hashed: u64::try_from(CREATION_FINGERPRINT_DOMAIN.len())
12413                .unwrap_or(u64::MAX)
12414                .saturating_add(length),
12415            allocation_operations: 1,
12416            peak_allocation_bytes: capacity,
12417            ..WorkCounters::default()
12418        },
12419    )
12420}
12421
12422fn add(prior: WorkCounters, next: WorkCounters) -> Result<WorkCounters, OperationFailure<FsError>> {
12423    prior
12424        .checked_add(next)
12425        .map_err(|error| OperationFailure::new(error.into(), prior))
12426}
12427
12428fn map_transfer_error(error: GenerationTransferError) -> FsError {
12429    match error {
12430        GenerationTransferError::Cancelled(error) => FsError::Cancelled(error),
12431        GenerationTransferError::Manifest(_) | GenerationTransferError::ManifestMismatch => {
12432            FsError::InvalidExportManifest
12433        }
12434        GenerationTransferError::Closure(error) => FsError::Closure(error),
12435        GenerationTransferError::Object(error) => FsError::Object(error),
12436        GenerationTransferError::Work(error) => FsError::Work(error),
12437        error @ (GenerationTransferError::InvalidCursor
12438        | GenerationTransferError::EmptyBatch
12439        | GenerationTransferError::TooManyObjects
12440        | GenerationTransferError::AllocationFailed) => FsError::Transfer(error),
12441    }
12442}
12443
12444#[cfg(all(feature = "local", not(target_arch = "wasm32")))]
12445fn object_vec_bytes(objects: &Vec<ObjectId>) -> u64 {
12446    u64::try_from(objects.capacity())
12447        .unwrap_or(u64::MAX)
12448        .saturating_mul(u64::try_from(size_of::<ObjectId>()).unwrap_or(u64::MAX))
12449}
12450
12451#[cfg(all(feature = "local", not(target_arch = "wasm32")))]
12452fn object_slice_bytes(objects: &[ObjectId]) -> u64 {
12453    u64::try_from(objects.len())
12454        .unwrap_or(u64::MAX)
12455        .saturating_mul(u64::try_from(size_of::<ObjectId>()).unwrap_or(u64::MAX))
12456}
12457
12458#[cfg(all(feature = "local", not(target_arch = "wasm32")))]
12459fn merge_sorted_object_ids(
12460    reachable: &mut Vec<ObjectId>,
12461    incoming: &[ObjectId],
12462    incoming_bytes: u64,
12463    retained_bytes: u64,
12464    work: &mut WorkCounters,
12465    budget: WorkBudget,
12466) -> Result<(), OperationFailure<FsError>> {
12467    let maximum_items = reachable
12468        .len()
12469        .checked_add(incoming.len())
12470        .ok_or_else(|| OperationFailure::new(FsError::Work(WorkError::Overflow), *work))?;
12471    let maximum_bytes = u64::try_from(maximum_items)
12472        .unwrap_or(u64::MAX)
12473        .saturating_mul(u64::try_from(size_of::<ObjectId>()).unwrap_or(u64::MAX));
12474    let peak = retained_bytes
12475        .checked_add(object_vec_bytes(reachable))
12476        .and_then(|value| value.checked_add(incoming_bytes))
12477        .and_then(|value| value.checked_add(maximum_bytes))
12478        .ok_or_else(|| OperationFailure::new(FsError::Work(WorkError::Overflow), *work))?;
12479    let admission = work
12480        .checked_add(WorkCounters {
12481            items_examined: u64::try_from(maximum_items).unwrap_or(u64::MAX),
12482            bytes_copied: maximum_bytes,
12483            allocation_operations: u64::from(maximum_items != 0),
12484            peak_allocation_bytes: peak,
12485            ..WorkCounters::default()
12486        })
12487        .map_err(|error| OperationFailure::new(error.into(), *work))?;
12488    admission
12489        .verify(budget)
12490        .map_err(|error| OperationFailure::new(error.into(), *work))?;
12491    let mut merged = Vec::new();
12492    merged
12493        .try_reserve_exact(maximum_items)
12494        .map_err(|_| OperationFailure::new(FsError::GarbageCollectionAllocationFailed, *work))?;
12495    let mut left_iter = reachable.iter().copied().peekable();
12496    let mut right_iter = incoming.iter().copied().peekable();
12497    loop {
12498        match (left_iter.peek(), right_iter.peek()) {
12499            (Some(&left), Some(&right)) => match left.cmp(&right) {
12500                std::cmp::Ordering::Less => {
12501                    merged.push(left);
12502                    left_iter.next();
12503                }
12504                std::cmp::Ordering::Greater => {
12505                    merged.push(right);
12506                    right_iter.next();
12507                }
12508                std::cmp::Ordering::Equal => {
12509                    merged.push(left);
12510                    left_iter.next();
12511                    right_iter.next();
12512                }
12513            },
12514            (Some(&left), None) => {
12515                merged.push(left);
12516                left_iter.next();
12517            }
12518            (None, Some(&right)) => {
12519                merged.push(right);
12520                right_iter.next();
12521            }
12522            (None, None) => break,
12523        }
12524    }
12525    let copied_bytes = u64::try_from(merged.len())
12526        .unwrap_or(u64::MAX)
12527        .saturating_mul(u64::try_from(size_of::<ObjectId>()).unwrap_or(u64::MAX));
12528    *work = work
12529        .checked_add(WorkCounters {
12530            items_examined: u64::try_from(maximum_items).unwrap_or(u64::MAX),
12531            bytes_copied: copied_bytes,
12532            allocation_operations: u64::from(maximum_items != 0),
12533            peak_allocation_bytes: peak,
12534            ..WorkCounters::default()
12535        })
12536        .map_err(|error| OperationFailure::new(error.into(), *work))?;
12537    *reachable = merged;
12538    Ok(())
12539}
12540
12541fn remaining(
12542    work: WorkCounters,
12543    budget: WorkBudget,
12544) -> Result<WorkBudget, OperationFailure<FsError>> {
12545    work.remaining(budget)
12546        .map_err(|error| OperationFailure::new(error.into(), work))
12547}
12548
12549fn merge_simultaneous_work(
12550    prior: WorkCounters,
12551    mut nested: WorkCounters,
12552    live_bytes: u64,
12553    budget: WorkBudget,
12554) -> Result<WorkCounters, OperationFailure<FsError>> {
12555    let simultaneous_peak = live_bytes
12556        .checked_add(nested.peak_allocation_bytes)
12557        .ok_or_else(|| OperationFailure::new(FsError::Work(WorkError::Overflow), prior))?;
12558    nested.peak_allocation_bytes = 0;
12559    let mut merged = prior
12560        .checked_add(nested)
12561        .map_err(|error| OperationFailure::new(error.into(), prior))?;
12562    merged.peak_allocation_bytes = merged.peak_allocation_bytes.max(simultaneous_peak);
12563    merged
12564        .verify(budget)
12565        .map_err(|error| OperationFailure::new(error.into(), merged))?;
12566    Ok(merged)
12567}
12568
12569fn merge_simultaneous_failure(
12570    prior: WorkCounters,
12571    mut nested: WorkCounters,
12572    live_bytes: u64,
12573    error: FsError,
12574) -> OperationFailure<FsError> {
12575    let Some(simultaneous_peak) = live_bytes.checked_add(nested.peak_allocation_bytes) else {
12576        return OperationFailure::new(FsError::Work(WorkError::Overflow), prior);
12577    };
12578    nested.peak_allocation_bytes = 0;
12579    let Ok(mut merged) = prior.checked_add(nested) else {
12580        return OperationFailure::new(FsError::Work(WorkError::Overflow), prior);
12581    };
12582    merged.peak_allocation_bytes = merged.peak_allocation_bytes.max(simultaneous_peak);
12583    OperationFailure::new(error, merged)
12584}
12585
12586#[cfg(all(test, feature = "memory"))]
12587#[path = "tests/facade.rs"]
12588mod tests;