Skip to main content

ftts_artifacts/
fttsq.rs

1//! `.fttsq` — the canonical, portable, quantized model container.
2//!
3//! # What this format is, and what it deliberately is not
4//!
5//! `.fttsq` is **portable and machine-independent**: tensor payloads, quantization scales, the
6//! frozen model config, provenance, and the license notice. It carries **no machine-specific
7//! tiling** — packed kernel layouts and the autotuned plan live in `.fttspack`, which is a
8//! regenerable per-machine cache. That split is the whole point: kernel layouts change often, and
9//! republishing a multi-gigabyte artifact every time a tile shape improves is not a cost we are
10//! willing to pay.
11//!
12//! # Access classes
13//!
14//! Sections are grouped by how the runtime *touches* them, because the paging policy differs by an
15//! order of magnitude between them:
16//!
17//! | Class | Rough size | Access pattern |
18//! |---|---|---|
19//! | [`AccessClass::HotRecurrentMicrodecoder`] | ~110 MB | reread 15× per frame — the residency target |
20//! | [`AccessClass::HotRecurrentTalker`] | ~440 MB | 28 layers, once per frame |
21//! | [`AccessClass::HotCodecDecoder`] | smaller | once per frame, latency-critical |
22//! | [`AccessClass::ColdTextEmbedding`] | ~622 MB | **row-granular**; never paged in wholesale |
23//! | [`AccessClass::EnrollmentSpeakerEncoder`] / [`AccessClass::EnrollmentCodecEncoder`] | optional | enrollment only |
24//! | [`AccessClass::Metadata`] | tiny | config, manifests, license |
25//!
26//! The cold text embedding is 622 MB that a synthesis run touches a few kilobytes of. Faulting it
27//! in as a unit would dominate startup and evict the microdecoder pack that the whole optimization
28//! program is built around, so its class is a load-bearing declaration, not a label.
29//!
30//! # Hardening
31//!
32//! This reader parses attacker-influenced binary blobs — an artifact is just a file someone gives
33//! you. Every length and offset is checked arithmetic against the real file length, section ranges
34//! may not overlap, tensor ranges must lie inside their section, counts and dimensions are capped,
35//! and every section is digest-verified before its bytes are handed out. A malformed artifact is a
36//! **named refusal**, never a partial load that resurfaces later as garbage audio.
37//!
38//! # Layout
39//!
40//! ```text
41//! [0  .. 8 )   magic          b"FTTSQ\0\0\0"
42//! [8  ..12 )   format_version u32 little-endian
43//! [12 ..20 )   directory_len  u64 little-endian
44//! [20 ..20+D)  directory      UTF-8 JSON
45//! [20+D..   )  section payloads, at absolute offsets named in the directory
46//! ```
47//!
48//! Bead: `frankentts-p2-fttsq-format-wsa`.
49
50use std::{collections::BTreeMap, fmt};
51
52use ftts_kernels::mmap::{MappedFile, MemoryAdvice, MemoryAdviceOutcome, MemoryResidency};
53use serde_json::{Value, json};
54
55use crate::sha256::{Sha256, hex_digest, to_hex};
56
57/// File magic. Eight bytes so the directory length lands 8-byte aligned.
58pub const MAGIC: &[u8; 8] = b"FTTSQ\0\0\0";
59
60/// The format version this binary writes and is the newest it can read.
61///
62/// A reader **refuses** anything newer: a future version may relocate bytes this binary would
63/// otherwise misinterpret, and "read it anyway and hope" is how a container format acquires
64/// silent, version-dependent corruption.
65pub const FORMAT_VERSION: u32 = 1;
66
67/// Fixed prefix length: magic + version + directory length.
68pub const HEADER_PREFIX_BYTES: u64 = 20;
69
70/// Largest directory we will parse, guarding against a hostile length prefix.
71///
72/// The real checkpoint's directory is a few hundred KiB. Matches `safetensors::MAX_HEADER_BYTES`
73/// deliberately — two artifact readers with different limits is a bug waiting to be found.
74pub const MAX_DIRECTORY_BYTES: u64 = 64 * 1024 * 1024;
75
76/// Most sections an artifact may declare. Seven access classes, with headroom for splitting.
77pub const MAX_SECTIONS: usize = 64;
78
79/// Most tensors an artifact may declare. The pinned checkpoint has 974.
80pub const MAX_TENSORS: usize = 16_384;
81
82/// Most dimensions one tensor may have. The pinned checkpoint's maximum rank is 4.
83pub const MAX_RANK: usize = 8;
84
85/// Largest single dimension. The largest real one is the 151,936-row text embedding.
86pub const MAX_DIM: u64 = 1 << 32;
87
88/// How the runtime touches a section. Drives the page-in policy.
89///
90/// Unknown values are refused rather than defaulted: a section whose access class we do not
91/// understand is one we cannot page correctly, and guessing "probably hot" for a 622 MB cold
92/// section is exactly the mistake that costs the microdecoder its cache residency.
93#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
94pub enum AccessClass {
95    /// The ~110 MB microdecoder hot pack, reread 15× per frame.
96    HotRecurrentMicrodecoder,
97    /// The ~440 MB talker body, read once per frame across 28 layers.
98    HotRecurrentTalker,
99    /// The codec decoder, read once per frame.
100    HotCodecDecoder,
101    /// The ~622 MB text embedding: lazy, row-granular, never paged in wholesale.
102    ColdTextEmbedding,
103    /// Speaker encoder, needed only during enrollment.
104    EnrollmentSpeakerEncoder,
105    /// Codec encoder, needed only during enrollment.
106    EnrollmentCodecEncoder,
107    /// Config, manifests, and the license notice.
108    Metadata,
109}
110
111impl AccessClass {
112    /// The stable wire string.
113    #[must_use]
114    pub const fn as_str(self) -> &'static str {
115        match self {
116            Self::HotRecurrentMicrodecoder => "HOT_RECURRENT_MICRODECODER",
117            Self::HotRecurrentTalker => "HOT_RECURRENT_TALKER",
118            Self::HotCodecDecoder => "HOT_CODEC_DECODER",
119            Self::ColdTextEmbedding => "COLD_TEXT_EMBEDDING",
120            Self::EnrollmentSpeakerEncoder => "ENROLLMENT_SPEAKER_ENCODER",
121            Self::EnrollmentCodecEncoder => "ENROLLMENT_CODEC_ENCODER",
122            Self::Metadata => "METADATA",
123        }
124    }
125
126    /// Parses a wire string, refusing anything unrecognized.
127    #[must_use]
128    pub fn parse(text: &str) -> Option<Self> {
129        Some(match text {
130            "HOT_RECURRENT_MICRODECODER" => Self::HotRecurrentMicrodecoder,
131            "HOT_RECURRENT_TALKER" => Self::HotRecurrentTalker,
132            "HOT_CODEC_DECODER" => Self::HotCodecDecoder,
133            "COLD_TEXT_EMBEDDING" => Self::ColdTextEmbedding,
134            "ENROLLMENT_SPEAKER_ENCODER" => Self::EnrollmentSpeakerEncoder,
135            "ENROLLMENT_CODEC_ENCODER" => Self::EnrollmentCodecEncoder,
136            "METADATA" => Self::Metadata,
137            _ => return None,
138        })
139    }
140
141    /// Whether this section should be resident during steady-state decode.
142    ///
143    /// Consumed by the page-in policy: hot classes are advised resident, the cold embedding is
144    /// explicitly not, and enrollment sections are only touched by the voice compiler.
145    #[must_use]
146    pub const fn is_hot(self) -> bool {
147        matches!(
148            self,
149            Self::HotRecurrentMicrodecoder | Self::HotRecurrentTalker | Self::HotCodecDecoder
150        )
151    }
152
153    /// Whether the runtime must access this section row-granularly rather than as a unit.
154    #[must_use]
155    pub const fn is_row_granular(self) -> bool {
156        matches!(self, Self::ColdTextEmbedding)
157    }
158}
159
160impl fmt::Display for AccessClass {
161    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
162        f.write_str(self.as_str())
163    }
164}
165
166/// What the loader should do with a section's pages.
167///
168/// This is the **decision** layer: a total, pure function of the access class, unit-testable with
169/// no syscalls and no `unsafe`. Applying it — `mmap` plus the matching `madvise` — belongs to the
170/// audited unsafe island in `ftts-kernels`, because `ftts-artifacts` is `forbid(unsafe_code)`.
171/// Keeping the two apart means the policy can be argued about and tested here, where getting it
172/// wrong is cheap, rather than inside a syscall wrapper where it is not.
173#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
174pub enum PagePolicy {
175    /// Fault in eagerly and keep resident: `MADV_WILLNEED`.
176    ///
177    /// Only for sections read every frame. The microdecoder pack is the whole reason this variant
178    /// exists — it is reread 15× per frame and its residency is the project's #1 design center.
179    Resident,
180    /// Map lazily and let the kernel fault pages in as they are touched, a row at a time.
181    ///
182    /// **Never** `MADV_WILLNEED`. The text embedding is ~622 MB of which one synthesis touches a
183    /// few kilobytes; prefetching it wholesale would dominate startup and evict the very pages
184    /// [`PagePolicy::Resident`] exists to protect.
185    LazyRowGranular,
186    /// Map lazily, no advice. Enrollment sections a synthesis run never touches.
187    OnDemand,
188}
189
190impl PagePolicy {
191    /// The stable wire string, for `ftts inspect` and the loader's trace events.
192    #[must_use]
193    pub const fn as_str(self) -> &'static str {
194        match self {
195            Self::Resident => "resident",
196            Self::LazyRowGranular => "lazy_row_granular",
197            Self::OnDemand => "on_demand",
198        }
199    }
200
201    /// Whether the loader may issue `MADV_WILLNEED` for this policy.
202    ///
203    /// The single invariant the whole policy exists to enforce. Expressed as its own predicate so
204    /// the syscall island can assert on it directly rather than re-deriving it from the class.
205    #[must_use]
206    pub const fn may_prefetch(self) -> bool {
207        matches!(self, Self::Resident)
208    }
209}
210
211impl fmt::Display for PagePolicy {
212    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
213        f.write_str(self.as_str())
214    }
215}
216
217impl AccessClass {
218    /// The page-in policy for this access class. Total, and the only place the mapping is defined.
219    #[must_use]
220    pub const fn page_policy(self) -> PagePolicy {
221        match self {
222            Self::HotRecurrentMicrodecoder | Self::HotRecurrentTalker | Self::HotCodecDecoder => {
223                PagePolicy::Resident
224            }
225            Self::ColdTextEmbedding => PagePolicy::LazyRowGranular,
226            // Metadata is tiny and read once at load; enrollment sections are untouched by
227            // synthesis. Neither earns a prefetch that would compete with the hot pack.
228            Self::EnrollmentSpeakerEncoder | Self::EnrollmentCodecEncoder | Self::Metadata => {
229                PagePolicy::OnDemand
230            }
231        }
232    }
233}
234
235/// How a tensor's elements are stored in the container.
236///
237/// Narrow on purpose, and unknown values are refused. The high-precision variants exist because
238/// the quant recipe protects norms, codebooks, and the speaker path — an artifact that claims a
239/// dtype we have never conformed is a refusal, not a best-effort read.
240#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
241pub enum StoredDtype {
242    /// bfloat16, kept verbatim from the checkpoint.
243    Bf16,
244    /// 32-bit float.
245    F32,
246    /// int8 with separate per-group scales.
247    Q8,
248    /// int4, two elements per byte, with separate per-group scales.
249    Q4,
250}
251
252impl StoredDtype {
253    /// The stable wire string.
254    #[must_use]
255    pub const fn as_str(self) -> &'static str {
256        match self {
257            Self::Bf16 => "bf16",
258            Self::F32 => "f32",
259            Self::Q8 => "q8",
260            Self::Q4 => "q4",
261        }
262    }
263
264    /// Parses a wire string, refusing anything unrecognized.
265    #[must_use]
266    pub fn parse(text: &str) -> Option<Self> {
267        Some(match text {
268            "bf16" => Self::Bf16,
269            "f32" => Self::F32,
270            "q8" => Self::Q8,
271            "q4" => Self::Q4,
272            _ => return None,
273        })
274    }
275
276    /// Storage bytes for `elements` values, or `None` on overflow.
277    ///
278    /// Q4 packs two elements per byte and rounds up, so an odd element count still occupies a whole
279    /// trailing byte.
280    #[must_use]
281    pub const fn storage_bytes(self, elements: u64) -> Option<u64> {
282        match self {
283            Self::Bf16 => elements.checked_mul(2),
284            Self::F32 => elements.checked_mul(4),
285            Self::Q8 => Some(elements),
286            Self::Q4 => match elements.checked_add(1) {
287                Some(padded) => Some(padded / 2),
288                None => None,
289            },
290        }
291    }
292}
293
294impl fmt::Display for StoredDtype {
295    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
296        f.write_str(self.as_str())
297    }
298}
299
300/// One access-class section: a contiguous, digest-verified byte range.
301#[derive(Clone, Debug, PartialEq, Eq)]
302pub struct SectionEntry {
303    /// Section name, unique within the artifact.
304    pub name: String,
305    /// How the runtime touches it.
306    pub access_class: AccessClass,
307    /// Absolute offset into the file.
308    pub offset: u64,
309    /// Byte length.
310    pub length: u64,
311    /// Lowercase-hex SHA-256 of the section bytes.
312    pub sha256: String,
313}
314
315impl SectionEntry {
316    /// Exclusive end offset, or `None` on overflow.
317    #[must_use]
318    pub const fn end(&self) -> Option<u64> {
319        self.offset.checked_add(self.length)
320    }
321}
322
323/// One tensor, located relative to the start of its section.
324#[derive(Clone, Debug, PartialEq, Eq)]
325pub struct TensorEntry {
326    /// Tensor name, unique within the artifact.
327    pub name: String,
328    /// Name of the section holding it.
329    pub section: String,
330    /// Storage dtype.
331    pub dtype: StoredDtype,
332    /// Logical shape.
333    pub shape: Vec<u64>,
334    /// Offset **relative to the section start**, so a section can move without rewriting tensors.
335    pub offset: u64,
336    /// Byte length; must equal the size implied by shape and dtype.
337    pub length: u64,
338    /// Name of the tensor holding this one's quantization scales, when quantized.
339    pub scales: Option<String>,
340}
341
342impl TensorEntry {
343    /// Element count, or `None` on overflow.
344    #[must_use]
345    pub fn elements(&self) -> Option<u64> {
346        self.shape
347            .iter()
348            .try_fold(1_u64, |acc, &d| acc.checked_mul(d))
349    }
350}
351
352/// What went wrong reading an artifact.
353///
354/// Every variant names the offending section, tensor, or offset: a refusal that does not say what
355/// it refused costs an hour of bisecting a multi-gigabyte file.
356#[derive(Clone, Debug, PartialEq, Eq)]
357pub enum FttsqError {
358    /// File is shorter than the fixed header prefix.
359    TooShort {
360        /// Bytes actually present.
361        length: u64,
362    },
363    /// Magic bytes are not `FTTSQ`.
364    BadMagic {
365        /// The first eight bytes found.
366        found: [u8; 8],
367    },
368    /// The artifact is newer than this binary understands.
369    UnsupportedVersion {
370        /// Version recorded in the file.
371        found: u32,
372        /// Newest version this binary reads.
373        supported: u32,
374    },
375    /// The directory length is implausible or does not fit in the file.
376    DirectoryLength {
377        /// Declared length.
378        declared: u64,
379        /// Cap or file length it violated.
380        limit: u64,
381    },
382    /// The directory is not valid UTF-8 JSON, or is not an object.
383    DirectoryMalformed {
384        /// What the parser said.
385        detail: String,
386    },
387    /// A required field is missing or the wrong type.
388    Field {
389        /// JSON path of the field.
390        path: String,
391        /// What was expected.
392        expected: String,
393    },
394    /// An enum carried a value this binary does not know.
395    UnknownValue {
396        /// JSON path of the field.
397        path: String,
398        /// The unrecognized value.
399        found: String,
400    },
401    /// A declared count or dimension exceeds its cap.
402    LimitExceeded {
403        /// What was being counted.
404        what: String,
405        /// Declared value.
406        found: u64,
407        /// The cap.
408        limit: u64,
409    },
410    /// A byte range overflowed or ran past the end of its container.
411    RangeOutOfBounds {
412        /// What the range belongs to.
413        what: String,
414        /// Start offset.
415        offset: u64,
416        /// Length.
417        length: u64,
418        /// The bound it violated.
419        bound: u64,
420    },
421    /// Two sections claim overlapping bytes.
422    SectionOverlap {
423        /// The earlier section.
424        first: String,
425        /// The later section.
426        second: String,
427    },
428    /// Two tensors in one section claim overlapping bytes.
429    TensorOverlap {
430        /// The earlier tensor.
431        first: String,
432        /// The later tensor.
433        second: String,
434    },
435    /// A name is declared twice.
436    DuplicateName {
437        /// What kind of thing.
438        what: String,
439        /// The repeated name.
440        name: String,
441    },
442    /// A tensor references a section that does not exist.
443    UnknownSection {
444        /// The tensor.
445        tensor: String,
446        /// The section it named.
447        section: String,
448    },
449    /// A tensor's declared length disagrees with its shape and dtype.
450    LengthMismatch {
451        /// The tensor.
452        tensor: String,
453        /// Length the directory declared.
454        declared: u64,
455        /// Length implied by shape and dtype.
456        implied: u64,
457    },
458    /// A section's bytes do not match its recorded digest.
459    DigestMismatch {
460        /// The section.
461        section: String,
462        /// Digest the directory recorded.
463        expected: String,
464        /// Digest the bytes actually produce.
465        actual: String,
466    },
467    /// The mandatory license notice is absent or empty.
468    ///
469    /// Apache-2.0 §4 attaches to every artifact we publish; an artifact without the notice must not
470    /// be readable, or the obligation becomes advisory in practice.
471    LicenseNoticeMissing,
472    /// A streaming writer received bytes for a section other than the next declared one.
473    SectionWriteOutOfOrder {
474        /// Section the stream expected next, or `None` once all sections are complete.
475        expected: Option<String>,
476        /// Section the caller attempted to write.
477        actual: String,
478    },
479    /// A streaming writer was given more bytes than its declared section length.
480    SectionLengthExceeded {
481        /// Section receiving bytes.
482        section: String,
483        /// Length fixed in the conversion plan.
484        declared: u64,
485        /// Total bytes the write would have produced.
486        attempted: u64,
487    },
488    /// A streaming writer was finalized before its current section was complete.
489    SectionIncomplete {
490        /// Section still awaiting bytes.
491        section: String,
492        /// Length fixed in the conversion plan.
493        declared: u64,
494        /// Bytes successfully written so far.
495        written: u64,
496    },
497    /// A filesystem operation failed.
498    ///
499    /// Carries the rendered message rather than [`std::io::Error`] so this enum stays `Clone` and
500    /// `PartialEq` — properties the tests and the fuzz target rely on.
501    Io {
502        /// What was being attempted.
503        operation: String,
504        /// The path involved.
505        path: String,
506        /// The OS error text.
507        detail: String,
508    },
509}
510
511impl fmt::Display for FttsqError {
512    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
513        match self {
514            Self::TooShort { length } => write!(
515                f,
516                "not a .fttsq artifact: {length} bytes is shorter than the {HEADER_PREFIX_BYTES}-byte header"
517            ),
518            Self::BadMagic { found } => {
519                write!(f, "not a .fttsq artifact: magic {found:?} is not {MAGIC:?}")
520            }
521            Self::UnsupportedVersion { found, supported } => write!(
522                f,
523                "artifact format version {found} is newer than this binary supports ({supported}); \
524                 upgrade ftts rather than reading it with a stale layout"
525            ),
526            Self::DirectoryLength { declared, limit } => {
527                write!(f, "directory length {declared} exceeds {limit}")
528            }
529            Self::DirectoryMalformed { detail } => write!(f, "directory is malformed: {detail}"),
530            Self::Field { path, expected } => {
531                write!(f, "directory field `{path}` is missing or not {expected}")
532            }
533            Self::UnknownValue { path, found } => write!(
534                f,
535                "directory field `{path}` has unknown value `{found}`; this artifact needs a newer ftts"
536            ),
537            Self::LimitExceeded { what, found, limit } => {
538                write!(f, "{what} count {found} exceeds the cap of {limit}")
539            }
540            Self::RangeOutOfBounds {
541                what,
542                offset,
543                length,
544                bound,
545            } => write!(
546                f,
547                "{what} range [{offset}, {offset}+{length}) runs past its bound {bound}"
548            ),
549            Self::SectionOverlap { first, second } => write!(
550                f,
551                "sections `{first}` and `{second}` claim overlapping bytes"
552            ),
553            Self::TensorOverlap { first, second } => write!(
554                f,
555                "tensors `{first}` and `{second}` claim overlapping bytes"
556            ),
557            Self::DuplicateName { what, name } => write!(f, "{what} `{name}` is declared twice"),
558            Self::UnknownSection { tensor, section } => write!(
559                f,
560                "tensor `{tensor}` names section `{section}`, which is not declared"
561            ),
562            Self::LengthMismatch {
563                tensor,
564                declared,
565                implied,
566            } => write!(
567                f,
568                "tensor `{tensor}` declares {declared} bytes but its shape and dtype imply {implied}"
569            ),
570            Self::DigestMismatch {
571                section,
572                expected,
573                actual,
574            } => write!(
575                f,
576                "section `{section}` is corrupt: recorded sha256 {expected}, computed {actual}"
577            ),
578            Self::LicenseNoticeMissing => f.write_str(
579                "artifact carries no license_notice; Apache-2.0 §4 requires it on every published \
580                 artifact, so an artifact without one is refused rather than silently accepted",
581            ),
582            Self::SectionWriteOutOfOrder { expected, actual } => match expected {
583                Some(expected) => write!(
584                    f,
585                    "streaming .fttsq writer expected section `{expected}`, not `{actual}`"
586                ),
587                None => write!(
588                    f,
589                    "streaming .fttsq writer is complete and cannot accept section `{actual}`"
590                ),
591            },
592            Self::SectionLengthExceeded {
593                section,
594                declared,
595                attempted,
596            } => write!(
597                f,
598                "section `{section}` declares {declared} bytes but streaming write would reach {attempted}"
599            ),
600            Self::SectionIncomplete {
601                section,
602                declared,
603                written,
604            } => write!(
605                f,
606                "section `{section}` declares {declared} bytes but only {written} were written"
607            ),
608            Self::Io {
609                operation,
610                path,
611                detail,
612            } => write!(f, "{operation} failed for `{path}`: {detail}"),
613        }
614    }
615}
616
617impl std::error::Error for FttsqError {}
618
619/// A parsed, validated `.fttsq` directory over a buffer the reader does not own.
620#[derive(Clone, Debug)]
621pub struct FttsqReader {
622    format_version: u32,
623    model_family: String,
624    source_sha256: String,
625    license_notice: String,
626    model_config: Value,
627    quantization_manifest: Value,
628    sections: Vec<SectionEntry>,
629    tensors: Vec<TensorEntry>,
630    section_index: BTreeMap<String, usize>,
631    tensor_index: BTreeMap<String, usize>,
632}
633
634/// The observable result of applying one access-class policy to a mapped section.
635///
636/// An advice failure is reported but does not reject an otherwise valid artifact: `madvise` is a
637/// performance hint, never an integrity mechanism. The reader still verifies every section digest
638/// before this record is produced, so a failed hint cannot turn corruption into a delayed fault.
639#[derive(Clone, Debug, PartialEq, Eq)]
640pub enum PageAdviceOutcome {
641    /// The access class requires no syscall; the section remains lazily mapped.
642    NotRequested,
643    /// The matching native advisory request succeeded.
644    Applied,
645    /// The section has no bytes to advise.
646    SkippedEmpty,
647    /// The build/platform uses the safe owned-byte fallback instead of an unimplemented OS FFI.
648    Unsupported,
649    /// The OS rejected a performance hint; artifact correctness is unaffected.
650    Failed(String),
651}
652
653/// An observed residency count for one section at a policy boundary.
654///
655/// This records a measurement for the OQ-18 access-class evidence, not a contract with the kernel:
656/// page-cache state can change immediately after `mincore` returns, and a failed observation must
657/// never reject an otherwise valid artifact.
658#[derive(Clone, Debug, PartialEq, Eq)]
659pub enum PageResidencyOutcome {
660    /// The kernel reported the number of resident pages in the section's range.
661    Measured {
662        /// Pages resident at the observation point.
663        resident_pages: usize,
664        /// Pages spanned by this section.
665        total_pages: usize,
666    },
667    /// This build has no audited residency-query implementation.
668    Unsupported,
669    /// The OS rejected the observation; artifact integrity is unaffected.
670    Failed(String),
671}
672
673/// One section's page-in decision and what the loader actually requested.
674#[derive(Clone, Debug, PartialEq, Eq)]
675pub struct PageAdviceApplication {
676    /// Artifact section name.
677    pub section: String,
678    /// The pure access-class policy selected by [`AccessClass::page_policy`].
679    pub policy: PagePolicy,
680    /// The OS hint issued for this section, if the policy calls for one.
681    pub requested: Option<MemoryAdvice>,
682    /// Residency observed immediately before the policy request.
683    pub residency_before: PageResidencyOutcome,
684    /// The outcome of that request.
685    pub outcome: PageAdviceOutcome,
686    /// Residency observed immediately after the policy request and before eager v1 digest checks.
687    pub residency_after: PageResidencyOutcome,
688}
689
690/// A fully verified `.fttsq` held as a read-only mapping (or the safe owned-byte fallback).
691///
692/// The mapping owns the bytes while [`FttsqReader`] owns only the validated directory, so tensor
693/// views borrow the artifact rather than duplicating multi-gigabyte payloads. The loader validates
694/// the directory's structure and ranges, applies bounded OS advice, then verifies every digest
695/// before returning. That ordering lets `MADV_WILLNEED` begin while validation runs; the mapping is
696/// never exposed to callers until the hardened reader has accepted every section digest.
697#[derive(Debug)]
698pub struct MappedFttsq {
699    mapping: MappedFile,
700    reader: FttsqReader,
701    page_advice: Vec<PageAdviceApplication>,
702    /// Sections the backing bytes stop short of, in declaration order.
703    ///
704    /// Empty for every whole-file load, which is every native load. Non-empty only via
705    /// [`MappedFttsq::from_prefix_bytes`], where a caller has deliberately declined to hold a
706    /// trailing section. Reads into an absent section are already refused by the range checks in
707    /// [`FttsqReader::tensor_bytes`]; this list exists so callers can ask BEFORE reading, and so
708    /// the refusal can name the cause rather than looking like a corrupt artifact.
709    absent_sections: Vec<String>,
710}
711
712impl MappedFttsq {
713    /// Map, fully validate, and apply the access-class page-in plan to an artifact.
714    ///
715    /// The integrity check remains eager because the v1 format carries a digest per section rather
716    /// than a per-page Merkle tree. Consequently, this loader establishes a *policy* guarantee —
717    /// the cold embedding is never sent `MADV_WILLNEED` — and records the pre-digest residency
718    /// measurement, not a claim that opening v1 avoids every cold-section page fault. A
719    /// page-granular integrity format is required before that stronger claim can be made honestly.
720    ///
721    /// # Errors
722    ///
723    /// Returns a named [`FttsqError`] for mapping, structural, or digest-validation failures.
724    pub fn open(path: impl AsRef<std::path::Path>) -> Result<Self, FttsqError> {
725        let path = path.as_ref();
726        let mapping = MappedFile::open(path).map_err(|error| FttsqError::Io {
727            operation: "memory-map artifact".to_owned(),
728            path: path.display().to_string(),
729            detail: error.to_string(),
730        })?;
731        // Structural validation bounds each requested range before an OS call. The subsequent
732        // digest pass is still mandatory before this mapping can be returned to a caller.
733        let reader = FttsqReader::parse_directory(mapping.as_slice())?;
734        let page_advice = apply_page_in_plan(&mapping, &reader);
735        reader.verify_digests(mapping.as_slice())?;
736        Ok(Self {
737            mapping,
738            reader,
739            page_advice,
740            absent_sections: Vec::new(),
741        })
742    }
743
744    /// Validate an artifact held wholly in memory, for targets with no filesystem (wasm32).
745    ///
746    /// Runs the identical structural-then-digest pipeline as [`MappedFttsq::open`]; only the
747    /// byte source differs, so a buffer that verifies here is exactly a file that would have
748    /// verified there.
749    ///
750    /// # Errors
751    ///
752    /// Returns the same named [`FttsqError`] classes as [`MappedFttsq::open`].
753    #[cfg(not(unix))] // the owned-bytes MappedFile backing (and its from_bytes) exists off-unix
754    pub fn from_bytes(bytes: Vec<u8>) -> Result<Self, FttsqError> {
755        let mapping = MappedFile::from_bytes(bytes);
756        let reader = FttsqReader::parse_directory(mapping.as_slice())?;
757        let page_advice = apply_page_in_plan(&mapping, &reader);
758        reader.verify_digests(mapping.as_slice())?;
759        Ok(Self {
760            mapping,
761            reader,
762            page_advice,
763            absent_sections: Vec::new(),
764        })
765    }
766
767    /// Load a PREFIX of an artifact: every section it fully contains is verified, the rest are
768    /// recorded as absent.
769    ///
770    /// # Why a partial artifact is a legitimate thing to want
771    ///
772    /// [`AccessClass::ColdTextEmbedding`] is 622 MB — 47% of the artifact — and the format already
773    /// says what it is for: lazy, row-granular, never paged in wholesale. A native host gets that
774    /// for free, because the mapping only faults in the rows an utterance actually touches.
775    /// wasm has no mapping. Every byte staged into linear memory is a byte permanently resident,
776    /// and the wasm heap is grow-only — a tab can never hand memory back — so on a phone the cold
777    /// table is 622 MB of ballast held for the lifetime of the page to serve a few hundred 4 KB
778    /// rows per utterance.
779    ///
780    /// So the browser stops the buffer where the cold section starts and reads those rows from
781    /// OPFS instead. That works ONLY because the cold section is last in the file: every other
782    /// section keeps its true offset, so nothing needs remapping and no offset arithmetic changes.
783    /// A future layout that put a hot section after a cold one would break this, which is why the
784    /// prefix length is derived from the directory rather than assumed
785    /// ([`FttsqReader::prefix_len_omitting`]).
786    ///
787    /// # Integrity
788    ///
789    /// Undiminished for what is present. The v1 format carries a digest per section, so a prefix
790    /// verifies exactly the sections it holds, in full, with the same SHA-256 comparison as a
791    /// whole-file load. What is NOT present is not vouched for here — the caller holding the rest
792    /// out of band is responsible for it (the browser verifies the whole file on download, before
793    /// it ever reaches this path).
794    ///
795    /// # Errors
796    ///
797    /// As [`MappedFttsq::from_bytes`], plus a refusal when the prefix is too short to contain the
798    /// directory itself, or when it splits a section rather than stopping cleanly before one — a
799    /// partial section cannot be verified, so it is rejected rather than silently trusted.
800    #[cfg(not(unix))] // same owned-bytes backing as `from_bytes`; a mapping never needs this
801    pub fn from_prefix_bytes(bytes: Vec<u8>, file_len: u64) -> Result<Self, FttsqError> {
802        let mapping = MappedFile::from_bytes(bytes);
803        // Against the artifact's REAL length, not the bytes in hand: the elided section runs past
804        // the prefix by construction, so bounding by what is present rejects every prefix outright.
805        let reader = FttsqReader::parse_directory_of_prefix(mapping.as_slice(), file_len)?;
806        let available = mapping.as_slice().len() as u64;
807
808        let absent_sections = reader.absent_sections_in_prefix(available)?;
809        let page_advice = apply_page_in_plan(&mapping, &reader);
810        reader.verify_digests_of_present(mapping.as_slice())?;
811        Ok(Self {
812            mapping,
813            reader,
814            page_advice,
815            absent_sections,
816        })
817    }
818
819    /// Sections this artifact's bytes stop short of; empty for a whole-file load.
820    #[must_use]
821    pub fn absent_sections(&self) -> &[String] {
822        &self.absent_sections
823    }
824
825    /// Whether a named section's bytes are actually held.
826    #[must_use]
827    pub fn has_section(&self, name: &str) -> bool {
828        self.reader.section(name).is_some() && !self.absent_sections.iter().any(|s| s == name)
829    }
830
831    /// The fully validated directory over this artifact.
832    #[must_use]
833    pub const fn reader(&self) -> &FttsqReader {
834        &self.reader
835    }
836
837    /// The policy application record, in the same priority order as `page_in_plan`.
838    #[must_use]
839    pub fn page_advice(&self) -> &[PageAdviceApplication] {
840        &self.page_advice
841    }
842
843    /// Borrow one tensor's logical bytes without copying the mapped payload.
844    ///
845    /// # Errors
846    ///
847    /// Returns the same named lookup/range errors as [`FttsqReader::tensor_bytes`].
848    pub fn tensor_bytes(&self, name: &str) -> Result<&[u8], FttsqError> {
849        self.reader.tensor_bytes(name, self.mapping.as_slice())
850    }
851
852    /// The artifact's mapped (or fallback-owned) byte length.
853    #[must_use]
854    pub fn len(&self) -> usize {
855        self.mapping.len()
856    }
857
858    /// Whether this artifact is empty.
859    #[must_use]
860    pub fn is_empty(&self) -> bool {
861        self.mapping.is_empty()
862    }
863}
864
865fn apply_page_in_plan(mapping: &MappedFile, reader: &FttsqReader) -> Vec<PageAdviceApplication> {
866    reader
867        .page_in_plan()
868        .into_iter()
869        .map(|(section, policy)| {
870            let requested = match policy {
871                PagePolicy::Resident => Some(MemoryAdvice::WillNeed),
872                PagePolicy::LazyRowGranular => Some(MemoryAdvice::Random),
873                PagePolicy::OnDemand => None,
874            };
875
876            // This is intentionally a runtime assertion rather than a documentation promise. A
877            // future policy refactor must fail immediately if it ever routes the 622 MB cold text
878            // embedding through `MADV_WILLNEED`.
879            assert!(
880                policy.may_prefetch() || requested != Some(MemoryAdvice::WillNeed),
881                "a non-prefetch policy must never issue MADV_WILLNEED"
882            );
883
884            let residency_before = observe_residency(mapping, section.offset, section.length);
885            let outcome = match requested {
886                Some(advice) => match mapping.advise(section.offset, section.length, advice) {
887                    Ok(MemoryAdviceOutcome::Applied) => PageAdviceOutcome::Applied,
888                    Ok(MemoryAdviceOutcome::SkippedEmpty) => PageAdviceOutcome::SkippedEmpty,
889                    Ok(MemoryAdviceOutcome::Unsupported) => PageAdviceOutcome::Unsupported,
890                    Err(error) => PageAdviceOutcome::Failed(error.to_string()),
891                },
892                None => PageAdviceOutcome::NotRequested,
893            };
894            let residency_after = observe_residency(mapping, section.offset, section.length);
895
896            PageAdviceApplication {
897                section: section.name.clone(),
898                policy,
899                requested,
900                residency_before,
901                outcome,
902                residency_after,
903            }
904        })
905        .collect()
906}
907
908fn observe_residency(mapping: &MappedFile, offset: u64, length: u64) -> PageResidencyOutcome {
909    match mapping.resident_pages(offset, length) {
910        Ok(MemoryResidency::Measured {
911            resident_pages,
912            total_pages,
913        }) => PageResidencyOutcome::Measured {
914            resident_pages,
915            total_pages,
916        },
917        Ok(MemoryResidency::Unsupported) => PageResidencyOutcome::Unsupported,
918        Err(error) => PageResidencyOutcome::Failed(error.to_string()),
919    }
920}
921
922impl FttsqReader {
923    /// Parses and fully validates an artifact, **including** every section digest.
924    ///
925    /// Digest verification is not optional here. A reader that can be asked to skip it grows a
926    /// caller that always skips it, and then corruption surfaces as audio rather than as an error.
927    ///
928    /// # Errors
929    ///
930    /// Returns a named [`FttsqError`] for any structural, range, or integrity violation.
931    pub fn open(bytes: &[u8]) -> Result<Self, FttsqError> {
932        let reader = Self::parse_directory(bytes)?;
933        reader.verify_digests(bytes)?;
934        Ok(reader)
935    }
936
937    /// Parses and structurally validates without computing digests.
938    ///
939    /// For inspection tooling (`ftts inspect`) over an artifact whose bytes are not all present —
940    /// listing a remote artifact's tensors, say. Never use this to load weights: it does not prove
941    /// the payload is intact.
942    ///
943    /// # Errors
944    ///
945    /// Returns a named [`FttsqError`] for any structural or range violation.
946    pub fn parse_directory(bytes: &[u8]) -> Result<Self, FttsqError> {
947        Self::parse_directory_for_file_len(bytes, bytes.len() as u64)
948    }
949
950    /// Parses the directory of a PREFIX, validating ranges against the artifact's full length.
951    ///
952    /// [`FttsqReader::parse_directory`] bounds every declared range by the bytes in hand, which is
953    /// right for a whole file and wrong for a deliberate prefix: the section being left behind
954    /// necessarily runs past the end, so parsing refuses before absence can even be considered.
955    /// Here the structural check keeps its full strength against `file_len` — the artifact's real
956    /// size, known independently — while only the header and directory need to be present.
957    ///
958    /// This proves structure, never integrity. Digests still decide what may be read, via
959    /// [`FttsqReader::verify_digests_of_present`].
960    ///
961    /// # Errors
962    ///
963    /// Returns a named [`FttsqError`] for any structural or range violation.
964    pub fn parse_directory_of_prefix(bytes: &[u8], file_len: u64) -> Result<Self, FttsqError> {
965        Self::parse_directory_for_file_len(bytes, file_len)
966    }
967
968    /// Parses a present header and directory against a declared final file length.
969    ///
970    /// The streaming writer uses this before accepting payload bytes: only the header and
971    /// directory are in memory at that point, but all declared section and tensor ranges must
972    /// already be valid for the eventual artifact length. It stays private so callers cannot
973    /// mistake a structural preflight for a verified artifact load.
974    fn parse_directory_for_file_len(bytes: &[u8], file_len: u64) -> Result<Self, FttsqError> {
975        let present_len = bytes.len() as u64;
976        if present_len < HEADER_PREFIX_BYTES {
977            return Err(FttsqError::TooShort {
978                length: present_len,
979            });
980        }
981
982        let mut magic = [0_u8; 8];
983        magic.copy_from_slice(&bytes[..8]);
984        if &magic != MAGIC {
985            return Err(FttsqError::BadMagic { found: magic });
986        }
987
988        let format_version = u32::from_le_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]);
989        // Version 0 was never issued: accepting it would silently read an unversioned or
990        // zero-filled header as v1. Valid artifacts carry 1..=FORMAT_VERSION.
991        if format_version == 0 || format_version > FORMAT_VERSION {
992            return Err(FttsqError::UnsupportedVersion {
993                found: format_version,
994                supported: FORMAT_VERSION,
995            });
996        }
997
998        let mut length_bytes = [0_u8; 8];
999        length_bytes.copy_from_slice(&bytes[12..20]);
1000        let directory_len = u64::from_le_bytes(length_bytes);
1001        if directory_len > MAX_DIRECTORY_BYTES {
1002            return Err(FttsqError::DirectoryLength {
1003                declared: directory_len,
1004                limit: MAX_DIRECTORY_BYTES,
1005            });
1006        }
1007        let directory_end =
1008            HEADER_PREFIX_BYTES
1009                .checked_add(directory_len)
1010                .ok_or(FttsqError::DirectoryLength {
1011                    declared: directory_len,
1012                    limit: u64::MAX,
1013                })?;
1014        if directory_end > present_len || directory_end > file_len {
1015            return Err(FttsqError::DirectoryLength {
1016                declared: directory_len,
1017                limit: present_len.min(file_len),
1018            });
1019        }
1020
1021        // `directory_end` is bounded by `file_len` above, so both casts are in range.
1022        let directory_bytes = &bytes[HEADER_PREFIX_BYTES as usize..directory_end as usize];
1023        let directory: Value = serde_json::from_slice(directory_bytes).map_err(|error| {
1024            FttsqError::DirectoryMalformed {
1025                detail: error.to_string(),
1026            }
1027        })?;
1028        let object = directory
1029            .as_object()
1030            .ok_or_else(|| FttsqError::DirectoryMalformed {
1031                detail: "top level is not a JSON object".to_owned(),
1032            })?;
1033
1034        let model_family = required_str(object.get("model_family"), "model_family")?.to_owned();
1035        let source_sha256 = required_str(object.get("source_sha256"), "source_sha256")?.to_owned();
1036
1037        // Apache-2.0 §4 compliance is a read-time gate, not a writer-side courtesy.
1038        let license_notice = object
1039            .get("license_notice")
1040            .and_then(Value::as_str)
1041            .unwrap_or_default()
1042            .to_owned();
1043        if license_notice.trim().is_empty() {
1044            return Err(FttsqError::LicenseNoticeMissing);
1045        }
1046
1047        let model_config = object.get("model_config").cloned().unwrap_or(Value::Null);
1048        let quantization_manifest = object
1049            .get("quantization_manifest")
1050            .cloned()
1051            .unwrap_or(Value::Null);
1052
1053        let sections = parse_sections(object.get("sections"), file_len)?;
1054        let section_index: BTreeMap<String, usize> = sections
1055            .iter()
1056            .enumerate()
1057            .map(|(index, section)| (section.name.clone(), index))
1058            .collect();
1059        let tensors = parse_tensors(object.get("tensors"), &sections, &section_index)?;
1060        let tensor_index: BTreeMap<String, usize> = tensors
1061            .iter()
1062            .enumerate()
1063            .map(|(index, tensor)| (tensor.name.clone(), index))
1064            .collect();
1065
1066        Ok(Self {
1067            format_version,
1068            model_family,
1069            source_sha256,
1070            license_notice,
1071            model_config,
1072            quantization_manifest,
1073            sections,
1074            tensors,
1075            section_index,
1076            tensor_index,
1077        })
1078    }
1079
1080    /// Recomputes and checks every section digest against `bytes`.
1081    ///
1082    /// # Errors
1083    ///
1084    /// Returns [`FttsqError::DigestMismatch`] naming the first corrupt section.
1085    pub fn verify_digests(&self, bytes: &[u8]) -> Result<(), FttsqError> {
1086        for section in &self.sections {
1087            let payload = self.section_bytes(section, bytes)?;
1088            let actual = to_hex(&crate::sha256::digest(payload));
1089            if actual != section.sha256 {
1090                return Err(FttsqError::DigestMismatch {
1091                    section: section.name.clone(),
1092                    expected: section.sha256.clone(),
1093                    actual,
1094                });
1095            }
1096        }
1097        Ok(())
1098    }
1099
1100    /// Verify every section the bytes fully contain, skipping those they stop short of.
1101    ///
1102    /// The skip is deliberately silent because the caller has already classified what is absent
1103    /// and refused any section that was merely truncated — see [`MappedFttsq::from_prefix_bytes`].
1104    /// What this verifies, it verifies exactly as [`FttsqReader::verify_digests`] does.
1105    ///
1106    /// # Errors
1107    ///
1108    /// On any present section whose payload does not match its declared digest.
1109    pub fn verify_digests_of_present(&self, bytes: &[u8]) -> Result<(), FttsqError> {
1110        for section in &self.sections {
1111            let Some(end) = section.end() else { continue };
1112            if end > bytes.len() as u64 {
1113                continue;
1114            }
1115            let payload = self.section_bytes(section, bytes)?;
1116            let actual = to_hex(&crate::sha256::digest(payload));
1117            if actual != section.sha256 {
1118                return Err(FttsqError::DigestMismatch {
1119                    section: section.name.clone(),
1120                    expected: section.sha256.clone(),
1121                    actual,
1122                });
1123            }
1124        }
1125        Ok(())
1126    }
1127
1128    /// Which sections a prefix of `available` bytes stops short of entirely.
1129    ///
1130    /// A section is absent when it starts at or after the cut, and present when it ends at or
1131    /// before it. A section straddling the cut is neither: it cannot be digest-checked and must
1132    /// not be read, so it is an error rather than a third category. That distinction is what
1133    /// separates "the caller deliberately declined a trailing section" from "the download stopped
1134    /// early", which would otherwise both arrive here looking identical.
1135    ///
1136    /// # Errors
1137    ///
1138    /// When the prefix splits a section, or a declared section's range overflows.
1139    pub fn absent_sections_in_prefix(&self, available: u64) -> Result<Vec<String>, FttsqError> {
1140        let mut absent = Vec::new();
1141        for section in &self.sections {
1142            let end = section.end().ok_or_else(|| FttsqError::RangeOutOfBounds {
1143                what: format!("section `{}`", section.name),
1144                offset: section.offset,
1145                length: section.length,
1146                bound: available,
1147            })?;
1148            if end <= available {
1149                continue;
1150            }
1151            if section.offset < available {
1152                return Err(FttsqError::RangeOutOfBounds {
1153                    what: format!("prefix splits section `{}`", section.name),
1154                    offset: section.offset,
1155                    length: section.length,
1156                    bound: available,
1157                });
1158            }
1159            absent.push(section.name.clone());
1160        }
1161        Ok(absent)
1162    }
1163
1164    /// The shortest prefix that holds every section EXCEPT those in `omit`.
1165    ///
1166    /// Returns `None` when omitting those classes would not leave a contiguous prefix — i.e. when
1167    /// a retained section sits after an omitted one. That is the guard that keeps the browser's
1168    /// truncation honest against a future layout change: the saving depends on the omitted
1169    /// sections being last, and this is what refuses to assume it.
1170    #[must_use]
1171    pub fn prefix_len_omitting(&self, omit: &[AccessClass]) -> Option<u64> {
1172        let omitted = |section: &SectionEntry| omit.contains(&section.access_class);
1173        let first_omitted = self
1174            .sections
1175            .iter()
1176            .filter(|section| omitted(section))
1177            .map(|section| section.offset)
1178            .min()?;
1179        // Nothing retained may live at or beyond that point, or the prefix would drop it too.
1180        if self
1181            .sections
1182            .iter()
1183            .any(|section| !omitted(section) && section.offset >= first_omitted)
1184        {
1185            return None;
1186        }
1187        Some(first_omitted)
1188    }
1189
1190    fn section_bytes<'a>(
1191        &self,
1192        section: &SectionEntry,
1193        bytes: &'a [u8],
1194    ) -> Result<&'a [u8], FttsqError> {
1195        let end = section.end().ok_or_else(|| FttsqError::RangeOutOfBounds {
1196            what: format!("section `{}`", section.name),
1197            offset: section.offset,
1198            length: section.length,
1199            bound: bytes.len() as u64,
1200        })?;
1201        if end > bytes.len() as u64 {
1202            return Err(FttsqError::RangeOutOfBounds {
1203                what: format!("section `{}`", section.name),
1204                offset: section.offset,
1205                length: section.length,
1206                bound: bytes.len() as u64,
1207            });
1208        }
1209        Ok(&bytes[section.offset as usize..end as usize])
1210    }
1211
1212    /// The format version the artifact declares.
1213    #[must_use]
1214    pub const fn format_version(&self) -> u32 {
1215        self.format_version
1216    }
1217
1218    /// The model family the artifact was built from.
1219    #[must_use]
1220    pub fn model_family(&self) -> &str {
1221        &self.model_family
1222    }
1223
1224    /// SHA-256 of the upstream checkpoint this artifact was converted from.
1225    #[must_use]
1226    pub fn source_sha256(&self) -> &str {
1227        &self.source_sha256
1228    }
1229
1230    /// The Apache-2.0 §4 attribution notice. Guaranteed non-empty.
1231    #[must_use]
1232    pub fn license_notice(&self) -> &str {
1233        &self.license_notice
1234    }
1235
1236    /// The frozen copy of the upstream model config.
1237    #[must_use]
1238    pub const fn model_config(&self) -> &Value {
1239        &self.model_config
1240    }
1241
1242    /// The per-tensor quantization policy actually applied, which the license notice references.
1243    #[must_use]
1244    pub const fn quantization_manifest(&self) -> &Value {
1245        &self.quantization_manifest
1246    }
1247
1248    /// Every declared section, in declaration order.
1249    #[must_use]
1250    pub fn sections(&self) -> &[SectionEntry] {
1251        &self.sections
1252    }
1253
1254    /// Every declared tensor, in declaration order.
1255    #[must_use]
1256    pub fn tensors(&self) -> &[TensorEntry] {
1257        &self.tensors
1258    }
1259
1260    /// Looks a section up by name.
1261    #[must_use]
1262    pub fn section(&self, name: &str) -> Option<&SectionEntry> {
1263        self.section_index
1264            .get(name)
1265            .and_then(|&index| self.sections.get(index))
1266    }
1267
1268    /// Looks a tensor up by name.
1269    #[must_use]
1270    pub fn tensor(&self, name: &str) -> Option<&TensorEntry> {
1271        self.tensor_index
1272            .get(name)
1273            .and_then(|&index| self.tensors.get(index))
1274    }
1275
1276    /// Sections belonging to one access class.
1277    #[must_use]
1278    pub fn sections_in_class(&self, class: AccessClass) -> Vec<&SectionEntry> {
1279        self.sections
1280            .iter()
1281            .filter(|section| section.access_class == class)
1282            .collect()
1283    }
1284
1285    /// The byte span of one tensor within `bytes`.
1286    ///
1287    /// # Errors
1288    ///
1289    /// Returns [`FttsqError::UnknownSection`] when the tensor's section is missing, or
1290    /// [`FttsqError::RangeOutOfBounds`] when the resolved span leaves the buffer.
1291    pub fn tensor_bytes<'a>(&self, name: &str, bytes: &'a [u8]) -> Result<&'a [u8], FttsqError> {
1292        let tensor = self
1293            .tensor(name)
1294            .ok_or_else(|| FttsqError::UnknownSection {
1295                tensor: name.to_owned(),
1296                section: "<unknown tensor>".to_owned(),
1297            })?;
1298        let section = self
1299            .section(&tensor.section)
1300            .ok_or_else(|| FttsqError::UnknownSection {
1301                tensor: tensor.name.clone(),
1302                section: tensor.section.clone(),
1303            })?;
1304        let payload = self.section_bytes(section, bytes)?;
1305        let end = tensor.offset.checked_add(tensor.length).ok_or_else(|| {
1306            FttsqError::RangeOutOfBounds {
1307                what: format!("tensor `{}`", tensor.name),
1308                offset: tensor.offset,
1309                length: tensor.length,
1310                bound: payload.len() as u64,
1311            }
1312        })?;
1313        if end > payload.len() as u64 {
1314            return Err(FttsqError::RangeOutOfBounds {
1315                what: format!("tensor `{}`", tensor.name),
1316                offset: tensor.offset,
1317                length: tensor.length,
1318                bound: payload.len() as u64,
1319            });
1320        }
1321        Ok(&payload[tensor.offset as usize..end as usize])
1322    }
1323
1324    /// The page-in plan: every section paired with its policy, in the order to apply them.
1325    ///
1326    /// Ordered [`PagePolicy::Resident`] first. Prefetch order matters under memory pressure — the
1327    /// section that gets its `MADV_WILLNEED` in last is the one most likely to find the page cache
1328    /// already full, and that must never be the microdecoder pack.
1329    ///
1330    /// Within the resident group, smaller sections come first: the ~110 MB microdecoder pack lands
1331    /// ahead of the ~440 MB talker, so the reread-15×-per-frame working set wins the race for
1332    /// cache it would otherwise lose to a body read once per frame.
1333    #[must_use]
1334    pub fn page_in_plan(&self) -> Vec<(&SectionEntry, PagePolicy)> {
1335        let mut plan: Vec<(&SectionEntry, PagePolicy)> = self
1336            .sections
1337            .iter()
1338            .map(|section| (section, section.access_class.page_policy()))
1339            .collect();
1340        plan.sort_by_key(|(section, policy)| {
1341            let rank = match policy {
1342                PagePolicy::Resident => 0_u8,
1343                PagePolicy::LazyRowGranular => 1,
1344                PagePolicy::OnDemand => 2,
1345            };
1346            (rank, section.length)
1347        });
1348        plan
1349    }
1350
1351    /// Audits the artifact's tensor directory against an expected inventory.
1352    ///
1353    /// # Errors
1354    ///
1355    /// Returns the itemized [`ArtifactCensus`] when anything is missing, extra, or mis-declared.
1356    pub fn verify_census(&self, manifest: &ArtifactManifest) -> Result<(), Box<ArtifactCensus>> {
1357        let report = manifest.audit(self);
1358        if report.is_green() {
1359            Ok(())
1360        } else {
1361            Err(Box::new(report))
1362        }
1363    }
1364}
1365
1366/// One tensor the artifact is required to carry, and where.
1367///
1368/// Distinct from [`crate::census::ExpectedTensor`] on purpose, and the difference is not
1369/// incidental: that one audits an *upstream* checkpoint, so it speaks
1370/// [`crate::safetensors::Dtype`] (bf16/f32) and has no notion of sections. A `.fttsq` is a
1371/// **quantized** artifact, so its expectations are keyed on [`StoredDtype`] and additionally pin
1372/// the [`AccessClass`] — a tensor that lands in the wrong section still loads and still produces
1373/// correct audio, while quietly destroying the residency the whole optimization program depends on.
1374/// That failure is invisible to a dtype-and-shape census, which is exactly why this one exists.
1375#[derive(Clone, Debug, PartialEq, Eq)]
1376pub struct ExpectedArtifactTensor {
1377    /// Tensor name, exactly as the directory records it.
1378    pub name: String,
1379    /// Required logical shape.
1380    pub shape: Vec<u64>,
1381    /// Required storage dtype after quantization.
1382    pub dtype: StoredDtype,
1383    /// The access class whose section must hold it.
1384    pub access_class: AccessClass,
1385}
1386
1387/// One way an artifact diverged from its expected inventory.
1388#[derive(Clone, Debug, PartialEq, Eq)]
1389pub enum ArtifactFinding {
1390    /// A required tensor is absent. Certain failure downstream.
1391    Missing {
1392        /// The tensor.
1393        name: String,
1394    },
1395    /// The artifact carries a tensor the manifest does not list — the signature of a different
1396    /// checkpoint or a converter that changed its naming.
1397    Extra {
1398        /// The tensor.
1399        name: String,
1400    },
1401    /// Present, but not the shape we compiled kernels for.
1402    ShapeMismatch {
1403        /// The tensor.
1404        name: String,
1405        /// Expected shape.
1406        expected: Vec<u64>,
1407        /// Shape found.
1408        found: Vec<u64>,
1409    },
1410    /// Present, but quantized differently than the recipe says.
1411    DtypeMismatch {
1412        /// The tensor.
1413        name: String,
1414        /// Expected dtype.
1415        expected: StoredDtype,
1416        /// Dtype found.
1417        found: StoredDtype,
1418    },
1419    /// Present and correct, but filed under the wrong access class.
1420    ///
1421    /// The quiet one: audio stays correct while the page-in policy silently becomes wrong.
1422    WrongAccessClass {
1423        /// The tensor.
1424        name: String,
1425        /// Expected class.
1426        expected: AccessClass,
1427        /// Class found.
1428        found: AccessClass,
1429    },
1430    /// A tensor names a section the artifact does not declare.
1431    DanglingSection {
1432        /// The tensor.
1433        name: String,
1434        /// The section it named.
1435        section: String,
1436    },
1437}
1438
1439impl ArtifactFinding {
1440    /// The tensor this finding is about.
1441    #[must_use]
1442    pub fn tensor(&self) -> &str {
1443        match self {
1444            Self::Missing { name }
1445            | Self::Extra { name }
1446            | Self::ShapeMismatch { name, .. }
1447            | Self::DtypeMismatch { name, .. }
1448            | Self::WrongAccessClass { name, .. }
1449            | Self::DanglingSection { name, .. } => name,
1450        }
1451    }
1452
1453    /// Short class label, for counting findings by kind.
1454    #[must_use]
1455    pub const fn class(&self) -> &'static str {
1456        match self {
1457            Self::Missing { .. } => "missing",
1458            Self::Extra { .. } => "extra",
1459            Self::ShapeMismatch { .. } => "shape_mismatch",
1460            Self::DtypeMismatch { .. } => "dtype_mismatch",
1461            Self::WrongAccessClass { .. } => "wrong_access_class",
1462            Self::DanglingSection { .. } => "dangling_section",
1463        }
1464    }
1465}
1466
1467impl fmt::Display for ArtifactFinding {
1468    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1469        match self {
1470            Self::Missing { name } => write!(f, "MISSING       {name}"),
1471            Self::Extra { name } => write!(f, "EXTRA         {name}"),
1472            Self::ShapeMismatch {
1473                name,
1474                expected,
1475                found,
1476            } => write!(
1477                f,
1478                "SHAPE         {name}: expected {expected:?}, found {found:?}"
1479            ),
1480            Self::DtypeMismatch {
1481                name,
1482                expected,
1483                found,
1484            } => write!(
1485                f,
1486                "DTYPE         {name}: expected {expected}, found {found}"
1487            ),
1488            Self::WrongAccessClass {
1489                name,
1490                expected,
1491                found,
1492            } => write!(
1493                f,
1494                "ACCESS_CLASS  {name}: expected {expected}, found {found}"
1495            ),
1496            Self::DanglingSection { name, section } => {
1497                write!(
1498                    f,
1499                    "DANGLING      {name}: names undeclared section `{section}`"
1500                )
1501            }
1502        }
1503    }
1504}
1505
1506/// The expected tensor inventory for one artifact.
1507///
1508/// The **mechanism**, not the data: which tensors a `.fttsq` must carry, at which precision, comes
1509/// from the OQ-2 inventory crossed with the converter's per-tensor quantization policy. This module
1510/// does not hard-code that recipe, because it is per-artifact and measured.
1511#[derive(Clone, Debug, Default)]
1512pub struct ArtifactManifest {
1513    label: String,
1514    expected: Vec<ExpectedArtifactTensor>,
1515}
1516
1517impl ArtifactManifest {
1518    /// Starts a manifest with a human label used in the report header.
1519    #[must_use]
1520    pub fn new(label: impl Into<String>) -> Self {
1521        Self {
1522            label: label.into(),
1523            expected: Vec::new(),
1524        }
1525    }
1526
1527    /// Adds one expectation.
1528    #[must_use]
1529    pub fn expect(mut self, tensor: ExpectedArtifactTensor) -> Self {
1530        self.expected.push(tensor);
1531        self
1532    }
1533
1534    /// The label.
1535    #[must_use]
1536    pub fn label(&self) -> &str {
1537        &self.label
1538    }
1539
1540    /// How many tensors are expected.
1541    #[must_use]
1542    pub fn len(&self) -> usize {
1543        self.expected.len()
1544    }
1545
1546    /// Whether the manifest expects nothing.
1547    #[must_use]
1548    pub fn is_empty(&self) -> bool {
1549        self.expected.is_empty()
1550    }
1551
1552    /// Audits a reader against this manifest, reporting **every** divergence.
1553    ///
1554    /// Deliberately does not stop at the first finding: a converter bug usually produces a family
1555    /// of related divergences, and seeing all of them at once is the difference between one fix and
1556    /// twenty rounds of rerunning a multi-gigabyte conversion.
1557    #[must_use]
1558    pub fn audit(&self, reader: &FttsqReader) -> ArtifactCensus {
1559        let mut findings = Vec::new();
1560        let expected_names: BTreeMap<&str, &ExpectedArtifactTensor> = self
1561            .expected
1562            .iter()
1563            .map(|tensor| (tensor.name.as_str(), tensor))
1564            .collect();
1565
1566        for expectation in &self.expected {
1567            let Some(found) = reader.tensor(&expectation.name) else {
1568                findings.push(ArtifactFinding::Missing {
1569                    name: expectation.name.clone(),
1570                });
1571                continue;
1572            };
1573            if found.shape != expectation.shape {
1574                findings.push(ArtifactFinding::ShapeMismatch {
1575                    name: expectation.name.clone(),
1576                    expected: expectation.shape.clone(),
1577                    found: found.shape.clone(),
1578                });
1579            }
1580            if found.dtype != expectation.dtype {
1581                findings.push(ArtifactFinding::DtypeMismatch {
1582                    name: expectation.name.clone(),
1583                    expected: expectation.dtype,
1584                    found: found.dtype,
1585                });
1586            }
1587            match reader.section(&found.section) {
1588                Some(section) if section.access_class != expectation.access_class => {
1589                    findings.push(ArtifactFinding::WrongAccessClass {
1590                        name: expectation.name.clone(),
1591                        expected: expectation.access_class,
1592                        found: section.access_class,
1593                    });
1594                }
1595                Some(_) => {}
1596                None => findings.push(ArtifactFinding::DanglingSection {
1597                    name: expectation.name.clone(),
1598                    section: found.section.clone(),
1599                }),
1600            }
1601        }
1602
1603        for tensor in reader.tensors() {
1604            if !expected_names.contains_key(tensor.name.as_str()) {
1605                findings.push(ArtifactFinding::Extra {
1606                    name: tensor.name.clone(),
1607                });
1608            }
1609        }
1610
1611        ArtifactCensus {
1612            label: self.label.clone(),
1613            expected: self.expected.len(),
1614            found: reader.tensors().len(),
1615            findings,
1616        }
1617    }
1618}
1619
1620/// The itemized result of an artifact census.
1621#[derive(Clone, Debug)]
1622pub struct ArtifactCensus {
1623    label: String,
1624    expected: usize,
1625    found: usize,
1626    findings: Vec<ArtifactFinding>,
1627}
1628
1629impl ArtifactCensus {
1630    /// Whether the artifact matched its manifest exactly.
1631    #[must_use]
1632    pub fn is_green(&self) -> bool {
1633        self.findings.is_empty()
1634    }
1635
1636    /// Every divergence found.
1637    #[must_use]
1638    pub fn findings(&self) -> &[ArtifactFinding] {
1639        &self.findings
1640    }
1641
1642    /// How many findings of one class.
1643    #[must_use]
1644    pub fn count_of(&self, class: &str) -> usize {
1645        self.findings
1646            .iter()
1647            .filter(|finding| finding.class() == class)
1648            .count()
1649    }
1650
1651    /// A readable, itemized report.
1652    #[must_use]
1653    pub fn render(&self) -> String {
1654        let mut out = format!(
1655            "artifact census `{}`: expected {} tensors, artifact declares {} — {}\n",
1656            self.label,
1657            self.expected,
1658            self.found,
1659            if self.is_green() {
1660                "GREEN".to_owned()
1661            } else {
1662                format!("{} FINDINGS", self.findings.len())
1663            }
1664        );
1665        for finding in &self.findings {
1666            out.push_str(&format!("  {finding}\n"));
1667        }
1668        out
1669    }
1670}
1671
1672impl fmt::Display for ArtifactCensus {
1673    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1674        f.write_str(&self.render())
1675    }
1676}
1677
1678impl std::error::Error for ArtifactCensus {}
1679
1680fn required_str<'a>(value: Option<&'a Value>, path: &str) -> Result<&'a str, FttsqError> {
1681    value
1682        .and_then(Value::as_str)
1683        .filter(|text| !text.is_empty())
1684        .ok_or_else(|| FttsqError::Field {
1685            path: path.to_owned(),
1686            expected: "a non-empty string".to_owned(),
1687        })
1688}
1689
1690fn required_u64(value: Option<&Value>, path: &str) -> Result<u64, FttsqError> {
1691    value
1692        .and_then(Value::as_u64)
1693        .ok_or_else(|| FttsqError::Field {
1694            path: path.to_owned(),
1695            expected: "a non-negative integer".to_owned(),
1696        })
1697}
1698
1699fn parse_sections(value: Option<&Value>, file_len: u64) -> Result<Vec<SectionEntry>, FttsqError> {
1700    let array = value
1701        .and_then(Value::as_array)
1702        .ok_or_else(|| FttsqError::Field {
1703            path: "sections".to_owned(),
1704            expected: "an array".to_owned(),
1705        })?;
1706    if array.len() > MAX_SECTIONS {
1707        return Err(FttsqError::LimitExceeded {
1708            what: "section".to_owned(),
1709            found: array.len() as u64,
1710            limit: MAX_SECTIONS as u64,
1711        });
1712    }
1713
1714    let mut sections = Vec::with_capacity(array.len());
1715    let mut seen: BTreeMap<String, ()> = BTreeMap::new();
1716    for (index, entry) in array.iter().enumerate() {
1717        let path = |field: &str| format!("sections[{index}].{field}");
1718        let name = required_str(entry.get("name"), &path("name"))?.to_owned();
1719        if seen.insert(name.clone(), ()).is_some() {
1720            return Err(FttsqError::DuplicateName {
1721                what: "section".to_owned(),
1722                name,
1723            });
1724        }
1725        let class_text = required_str(entry.get("access_class"), &path("access_class"))?;
1726        let access_class =
1727            AccessClass::parse(class_text).ok_or_else(|| FttsqError::UnknownValue {
1728                path: path("access_class"),
1729                found: class_text.to_owned(),
1730            })?;
1731        let offset = required_u64(entry.get("offset"), &path("offset"))?;
1732        let length = required_u64(entry.get("length"), &path("length"))?;
1733        let sha256 = required_str(entry.get("sha256"), &path("sha256"))?.to_owned();
1734
1735        let end = offset
1736            .checked_add(length)
1737            .ok_or_else(|| FttsqError::RangeOutOfBounds {
1738                what: format!("section `{name}`"),
1739                offset,
1740                length,
1741                bound: file_len,
1742            })?;
1743        if end > file_len {
1744            return Err(FttsqError::RangeOutOfBounds {
1745                what: format!("section `{name}`"),
1746                offset,
1747                length,
1748                bound: file_len,
1749            });
1750        }
1751
1752        sections.push(SectionEntry {
1753            name,
1754            access_class,
1755            offset,
1756            length,
1757            sha256,
1758        });
1759    }
1760
1761    // Overlap is checked on a sorted copy so declaration order stays free.
1762    let mut ordered: Vec<&SectionEntry> = sections.iter().collect();
1763    ordered.sort_by_key(|section| section.offset);
1764    for pair in ordered.windows(2) {
1765        let (first, second) = (pair[0], pair[1]);
1766        let first_end = first.end().unwrap_or(u64::MAX);
1767        if first_end > second.offset {
1768            return Err(FttsqError::SectionOverlap {
1769                first: first.name.clone(),
1770                second: second.name.clone(),
1771            });
1772        }
1773    }
1774
1775    Ok(sections)
1776}
1777
1778fn parse_tensors(
1779    value: Option<&Value>,
1780    sections: &[SectionEntry],
1781    section_index: &BTreeMap<String, usize>,
1782) -> Result<Vec<TensorEntry>, FttsqError> {
1783    let array = value
1784        .and_then(Value::as_array)
1785        .ok_or_else(|| FttsqError::Field {
1786            path: "tensors".to_owned(),
1787            expected: "an array".to_owned(),
1788        })?;
1789    if array.len() > MAX_TENSORS {
1790        return Err(FttsqError::LimitExceeded {
1791            what: "tensor".to_owned(),
1792            found: array.len() as u64,
1793            limit: MAX_TENSORS as u64,
1794        });
1795    }
1796
1797    let mut tensors = Vec::with_capacity(array.len());
1798    let mut seen: BTreeMap<String, ()> = BTreeMap::new();
1799    for (index, entry) in array.iter().enumerate() {
1800        let path = |field: &str| format!("tensors[{index}].{field}");
1801        let name = required_str(entry.get("name"), &path("name"))?.to_owned();
1802        if seen.insert(name.clone(), ()).is_some() {
1803            return Err(FttsqError::DuplicateName {
1804                what: "tensor".to_owned(),
1805                name,
1806            });
1807        }
1808        let section = required_str(entry.get("section"), &path("section"))?.to_owned();
1809        let dtype_text = required_str(entry.get("dtype"), &path("dtype"))?;
1810        let dtype = StoredDtype::parse(dtype_text).ok_or_else(|| FttsqError::UnknownValue {
1811            path: path("dtype"),
1812            found: dtype_text.to_owned(),
1813        })?;
1814
1815        let shape_array = entry
1816            .get("shape")
1817            .and_then(Value::as_array)
1818            .ok_or_else(|| FttsqError::Field {
1819                path: path("shape"),
1820                expected: "an array".to_owned(),
1821            })?;
1822        if shape_array.len() > MAX_RANK {
1823            return Err(FttsqError::LimitExceeded {
1824                what: format!("tensor `{name}` rank"),
1825                found: shape_array.len() as u64,
1826                limit: MAX_RANK as u64,
1827            });
1828        }
1829        let mut shape = Vec::with_capacity(shape_array.len());
1830        for (axis, dim) in shape_array.iter().enumerate() {
1831            let dim = dim.as_u64().ok_or_else(|| FttsqError::Field {
1832                path: format!("{}[{axis}]", path("shape")),
1833                expected: "a non-negative integer".to_owned(),
1834            })?;
1835            if dim > MAX_DIM {
1836                return Err(FttsqError::LimitExceeded {
1837                    what: format!("tensor `{name}` dimension {axis}"),
1838                    found: dim,
1839                    limit: MAX_DIM,
1840                });
1841            }
1842            shape.push(dim);
1843        }
1844
1845        let offset = required_u64(entry.get("offset"), &path("offset"))?;
1846        let length = required_u64(entry.get("length"), &path("length"))?;
1847        let scales = entry
1848            .get("scales")
1849            .and_then(Value::as_str)
1850            .map(str::to_owned);
1851
1852        let tensor = TensorEntry {
1853            name,
1854            section,
1855            dtype,
1856            shape,
1857            offset,
1858            length,
1859            scales,
1860        };
1861
1862        // The declared length must equal what shape and dtype imply. Trusting the declared length
1863        // alone would let a directory hand out a window that does not match the tensor a kernel
1864        // then indexes by shape.
1865        let elements = tensor.elements().ok_or_else(|| FttsqError::LimitExceeded {
1866            what: format!("tensor `{}` element count", tensor.name),
1867            found: u64::MAX,
1868            limit: MAX_DIM,
1869        })?;
1870        let implied = dtype
1871            .storage_bytes(elements)
1872            .ok_or_else(|| FttsqError::LimitExceeded {
1873                what: format!("tensor `{}` storage size", tensor.name),
1874                found: u64::MAX,
1875                limit: MAX_DIM,
1876            })?;
1877        if implied != tensor.length {
1878            return Err(FttsqError::LengthMismatch {
1879                tensor: tensor.name.clone(),
1880                declared: tensor.length,
1881                implied,
1882            });
1883        }
1884
1885        let owner = section_index
1886            .get(&tensor.section)
1887            .and_then(|&index| sections.get(index))
1888            .ok_or_else(|| FttsqError::UnknownSection {
1889                tensor: tensor.name.clone(),
1890                section: tensor.section.clone(),
1891            })?;
1892        let end = tensor.offset.checked_add(tensor.length).ok_or_else(|| {
1893            FttsqError::RangeOutOfBounds {
1894                what: format!("tensor `{}`", tensor.name),
1895                offset: tensor.offset,
1896                length: tensor.length,
1897                bound: owner.length,
1898            }
1899        })?;
1900        if end > owner.length {
1901            return Err(FttsqError::RangeOutOfBounds {
1902                what: format!("tensor `{}`", tensor.name),
1903                offset: tensor.offset,
1904                length: tensor.length,
1905                bound: owner.length,
1906            });
1907        }
1908
1909        tensors.push(tensor);
1910    }
1911
1912    // Overlap within each section. Two tensors sharing bytes means one of them is wrong, and which
1913    // one is not knowable at read time — so both are refused.
1914    let mut by_section: BTreeMap<&str, Vec<&TensorEntry>> = BTreeMap::new();
1915    for tensor in &tensors {
1916        by_section
1917            .entry(tensor.section.as_str())
1918            .or_default()
1919            .push(tensor);
1920    }
1921    for group in by_section.values_mut() {
1922        group.sort_by_key(|tensor| tensor.offset);
1923        for pair in group.windows(2) {
1924            let (first, second) = (pair[0], pair[1]);
1925            let first_end = first.offset.saturating_add(first.length);
1926            if first_end > second.offset {
1927                return Err(FttsqError::TensorOverlap {
1928                    first: first.name.clone(),
1929                    second: second.name.clone(),
1930                });
1931            }
1932        }
1933    }
1934
1935    Ok(tensors)
1936}
1937
1938/// Metadata and fixed section lengths for a bounded `.fttsq` conversion.
1939///
1940/// A converter learns every tensor's shape, storage policy, section, and final byte length from
1941/// the validated input manifest before it starts payload conversion. That lets this plan reserve
1942/// the directory first, then stream one section at a time into a caller-owned seekable temporary
1943/// file. The writer never retains a payload section, and does not create, rename, or remove files:
1944/// the caller owns the atomic-temp-file policy around it.
1945#[derive(Debug)]
1946pub struct FttsqStreamPlan {
1947    model_family: String,
1948    source_sha256: String,
1949    license_notice: String,
1950    model_config: Value,
1951    quantization_manifest: Value,
1952    sections: Vec<(String, AccessClass, u64)>,
1953    tensors: Vec<TensorEntry>,
1954}
1955
1956impl FttsqStreamPlan {
1957    /// Starts a bounded conversion plan for one model family and source checkpoint.
1958    #[must_use]
1959    pub fn new(model_family: impl Into<String>, source_sha256: impl Into<String>) -> Self {
1960        Self {
1961            model_family: model_family.into(),
1962            source_sha256: source_sha256.into(),
1963            license_notice: String::new(),
1964            model_config: Value::Null,
1965            quantization_manifest: Value::Null,
1966            sections: Vec::new(),
1967            tensors: Vec::new(),
1968        }
1969    }
1970
1971    /// Sets the required Apache-2.0 §4 attribution notice.
1972    #[must_use]
1973    pub fn license_notice(mut self, notice: impl Into<String>) -> Self {
1974        self.license_notice = notice.into();
1975        self
1976    }
1977
1978    /// Attaches the frozen upstream model configuration.
1979    #[must_use]
1980    pub fn model_config(mut self, config: Value) -> Self {
1981        self.model_config = config;
1982        self
1983    }
1984
1985    /// Attaches the policy used to quantize each tensor.
1986    #[must_use]
1987    pub fn quantization_manifest(mut self, manifest: Value) -> Self {
1988        self.quantization_manifest = manifest;
1989        self
1990    }
1991
1992    /// Declares a section's final byte length before its bytes are streamed.
1993    #[must_use]
1994    pub fn section(
1995        mut self,
1996        name: impl Into<String>,
1997        access_class: AccessClass,
1998        length: u64,
1999    ) -> Self {
2000        self.sections.push((name.into(), access_class, length));
2001        self
2002    }
2003
2004    /// Declares a tensor located in one of the planned sections.
2005    #[must_use]
2006    pub fn tensor(mut self, tensor: TensorEntry) -> Self {
2007        self.tensors.push(tensor);
2008        self
2009    }
2010
2011    /// Writes the header and reserved directory into a caller-owned seekable stream.
2012    ///
2013    /// Payload sections must subsequently be supplied in declaration order through
2014    /// [`FttsqStreamingWriter::write_section`]. A caller that needs an atomic artifact should
2015    /// provide its own same-filesystem temporary file, call [`FttsqStreamingWriter::finish`],
2016    /// sync it, and rename it only after this method has finalized the directory.
2017    ///
2018    /// # Errors
2019    ///
2020    /// Refuses malformed planned metadata before writing any bytes, and names I/O failures from
2021    /// the caller's stream without assuming a path or filesystem policy.
2022    pub fn begin<W: std::io::Write + std::io::Seek>(
2023        self,
2024        mut writer: W,
2025    ) -> Result<FttsqStreamingWriter<W>, FttsqError> {
2026        if self.license_notice.trim().is_empty() {
2027            return Err(FttsqError::LicenseNoticeMissing);
2028        }
2029
2030        // The final SHA-256 strings are unknown until each section has streamed, but their exact
2031        // wire width is known. Filling that width now keeps the reserved directory large enough
2032        // for the finalized digests without retaining a single payload byte.
2033        let mut sections: Vec<SectionEntry> = self
2034            .sections
2035            .into_iter()
2036            .map(|(name, access_class, length)| SectionEntry {
2037                name,
2038                access_class,
2039                offset: 0,
2040                length,
2041                sha256: "0".repeat(64),
2042            })
2043            .collect();
2044        let mut probe_sections = sections.clone();
2045        for section in &mut probe_sections {
2046            // Twenty decimal digits are the widest valid offset. No section layout is required
2047            // for this pass, avoiding arithmetic at a fake near-`u64::MAX` payload start.
2048            section.offset = u64::MAX;
2049        }
2050        let probe = stream_directory_json(
2051            &self.model_family,
2052            &self.source_sha256,
2053            &self.license_notice,
2054            &self.model_config,
2055            &self.quantization_manifest,
2056            &probe_sections,
2057            &self.tensors,
2058        );
2059        let directory_len = serde_json::to_vec(&probe)
2060            .map_err(|error| FttsqError::DirectoryMalformed {
2061                detail: error.to_string(),
2062            })?
2063            .len() as u64;
2064        if directory_len > MAX_DIRECTORY_BYTES {
2065            return Err(FttsqError::DirectoryLength {
2066                declared: directory_len,
2067                limit: MAX_DIRECTORY_BYTES,
2068            });
2069        }
2070        let payload_start =
2071            HEADER_PREFIX_BYTES
2072                .checked_add(directory_len)
2073                .ok_or(FttsqError::DirectoryLength {
2074                    declared: directory_len,
2075                    limit: u64::MAX,
2076                })?;
2077        let final_file_len = layout_stream_sections(&mut sections, payload_start)?;
2078
2079        let directory = stream_directory_json(
2080            &self.model_family,
2081            &self.source_sha256,
2082            &self.license_notice,
2083            &self.model_config,
2084            &self.quantization_manifest,
2085            &sections,
2086            &self.tensors,
2087        );
2088        let mut directory_bytes =
2089            serde_json::to_vec(&directory).map_err(|error| FttsqError::DirectoryMalformed {
2090                detail: error.to_string(),
2091            })?;
2092        if directory_bytes.len() as u64 > directory_len {
2093            return Err(FttsqError::DirectoryLength {
2094                declared: directory_bytes.len() as u64,
2095                limit: directory_len,
2096            });
2097        }
2098        directory_bytes.resize(directory_len as usize, b' ');
2099
2100        let mut header_and_directory = Vec::with_capacity(
2101            (HEADER_PREFIX_BYTES as usize).saturating_add(directory_bytes.len()),
2102        );
2103        header_and_directory.extend_from_slice(MAGIC);
2104        header_and_directory.extend_from_slice(&FORMAT_VERSION.to_le_bytes());
2105        header_and_directory.extend_from_slice(&directory_len.to_le_bytes());
2106        header_and_directory.extend_from_slice(&directory_bytes);
2107
2108        // Validate the exact finalized offsets and tensor spans while their metadata is still
2109        // cheap. Digest verification deliberately waits for all streamed payload bytes.
2110        FttsqReader::parse_directory_for_file_len(&header_and_directory, final_file_len)?;
2111        writer
2112            .write_all(&header_and_directory)
2113            .map_err(|error| stream_io_error("write header and directory", &error))?;
2114
2115        let mut streaming = FttsqStreamingWriter {
2116            writer,
2117            model_family: self.model_family,
2118            source_sha256: self.source_sha256,
2119            license_notice: self.license_notice,
2120            model_config: self.model_config,
2121            quantization_manifest: self.quantization_manifest,
2122            sections,
2123            tensors: self.tensors,
2124            directory_len,
2125            current_section: 0,
2126            section_written: 0,
2127            section_hasher: Sha256::new(),
2128        };
2129        streaming.finalize_empty_sections();
2130        Ok(streaming)
2131    }
2132}
2133
2134/// A bounded writer for `.fttsq` section payloads.
2135///
2136/// The stream owns metadata plus one incremental SHA-256 state; it never owns a section payload.
2137/// This is intentionally lower-level than [`FttsqWriter`]: callers retain responsibility for the
2138/// surrounding atomic temporary-file creation, sync, and rename, while this type finalizes the
2139/// directory only after every declared byte and digest is complete.
2140#[derive(Debug)]
2141pub struct FttsqStreamingWriter<W> {
2142    writer: W,
2143    model_family: String,
2144    source_sha256: String,
2145    license_notice: String,
2146    model_config: Value,
2147    quantization_manifest: Value,
2148    sections: Vec<SectionEntry>,
2149    tensors: Vec<TensorEntry>,
2150    directory_len: u64,
2151    current_section: usize,
2152    section_written: u64,
2153    section_hasher: Sha256,
2154}
2155
2156impl<W: std::io::Write + std::io::Seek> FttsqStreamingWriter<W> {
2157    /// Appends bytes to the next declared section.
2158    ///
2159    /// Calls for a later section are refused rather than buffered. That ordering makes the
2160    /// converter's one-tensor-at-a-time memory bound mechanical: once a section is complete, its
2161    /// source mapping and tile buffers can be released before conversion continues.
2162    ///
2163    /// # Errors
2164    ///
2165    /// Returns a named refusal for out-of-order or overlong sections, or [`FttsqError::Io`] when
2166    /// the caller-owned stream cannot accept the bytes.
2167    pub fn write_section(&mut self, section: &str, bytes: &[u8]) -> Result<(), FttsqError> {
2168        let Some(entry) = self.sections.get(self.current_section) else {
2169            return Err(FttsqError::SectionWriteOutOfOrder {
2170                expected: None,
2171                actual: section.to_owned(),
2172            });
2173        };
2174        let expected = entry.name.clone();
2175        let declared = entry.length;
2176        if expected != section {
2177            return Err(FttsqError::SectionWriteOutOfOrder {
2178                expected: Some(expected),
2179                actual: section.to_owned(),
2180            });
2181        }
2182        let bytes_len = bytes.len() as u64;
2183        let attempted = self.section_written.checked_add(bytes_len).ok_or_else(|| {
2184            FttsqError::SectionLengthExceeded {
2185                section: expected.clone(),
2186                declared,
2187                attempted: u64::MAX,
2188            }
2189        })?;
2190        if attempted > declared {
2191            return Err(FttsqError::SectionLengthExceeded {
2192                section: expected,
2193                declared,
2194                attempted,
2195            });
2196        }
2197
2198        self.writer
2199            .write_all(bytes)
2200            .map_err(|error| stream_io_error("write section", &error))?;
2201        self.section_hasher.update(bytes);
2202        self.section_written = attempted;
2203        self.finalize_empty_sections();
2204        Ok(())
2205    }
2206
2207    /// Finalizes all completed section digests and rewrites the reserved directory in place.
2208    ///
2209    /// The returned stream is positioned at its end and flushed, ready for a file-owning caller to
2210    /// perform its durability and atomic-rename steps. A successful return guarantees that a
2211    /// complete byte buffer collected from the stream passes [`FttsqReader::open`].
2212    ///
2213    /// # Errors
2214    ///
2215    /// Refuses an incomplete declared section and names failures while seeking, finalizing, or
2216    /// flushing the caller-owned stream.
2217    pub fn finish(mut self) -> Result<W, FttsqError> {
2218        if let Some(section) = self.sections.get(self.current_section) {
2219            return Err(FttsqError::SectionIncomplete {
2220                section: section.name.clone(),
2221                declared: section.length,
2222                written: self.section_written,
2223            });
2224        }
2225
2226        let directory = stream_directory_json(
2227            &self.model_family,
2228            &self.source_sha256,
2229            &self.license_notice,
2230            &self.model_config,
2231            &self.quantization_manifest,
2232            &self.sections,
2233            &self.tensors,
2234        );
2235        let directory_bytes =
2236            serde_json::to_vec(&directory).map_err(|error| FttsqError::DirectoryMalformed {
2237                detail: error.to_string(),
2238            })?;
2239        if directory_bytes.len() as u64 > self.directory_len {
2240            return Err(FttsqError::DirectoryLength {
2241                declared: directory_bytes.len() as u64,
2242                limit: self.directory_len,
2243            });
2244        }
2245
2246        self.writer
2247            .seek(std::io::SeekFrom::Start(HEADER_PREFIX_BYTES))
2248            .map_err(|error| stream_io_error("seek to directory", &error))?;
2249        self.writer
2250            .write_all(&directory_bytes)
2251            .map_err(|error| stream_io_error("finalize directory", &error))?;
2252        write_space_padding(
2253            &mut self.writer,
2254            self.directory_len - directory_bytes.len() as u64,
2255        )?;
2256        self.writer
2257            .seek(std::io::SeekFrom::End(0))
2258            .map_err(|error| stream_io_error("seek to artifact end", &error))?;
2259        self.writer
2260            .flush()
2261            .map_err(|error| stream_io_error("flush finalized artifact", &error))?;
2262        Ok(self.writer)
2263    }
2264
2265    fn finalize_empty_sections(&mut self) {
2266        while let Some(section) = self.sections.get_mut(self.current_section) {
2267            if self.section_written != section.length {
2268                break;
2269            }
2270            section.sha256 = to_hex(&std::mem::take(&mut self.section_hasher).finish());
2271            self.current_section += 1;
2272            self.section_written = 0;
2273        }
2274    }
2275}
2276
2277fn layout_stream_sections(
2278    sections: &mut [SectionEntry],
2279    payload_start: u64,
2280) -> Result<u64, FttsqError> {
2281    let mut cursor = payload_start;
2282    for section in sections {
2283        section.offset = cursor;
2284        cursor =
2285            cursor
2286                .checked_add(section.length)
2287                .ok_or_else(|| FttsqError::RangeOutOfBounds {
2288                    what: format!("section `{}`", section.name),
2289                    offset: section.offset,
2290                    length: section.length,
2291                    bound: u64::MAX,
2292                })?;
2293    }
2294    Ok(cursor)
2295}
2296
2297fn stream_directory_json(
2298    model_family: &str,
2299    source_sha256: &str,
2300    license_notice: &str,
2301    model_config: &Value,
2302    quantization_manifest: &Value,
2303    sections: &[SectionEntry],
2304    tensors: &[TensorEntry],
2305) -> Value {
2306    let sections: Vec<Value> = sections
2307        .iter()
2308        .map(|section| {
2309            json!({
2310                "name": section.name,
2311                "access_class": section.access_class.as_str(),
2312                "offset": section.offset,
2313                "length": section.length,
2314                "sha256": section.sha256,
2315            })
2316        })
2317        .collect();
2318    let tensors: Vec<Value> = tensors
2319        .iter()
2320        .map(|tensor| {
2321            json!({
2322                "name": tensor.name,
2323                "section": tensor.section,
2324                "dtype": tensor.dtype.as_str(),
2325                "shape": tensor.shape,
2326                "offset": tensor.offset,
2327                "length": tensor.length,
2328                "scales": tensor.scales,
2329            })
2330        })
2331        .collect();
2332    json!({
2333        "format_version": FORMAT_VERSION,
2334        "model_family": model_family,
2335        "source_sha256": source_sha256,
2336        "license_notice": license_notice,
2337        "model_config": model_config,
2338        "quantization_manifest": quantization_manifest,
2339        "sections": sections,
2340        "tensors": tensors,
2341    })
2342}
2343
2344fn stream_io_error(operation: &str, error: &std::io::Error) -> FttsqError {
2345    FttsqError::Io {
2346        operation: operation.to_owned(),
2347        path: "<fttsq stream>".to_owned(),
2348        detail: error.to_string(),
2349    }
2350}
2351
2352fn write_space_padding<W: std::io::Write>(
2353    writer: &mut W,
2354    mut remaining: u64,
2355) -> Result<(), FttsqError> {
2356    const SPACES: [u8; 4096] = [b' '; 4096];
2357    while remaining > 0 {
2358        let count = remaining.min(SPACES.len() as u64) as usize;
2359        writer
2360            .write_all(&SPACES[..count])
2361            .map_err(|error| stream_io_error("pad finalized directory", &error))?;
2362        remaining -= count as u64;
2363    }
2364    Ok(())
2365}
2366
2367/// Builds a `.fttsq` artifact.
2368///
2369/// Sections are appended in the order given; the writer computes each digest and lays out absolute
2370/// offsets, so a caller cannot produce an artifact whose directory disagrees with its payload.
2371#[derive(Debug, Default)]
2372pub struct FttsqWriter {
2373    model_family: String,
2374    source_sha256: String,
2375    license_notice: String,
2376    model_config: Value,
2377    quantization_manifest: Value,
2378    sections: Vec<(SectionEntry, Vec<u8>)>,
2379    tensors: Vec<TensorEntry>,
2380}
2381
2382impl FttsqWriter {
2383    /// Starts an artifact for one model family, converted from a checkpoint with `source_sha256`.
2384    #[must_use]
2385    pub fn new(model_family: impl Into<String>, source_sha256: impl Into<String>) -> Self {
2386        Self {
2387            model_family: model_family.into(),
2388            source_sha256: source_sha256.into(),
2389            license_notice: String::new(),
2390            model_config: Value::Null,
2391            quantization_manifest: Value::Null,
2392            sections: Vec::new(),
2393            tensors: Vec::new(),
2394        }
2395    }
2396
2397    /// Sets the Apache-2.0 §4 attribution notice. Required — [`FttsqWriter::finish`] refuses without it.
2398    #[must_use]
2399    pub fn license_notice(mut self, notice: impl Into<String>) -> Self {
2400        self.license_notice = notice.into();
2401        self
2402    }
2403
2404    /// Attaches the frozen upstream model config.
2405    #[must_use]
2406    pub fn model_config(mut self, config: Value) -> Self {
2407        self.model_config = config;
2408        self
2409    }
2410
2411    /// Attaches the per-tensor quantization policy the license notice refers to.
2412    #[must_use]
2413    pub fn quantization_manifest(mut self, manifest: Value) -> Self {
2414        self.quantization_manifest = manifest;
2415        self
2416    }
2417
2418    /// Appends a section with its payload. Offset and digest are computed at [`FttsqWriter::finish`].
2419    #[must_use]
2420    pub fn section(
2421        mut self,
2422        name: impl Into<String>,
2423        access_class: AccessClass,
2424        payload: Vec<u8>,
2425    ) -> Self {
2426        let entry = SectionEntry {
2427            name: name.into(),
2428            access_class,
2429            offset: 0,
2430            length: payload.len() as u64,
2431            sha256: String::new(),
2432        };
2433        self.sections.push((entry, payload));
2434        self
2435    }
2436
2437    /// Declares a tensor located inside an already-added section.
2438    #[must_use]
2439    pub fn tensor(mut self, tensor: TensorEntry) -> Self {
2440        self.tensors.push(tensor);
2441        self
2442    }
2443
2444    /// Serializes the artifact.
2445    ///
2446    /// The result is re-parsed before being returned, so a writer bug surfaces here rather than as
2447    /// an unreadable multi-gigabyte file discovered hours later.
2448    ///
2449    /// # Errors
2450    ///
2451    /// Returns [`FttsqError::LicenseNoticeMissing`] without a notice, or whatever the validating
2452    /// re-parse rejects.
2453    pub fn finish(mut self) -> Result<Vec<u8>, FttsqError> {
2454        if self.license_notice.trim().is_empty() {
2455            return Err(FttsqError::LicenseNoticeMissing);
2456        }
2457
2458        for (entry, payload) in &mut self.sections {
2459            entry.length = payload.len() as u64;
2460            entry.sha256 = hex_digest(payload);
2461        }
2462
2463        // Two passes: the directory's size depends on the offsets, and the offsets depend on the
2464        // directory's size. Serialize once with placeholder offsets to learn the exact directory
2465        // length, then again with the real ones. The placeholder pass uses u64::MAX-width numbers
2466        // so the second directory can only be the same size or smaller — and we pad to match.
2467        let probe = self.directory_json(u64::MAX);
2468        let probe_len = serde_json::to_vec(&probe)
2469            .map_err(|error| FttsqError::DirectoryMalformed {
2470                detail: error.to_string(),
2471            })?
2472            .len() as u64;
2473
2474        let payload_start = HEADER_PREFIX_BYTES + probe_len;
2475        let directory = self.directory_json(payload_start);
2476        let mut directory_bytes =
2477            serde_json::to_vec(&directory).map_err(|error| FttsqError::DirectoryMalformed {
2478                detail: error.to_string(),
2479            })?;
2480        // Pad with trailing spaces so the directory occupies exactly `probe_len` bytes and the
2481        // offsets computed above stay correct. JSON tolerates trailing whitespace.
2482        while (directory_bytes.len() as u64) < probe_len {
2483            directory_bytes.push(b' ');
2484        }
2485
2486        let mut out = Vec::with_capacity(payload_start as usize);
2487        out.extend_from_slice(MAGIC);
2488        out.extend_from_slice(&FORMAT_VERSION.to_le_bytes());
2489        out.extend_from_slice(&(directory_bytes.len() as u64).to_le_bytes());
2490        out.extend_from_slice(&directory_bytes);
2491        for (_, payload) in &self.sections {
2492            out.extend_from_slice(payload);
2493        }
2494
2495        // Prove what we just wrote is readable, digests and all.
2496        FttsqReader::open(&out)?;
2497        Ok(out)
2498    }
2499
2500    /// Serializes and writes the artifact to `path` **atomically**.
2501    ///
2502    /// Writes to a temporary file beside the destination, fsyncs it, then renames over the target.
2503    /// A reader therefore observes either the previous artifact or the complete new one, never a
2504    /// half-written prefix — which matters because a truncated `.fttsq` is exactly the shape that
2505    /// digest verification would report as *corruption* rather than as an interrupted write.
2506    ///
2507    /// The temporary lives in the destination's own directory so the rename stays within one
2508    /// filesystem; `rename` across mount points is not atomic and would silently degrade to a copy.
2509    /// On failure the temporary is removed.
2510    ///
2511    /// # Errors
2512    ///
2513    /// Returns [`FttsqError::Io`] naming the operation and path, or whatever
2514    /// [`FttsqWriter::finish`] rejects.
2515    pub fn write_to_path(self, path: &std::path::Path) -> Result<(), FttsqError> {
2516        use std::io::Write as _;
2517
2518        let bytes = self.finish()?;
2519
2520        let parent = path.parent().unwrap_or_else(|| std::path::Path::new("."));
2521        // Unique per process so two concurrent converters cannot collide on the temporary.
2522        let file_name = path.file_name().map_or_else(
2523            || std::ffi::OsString::from("artifact.fttsq"),
2524            std::ffi::OsStr::to_os_string,
2525        );
2526        let mut temp_name = file_name;
2527        temp_name.push(format!(".tmp.{}", std::process::id()));
2528        let temp_path = parent.join(temp_name);
2529
2530        let io =
2531            |operation: &str, target: &std::path::Path, error: &std::io::Error| FttsqError::Io {
2532                operation: operation.to_owned(),
2533                path: target.display().to_string(),
2534                detail: error.to_string(),
2535            };
2536
2537        // Any failure past this point must not leave the temporary behind.
2538        let result = (|| -> Result<(), FttsqError> {
2539            let mut file = std::fs::File::create(&temp_path)
2540                .map_err(|error| io("create", &temp_path, &error))?;
2541            file.write_all(&bytes)
2542                .map_err(|error| io("write", &temp_path, &error))?;
2543            // fsync before rename: without it the rename can land while the data is still in the
2544            // page cache, so a crash leaves a correctly-named file full of zeros.
2545            file.sync_all()
2546                .map_err(|error| io("fsync", &temp_path, &error))?;
2547            drop(file);
2548            std::fs::rename(&temp_path, path).map_err(|error| io("rename", path, &error))
2549        })();
2550
2551        if result.is_err() {
2552            let _ = std::fs::remove_file(&temp_path);
2553        }
2554        result
2555    }
2556
2557    fn directory_json(&self, payload_start: u64) -> Value {
2558        let mut cursor = payload_start;
2559        let sections: Vec<Value> = self
2560            .sections
2561            .iter()
2562            .map(|(entry, _)| {
2563                let offset = cursor;
2564                // The probe pass begins at `u64::MAX` to reserve the widest possible decimal
2565                // offset. Saturation keeps that metadata-only calculation defined even for an
2566                // adversarially large in-memory construction; real artifact offsets below are
2567                // still computed from the actual payload start.
2568                cursor = cursor.saturating_add(entry.length);
2569                json!({
2570                    "name": entry.name,
2571                    "access_class": entry.access_class.as_str(),
2572                    "offset": offset,
2573                    "length": entry.length,
2574                    "sha256": entry.sha256,
2575                })
2576            })
2577            .collect();
2578
2579        let tensors: Vec<Value> = self
2580            .tensors
2581            .iter()
2582            .map(|tensor| {
2583                json!({
2584                    "name": tensor.name,
2585                    "section": tensor.section,
2586                    "dtype": tensor.dtype.as_str(),
2587                    "shape": tensor.shape,
2588                    "offset": tensor.offset,
2589                    "length": tensor.length,
2590                    "scales": tensor.scales,
2591                })
2592            })
2593            .collect();
2594
2595        json!({
2596            "format_version": FORMAT_VERSION,
2597            "model_family": self.model_family,
2598            "source_sha256": self.source_sha256,
2599            "license_notice": self.license_notice,
2600            "model_config": self.model_config,
2601            "quantization_manifest": self.quantization_manifest,
2602            "sections": sections,
2603            "tensors": tensors,
2604        })
2605    }
2606}
2607
2608#[cfg(test)]
2609mod tests {
2610    use super::*;
2611    use std::io::Cursor;
2612
2613    /// The §3 notice from `docs/LICENSE_AND_ATTRIBUTION.md`, abbreviated for tests.
2614    const NOTICE: &str = "Copyright 2026 Alibaba Cloud\nApache-2.0\nCHANGES: requantized to .fttsq";
2615
2616    fn artifact() -> Vec<u8> {
2617        FttsqWriter::new("qwen3-tts-12hz-0.6b-base", "a".repeat(64))
2618            .license_notice(NOTICE)
2619            .model_config(json!({ "hidden_size": 1024 }))
2620            .quantization_manifest(json!({ "talker": "q8" }))
2621            .section(
2622                "microdecoder",
2623                AccessClass::HotRecurrentMicrodecoder,
2624                vec![7_u8; 64],
2625            )
2626            .section(
2627                "text_embedding",
2628                AccessClass::ColdTextEmbedding,
2629                vec![9_u8; 32],
2630            )
2631            .tensor(TensorEntry {
2632                name: "microdecoder.body".to_owned(),
2633                section: "microdecoder".to_owned(),
2634                dtype: StoredDtype::Q8,
2635                shape: vec![8, 8],
2636                offset: 0,
2637                length: 64,
2638                scales: Some("microdecoder.body.scales".to_owned()),
2639            })
2640            .tensor(TensorEntry {
2641                name: "text_embedding.weight".to_owned(),
2642                section: "text_embedding".to_owned(),
2643                dtype: StoredDtype::Bf16,
2644                shape: vec![4, 4],
2645                offset: 0,
2646                length: 32,
2647                scales: None,
2648            })
2649            .finish()
2650            .expect("the fixture artifact is writable")
2651    }
2652
2653    fn stream_plan() -> FttsqStreamPlan {
2654        FttsqStreamPlan::new("qwen3-tts-12hz-0.6b-base", "a".repeat(64))
2655            .license_notice(NOTICE)
2656            .model_config(json!({ "hidden_size": 1024 }))
2657            .quantization_manifest(json!({ "talker": "q8" }))
2658            .section("microdecoder", AccessClass::HotRecurrentMicrodecoder, 64)
2659            .section("text_embedding", AccessClass::ColdTextEmbedding, 32)
2660            .tensor(TensorEntry {
2661                name: "microdecoder.body".to_owned(),
2662                section: "microdecoder".to_owned(),
2663                dtype: StoredDtype::Q8,
2664                shape: vec![8, 8],
2665                offset: 0,
2666                length: 64,
2667                scales: Some("microdecoder.body.scales".to_owned()),
2668            })
2669            .tensor(TensorEntry {
2670                name: "text_embedding.weight".to_owned(),
2671                section: "text_embedding".to_owned(),
2672                dtype: StoredDtype::Bf16,
2673                shape: vec![4, 4],
2674                offset: 0,
2675                length: 32,
2676                scales: None,
2677            })
2678    }
2679
2680    fn streamed_artifact() -> Vec<u8> {
2681        let mut writer = stream_plan()
2682            .begin(Cursor::new(Vec::new()))
2683            .expect("the stream plan is structurally valid");
2684        writer
2685            .write_section("microdecoder", &[7_u8; 64])
2686            .expect("first section streams");
2687        writer
2688            .write_section("text_embedding", &[9_u8; 32])
2689            .expect("second section streams");
2690        writer
2691            .finish()
2692            .expect("complete stream finalizes")
2693            .into_inner()
2694    }
2695
2696    #[test]
2697    fn streaming_writer_is_canonical_and_never_retains_section_payloads() {
2698        // The buffered writer is only a small-fixture oracle here. The stream receives two
2699        // independent borrowed sections and must produce identical canonical bytes, including
2700        // directory offsets and digests, without taking ownership of either payload.
2701        let bytes = streamed_artifact();
2702        assert_eq!(bytes, artifact());
2703        let reader = FttsqReader::open(&bytes).expect("finalized stream verifies");
2704        assert_eq!(
2705            reader
2706                .tensor_bytes("microdecoder.body", &bytes)
2707                .expect("streamed tensor resolves"),
2708            &[7_u8; 64]
2709        );
2710    }
2711
2712    #[test]
2713    fn streaming_writer_refuses_out_of_order_or_incomplete_sections() {
2714        let mut writer = stream_plan()
2715            .begin(Cursor::new(Vec::new()))
2716            .expect("the stream plan is structurally valid");
2717        assert_eq!(
2718            writer
2719                .write_section("text_embedding", &[9_u8; 32])
2720                .expect_err("later sections cannot be buffered"),
2721            FttsqError::SectionWriteOutOfOrder {
2722                expected: Some("microdecoder".to_owned()),
2723                actual: "text_embedding".to_owned(),
2724            }
2725        );
2726        writer
2727            .write_section("microdecoder", &[7_u8; 63])
2728            .expect("a bounded partial chunk is accepted");
2729        assert_eq!(
2730            writer
2731                .finish()
2732                .expect_err("a partial section cannot acquire a digest"),
2733            FttsqError::SectionIncomplete {
2734                section: "microdecoder".to_owned(),
2735                declared: 64,
2736                written: 63,
2737            }
2738        );
2739    }
2740
2741    #[test]
2742    fn round_trips_through_write_and_read() {
2743        let bytes = artifact();
2744        let reader =
2745            FttsqReader::open(&bytes).expect("the artifact we just wrote must be readable");
2746
2747        assert_eq!(reader.format_version(), FORMAT_VERSION);
2748        assert_eq!(reader.model_family(), "qwen3-tts-12hz-0.6b-base");
2749        assert!(reader.license_notice().contains("Alibaba Cloud"));
2750        assert_eq!(reader.model_config()["hidden_size"], 1024);
2751        assert_eq!(reader.sections().len(), 2);
2752        assert_eq!(reader.tensors().len(), 2);
2753
2754        // Tensor payloads must come back byte-identical, through the section indirection.
2755        assert_eq!(
2756            reader
2757                .tensor_bytes("microdecoder.body", &bytes)
2758                .expect("tensor resolves"),
2759            &vec![7_u8; 64][..]
2760        );
2761        assert_eq!(
2762            reader
2763                .tensor_bytes("text_embedding.weight", &bytes)
2764                .expect("tensor resolves"),
2765            &vec![9_u8; 32][..]
2766        );
2767    }
2768
2769    #[test]
2770    fn bf16_payload_is_byte_identical_across_the_round_trip() {
2771        // Verbatim BF16 carriage is the property the converter's parity argument rests on: if the
2772        // container perturbs a single byte, every downstream parity claim is about the wrong bytes.
2773        let payload: Vec<u8> = (0..=255_u8).cycle().take(4096).collect();
2774        let bytes = FttsqWriter::new("qwen3-tts-12hz-0.6b-base", "b".repeat(64))
2775            .license_notice(NOTICE)
2776            .section("talker", AccessClass::HotRecurrentTalker, payload.clone())
2777            .tensor(TensorEntry {
2778                name: "talker.weight".to_owned(),
2779                section: "talker".to_owned(),
2780                dtype: StoredDtype::Bf16,
2781                shape: vec![64, 32],
2782                offset: 0,
2783                length: 4096,
2784                scales: None,
2785            })
2786            .finish()
2787            .expect("writable");
2788        let reader = FttsqReader::open(&bytes).expect("readable");
2789        assert_eq!(
2790            reader
2791                .tensor_bytes("talker.weight", &bytes)
2792                .expect("resolves"),
2793            &payload[..]
2794        );
2795    }
2796
2797    #[test]
2798    fn access_classes_drive_the_page_in_policy() {
2799        let bytes = artifact();
2800        let reader = FttsqReader::open(&bytes).expect("readable");
2801
2802        let hot = reader.sections_in_class(AccessClass::HotRecurrentMicrodecoder);
2803        assert_eq!(hot.len(), 1);
2804        assert!(hot[0].access_class.is_hot());
2805        assert!(!hot[0].access_class.is_row_granular());
2806
2807        let cold = reader.sections_in_class(AccessClass::ColdTextEmbedding);
2808        assert_eq!(cold.len(), 1);
2809        assert!(
2810            !cold[0].access_class.is_hot(),
2811            "the 622 MB embedding must never be advised resident"
2812        );
2813        assert!(
2814            cold[0].access_class.is_row_granular(),
2815            "the cold embedding is accessed a row at a time, never as a unit"
2816        );
2817    }
2818
2819    #[test]
2820    fn a_newer_format_version_is_refused_rather_than_guessed_at() {
2821        let mut bytes = artifact();
2822        bytes[8..12].copy_from_slice(&(FORMAT_VERSION + 1).to_le_bytes());
2823        let error = FttsqReader::parse_directory(&bytes).expect_err("must refuse");
2824        assert_eq!(
2825            error,
2826            FttsqError::UnsupportedVersion {
2827                found: FORMAT_VERSION + 1,
2828                supported: FORMAT_VERSION,
2829            }
2830        );
2831    }
2832
2833    #[test]
2834    fn bad_magic_and_truncation_are_named_refusals() {
2835        assert!(matches!(
2836            FttsqReader::parse_directory(&[]),
2837            Err(FttsqError::TooShort { .. })
2838        ));
2839        let mut bytes = artifact();
2840        bytes[0] = b'X';
2841        assert!(matches!(
2842            FttsqReader::parse_directory(&bytes),
2843            Err(FttsqError::BadMagic { .. })
2844        ));
2845    }
2846
2847    #[test]
2848    fn a_truncated_file_never_yields_a_partial_load() {
2849        let full = artifact();
2850        // Cut into the payload: the directory still parses, but a section runs past the end.
2851        for cut in [full.len() - 1, full.len() - 40, full.len() - 90] {
2852            let error = FttsqReader::open(&full[..cut]).expect_err("truncation must be refused");
2853            assert!(
2854                matches!(
2855                    error,
2856                    FttsqError::RangeOutOfBounds { .. } | FttsqError::DirectoryLength { .. }
2857                ),
2858                "unexpected error for cut at {cut}: {error}"
2859            );
2860        }
2861    }
2862
2863    #[test]
2864    fn a_single_flipped_payload_bit_fails_digest_verification() {
2865        let mut bytes = artifact();
2866        let last = bytes.len() - 1;
2867        bytes[last] ^= 0x01;
2868        let error = FttsqReader::open(&bytes).expect_err("a bit flip must be caught");
2869        assert!(
2870            matches!(
2871                &error,
2872                FttsqError::DigestMismatch { section, .. } if section == "text_embedding"
2873            ),
2874            "expected a digest mismatch for text_embedding, got {error}"
2875        );
2876        // Structure alone still parses — which is exactly why the digest gate has to exist.
2877        assert!(FttsqReader::parse_directory(&bytes).is_ok());
2878    }
2879
2880    #[test]
2881    fn a_hostile_directory_length_cannot_provoke_a_huge_read() {
2882        let mut bytes = artifact();
2883        bytes[12..20].copy_from_slice(&u64::MAX.to_le_bytes());
2884        let error = FttsqReader::parse_directory(&bytes).expect_err("must refuse");
2885        assert!(matches!(error, FttsqError::DirectoryLength { .. }));
2886    }
2887
2888    /// Directory-level defects, each of which would otherwise become a bad read at runtime.
2889    #[test]
2890    fn structural_violations_are_each_refused_by_name() {
2891        type StructuralCase = (&'static str, Value, fn(&FttsqError) -> bool);
2892        let cases: Vec<StructuralCase> = vec![
2893            (
2894                "overlapping sections",
2895                json!([
2896                    {"name": "a", "access_class": "METADATA", "offset": 100, "length": 50, "sha256": "x"},
2897                    {"name": "b", "access_class": "METADATA", "offset": 120, "length": 10, "sha256": "x"},
2898                ]),
2899                |e| matches!(e, FttsqError::SectionOverlap { .. }),
2900            ),
2901            (
2902                "a section running past the file",
2903                json!([
2904                    {"name": "a", "access_class": "METADATA", "offset": 100, "length": u64::MAX, "sha256": "x"},
2905                ]),
2906                |e| matches!(e, FttsqError::RangeOutOfBounds { .. }),
2907            ),
2908            (
2909                "a duplicate section name",
2910                json!([
2911                    {"name": "a", "access_class": "METADATA", "offset": 100, "length": 10, "sha256": "x"},
2912                    {"name": "a", "access_class": "METADATA", "offset": 200, "length": 10, "sha256": "x"},
2913                ]),
2914                |e| matches!(e, FttsqError::DuplicateName { .. }),
2915            ),
2916            (
2917                "an unknown access class",
2918                json!([
2919                    {"name": "a", "access_class": "PROBABLY_HOT", "offset": 100, "length": 10, "sha256": "x"},
2920                ]),
2921                |e| matches!(e, FttsqError::UnknownValue { .. }),
2922            ),
2923        ];
2924
2925        for (description, sections, matches_expected) in cases {
2926            let error = parse_sections(Some(&sections), 4096)
2927                .expect_err(&format!("`{description}` must be refused"));
2928            assert!(
2929                matches_expected(&error),
2930                "`{description}` produced the wrong error: {error}"
2931            );
2932        }
2933    }
2934
2935    #[test]
2936    fn a_tensor_whose_length_disagrees_with_its_shape_is_refused() {
2937        let sections = vec![SectionEntry {
2938            name: "s".to_owned(),
2939            access_class: AccessClass::Metadata,
2940            offset: 0,
2941            length: 4096,
2942            sha256: String::new(),
2943        }];
2944        let index: BTreeMap<String, usize> = [("s".to_owned(), 0)].into_iter().collect();
2945
2946        // 8x8 bf16 is 128 bytes, not 64.
2947        let tensors = json!([
2948            {"name": "t", "section": "s", "dtype": "bf16", "shape": [8, 8], "offset": 0, "length": 64},
2949        ]);
2950        let error = parse_tensors(Some(&tensors), &sections, &index).expect_err("must refuse");
2951        assert_eq!(
2952            error,
2953            FttsqError::LengthMismatch {
2954                tensor: "t".to_owned(),
2955                declared: 64,
2956                implied: 128,
2957            }
2958        );
2959    }
2960
2961    #[test]
2962    fn tensors_may_not_overlap_within_a_section() {
2963        let sections = vec![SectionEntry {
2964            name: "s".to_owned(),
2965            access_class: AccessClass::Metadata,
2966            offset: 0,
2967            length: 4096,
2968            sha256: String::new(),
2969        }];
2970        let index: BTreeMap<String, usize> = [("s".to_owned(), 0)].into_iter().collect();
2971        let tensors = json!([
2972            {"name": "a", "section": "s", "dtype": "q8", "shape": [64], "offset": 0, "length": 64},
2973            {"name": "b", "section": "s", "dtype": "q8", "shape": [64], "offset": 32, "length": 64},
2974        ]);
2975        let error = parse_tensors(Some(&tensors), &sections, &index).expect_err("must refuse");
2976        assert!(matches!(error, FttsqError::TensorOverlap { .. }), "{error}");
2977    }
2978
2979    #[test]
2980    fn a_tensor_leaving_its_section_is_refused() {
2981        let sections = vec![SectionEntry {
2982            name: "s".to_owned(),
2983            access_class: AccessClass::Metadata,
2984            offset: 0,
2985            length: 64,
2986            sha256: String::new(),
2987        }];
2988        let index: BTreeMap<String, usize> = [("s".to_owned(), 0)].into_iter().collect();
2989        let tensors = json!([
2990            {"name": "a", "section": "s", "dtype": "q8", "shape": [64], "offset": 32, "length": 64},
2991        ]);
2992        let error = parse_tensors(Some(&tensors), &sections, &index).expect_err("must refuse");
2993        assert!(
2994            matches!(error, FttsqError::RangeOutOfBounds { .. }),
2995            "{error}"
2996        );
2997    }
2998
2999    #[test]
3000    fn an_artifact_without_a_license_notice_cannot_be_written_or_read() {
3001        // Writer side.
3002        let error = FttsqWriter::new("qwen3-tts-12hz-0.6b-base", "c".repeat(64))
3003            .section("m", AccessClass::Metadata, vec![1, 2, 3])
3004            .finish()
3005            .expect_err("Apache-2.0 §4 makes the notice mandatory");
3006        assert_eq!(error, FttsqError::LicenseNoticeMissing);
3007
3008        // Reader side: an artifact whose notice was stripped after the fact is still refused.
3009        let mut bytes = artifact();
3010        let directory_len = u64::from_le_bytes(bytes[12..20].try_into().expect("header length"));
3011        let directory_start = HEADER_PREFIX_BYTES as usize;
3012        let directory_end = directory_start + directory_len as usize;
3013        let mut directory: Value = serde_json::from_slice(&bytes[directory_start..directory_end])
3014            .expect("fixture directory");
3015        directory["license_notice"] = Value::String(String::new());
3016        let mut replacement = serde_json::to_vec(&directory).expect("serializes directory");
3017        assert!(
3018            replacement.len() <= directory_len as usize,
3019            "removing a notice cannot grow it"
3020        );
3021        replacement.resize(directory_len as usize, b' ');
3022        bytes[directory_start..directory_end].copy_from_slice(&replacement);
3023        assert_eq!(
3024            FttsqReader::open(&bytes).expect_err("must refuse a missing notice"),
3025            FttsqError::LicenseNoticeMissing
3026        );
3027    }
3028
3029    #[test]
3030    fn write_to_path_lands_a_complete_readable_artifact_and_leaves_no_temporary() {
3031        let dir = std::env::temp_dir().join(format!("ftts-fttsq-write-{}", std::process::id()));
3032        std::fs::create_dir_all(&dir).expect("scratch dir");
3033        let path = dir.join("model.fttsq");
3034
3035        FttsqWriter::new("qwen3-tts-12hz-0.6b-base", "d".repeat(64))
3036            .license_notice(NOTICE)
3037            .section("m", AccessClass::HotRecurrentMicrodecoder, vec![3_u8; 128])
3038            .section(
3039                "embedding",
3040                AccessClass::ColdTextEmbedding,
3041                vec![9_u8; 8192],
3042            )
3043            .tensor(TensorEntry {
3044                name: "m.w".to_owned(),
3045                section: "m".to_owned(),
3046                dtype: StoredDtype::Q8,
3047                shape: vec![128],
3048                offset: 0,
3049                length: 128,
3050                scales: None,
3051            })
3052            .tensor(TensorEntry {
3053                name: "embedding.one_row".to_owned(),
3054                section: "embedding".to_owned(),
3055                dtype: StoredDtype::Q8,
3056                shape: vec![32],
3057                offset: 4096,
3058                length: 32,
3059                scales: None,
3060            })
3061            .write_to_path(&path)
3062            .expect("artifact is writable");
3063
3064        let bytes = std::fs::read(&path).expect("artifact is readable");
3065        let reader = FttsqReader::open(&bytes).expect("what landed on disk must verify");
3066        assert_eq!(
3067            reader.tensor_bytes("m.w", &bytes).expect("resolves"),
3068            &vec![3_u8; 128][..]
3069        );
3070
3071        let mapped = MappedFttsq::open(&path).expect("mapped artifact validates");
3072        assert_eq!(mapped.len(), bytes.len());
3073        assert_eq!(
3074            mapped
3075                .tensor_bytes("embedding.one_row")
3076                .expect("row range resolves without copying the section"),
3077            &vec![9_u8; 32][..]
3078        );
3079
3080        let micro = mapped
3081            .page_advice()
3082            .iter()
3083            .find(|application| application.section == "m")
3084            .expect("microdecoder application is recorded");
3085        assert_eq!(micro.policy, PagePolicy::Resident);
3086        assert_eq!(micro.requested, Some(MemoryAdvice::WillNeed));
3087        assert!(
3088            !matches!(micro.outcome, PageAdviceOutcome::Failed(_)),
3089            "a valid mapped microdecoder section must receive a usable advice result: {micro:?}"
3090        );
3091
3092        let embedding = mapped
3093            .page_advice()
3094            .iter()
3095            .find(|application| application.section == "embedding")
3096            .expect("embedding application is recorded");
3097        assert_eq!(embedding.policy, PagePolicy::LazyRowGranular);
3098        assert_eq!(embedding.requested, Some(MemoryAdvice::Random));
3099        assert!(
3100            !embedding.policy.may_prefetch(),
3101            "the cold embedding policy must make wholesale prefetch impossible"
3102        );
3103        for observation in [&embedding.residency_before, &embedding.residency_after] {
3104            match observation {
3105                PageResidencyOutcome::Measured {
3106                    resident_pages,
3107                    total_pages,
3108                } => assert!(
3109                    resident_pages <= total_pages,
3110                    "the OQ-18 residency measurement exceeded the section's page span"
3111                ),
3112                PageResidencyOutcome::Unsupported => {}
3113                PageResidencyOutcome::Failed(detail) => {
3114                    panic!("the cold embedding residency measurement failed: {detail}");
3115                }
3116            }
3117        }
3118        assert!(
3119            mapped.page_advice().iter().all(|application| {
3120                application.policy.may_prefetch()
3121                    || application.requested != Some(MemoryAdvice::WillNeed)
3122            }),
3123            "a non-prefetch section was routed to MADV_WILLNEED"
3124        );
3125
3126        // The temporary must not survive a successful write.
3127        let strays: Vec<_> = std::fs::read_dir(&dir)
3128            .expect("dir is listable")
3129            .filter_map(Result::ok)
3130            .map(|entry| entry.file_name().to_string_lossy().into_owned())
3131            .filter(|name| name.contains(".tmp."))
3132            .collect();
3133        assert!(strays.is_empty(), "temporary files left behind: {strays:?}");
3134
3135        std::fs::remove_file(&path).expect("cleanup");
3136    }
3137
3138    #[test]
3139    fn write_to_path_refuses_before_touching_the_filesystem_when_the_notice_is_missing() {
3140        let dir = std::env::temp_dir().join(format!("ftts-fttsq-refuse-{}", std::process::id()));
3141        std::fs::create_dir_all(&dir).expect("scratch dir");
3142        let path = dir.join("model.fttsq");
3143
3144        let error = FttsqWriter::new("qwen3-tts-12hz-0.6b-base", "e".repeat(64))
3145            .section("m", AccessClass::Metadata, vec![1, 2, 3])
3146            .write_to_path(&path)
3147            .expect_err("a notice-less artifact must never reach disk");
3148        assert_eq!(error, FttsqError::LicenseNoticeMissing);
3149        assert!(
3150            !path.exists(),
3151            "a refused artifact must not leave a file behind"
3152        );
3153    }
3154
3155    /// The invariant the whole page-in policy exists to enforce.
3156    #[test]
3157    fn the_cold_text_embedding_is_never_prefetched_and_hot_classes_always_are() {
3158        assert_eq!(
3159            AccessClass::ColdTextEmbedding.page_policy(),
3160            PagePolicy::LazyRowGranular
3161        );
3162        assert!(
3163            !AccessClass::ColdTextEmbedding.page_policy().may_prefetch(),
3164            "MADV_WILLNEED over the ~622 MB embedding would evict the microdecoder pack"
3165        );
3166
3167        for hot in [
3168            AccessClass::HotRecurrentMicrodecoder,
3169            AccessClass::HotRecurrentTalker,
3170            AccessClass::HotCodecDecoder,
3171        ] {
3172            assert_eq!(hot.page_policy(), PagePolicy::Resident);
3173            assert!(hot.page_policy().may_prefetch());
3174        }
3175        for cold in [
3176            AccessClass::EnrollmentSpeakerEncoder,
3177            AccessClass::EnrollmentCodecEncoder,
3178            AccessClass::Metadata,
3179        ] {
3180            assert_eq!(cold.page_policy(), PagePolicy::OnDemand);
3181            assert!(!cold.page_policy().may_prefetch());
3182        }
3183
3184        // is_hot() and the policy must not be able to disagree — two encodings of one fact.
3185        for class in [
3186            AccessClass::HotRecurrentMicrodecoder,
3187            AccessClass::HotRecurrentTalker,
3188            AccessClass::HotCodecDecoder,
3189            AccessClass::ColdTextEmbedding,
3190            AccessClass::EnrollmentSpeakerEncoder,
3191            AccessClass::EnrollmentCodecEncoder,
3192            AccessClass::Metadata,
3193        ] {
3194            assert_eq!(
3195                class.is_hot(),
3196                class.page_policy().may_prefetch(),
3197                "is_hot() and page_policy() disagree for {class}"
3198            );
3199            assert_eq!(
3200                class.is_row_granular(),
3201                class.page_policy() == PagePolicy::LazyRowGranular,
3202                "is_row_granular() and page_policy() disagree for {class}"
3203            );
3204        }
3205    }
3206
3207    #[test]
3208    fn the_page_in_plan_prefetches_the_microdecoder_before_the_larger_talker() {
3209        // Sizes stand in for the real ~110 MB pack and ~440 MB talker.
3210        let bytes = FttsqWriter::new("qwen3-tts-12hz-0.6b-base", "f".repeat(64))
3211            .license_notice(NOTICE)
3212            .section("talker", AccessClass::HotRecurrentTalker, vec![1_u8; 400])
3213            .section("embedding", AccessClass::ColdTextEmbedding, vec![2_u8; 900])
3214            .section(
3215                "micro",
3216                AccessClass::HotRecurrentMicrodecoder,
3217                vec![3_u8; 100],
3218            )
3219            .section("meta", AccessClass::Metadata, vec![4_u8; 8])
3220            .finish()
3221            .expect("writable");
3222        let reader = FttsqReader::open(&bytes).expect("readable");
3223
3224        let plan = reader.page_in_plan();
3225        let order: Vec<&str> = plan
3226            .iter()
3227            .map(|(section, _)| section.name.as_str())
3228            .collect();
3229        assert_eq!(
3230            order,
3231            vec!["micro", "talker", "embedding", "meta"],
3232            "resident sections first, smallest first, so the 15x-reread pack wins the cache race"
3233        );
3234        assert_eq!(plan[0].1, PagePolicy::Resident);
3235        assert_eq!(plan[2].1, PagePolicy::LazyRowGranular);
3236        assert_eq!(plan[3].1, PagePolicy::OnDemand);
3237
3238        // Nothing outside the resident group may ever be prefetched.
3239        for (section, policy) in &plan {
3240            assert_eq!(
3241                policy.may_prefetch(),
3242                section.access_class.is_hot(),
3243                "section `{}` would be prefetched against policy",
3244                section.name
3245            );
3246        }
3247    }
3248
3249    fn census_fixture() -> (Vec<u8>, ArtifactManifest) {
3250        let bytes = FttsqWriter::new("qwen3-tts-12hz-0.6b-base", "g".repeat(64))
3251            .license_notice(NOTICE)
3252            .section(
3253                "micro",
3254                AccessClass::HotRecurrentMicrodecoder,
3255                vec![1_u8; 64],
3256            )
3257            .section("embedding", AccessClass::ColdTextEmbedding, vec![2_u8; 32])
3258            .tensor(TensorEntry {
3259                name: "micro.body".to_owned(),
3260                section: "micro".to_owned(),
3261                dtype: StoredDtype::Q8,
3262                shape: vec![8, 8],
3263                offset: 0,
3264                length: 64,
3265                scales: None,
3266            })
3267            .tensor(TensorEntry {
3268                name: "text_embedding.weight".to_owned(),
3269                section: "embedding".to_owned(),
3270                dtype: StoredDtype::Bf16,
3271                shape: vec![4, 4],
3272                offset: 0,
3273                length: 32,
3274                scales: None,
3275            })
3276            .finish()
3277            .expect("writable");
3278
3279        let manifest = ArtifactManifest::new("qwen3-tts pinned")
3280            .expect(ExpectedArtifactTensor {
3281                name: "micro.body".to_owned(),
3282                shape: vec![8, 8],
3283                dtype: StoredDtype::Q8,
3284                access_class: AccessClass::HotRecurrentMicrodecoder,
3285            })
3286            .expect(ExpectedArtifactTensor {
3287                name: "text_embedding.weight".to_owned(),
3288                shape: vec![4, 4],
3289                dtype: StoredDtype::Bf16,
3290                access_class: AccessClass::ColdTextEmbedding,
3291            });
3292        (bytes, manifest)
3293    }
3294
3295    #[test]
3296    fn a_matching_artifact_passes_its_census() {
3297        let (bytes, manifest) = census_fixture();
3298        let reader = FttsqReader::open(&bytes).expect("readable");
3299        let report = manifest.audit(&reader);
3300        assert!(report.is_green(), "{}", report.render());
3301        assert!(reader.verify_census(&manifest).is_ok());
3302    }
3303
3304    /// Each divergence class must be caught, and all of them reported in one pass.
3305    #[test]
3306    fn the_census_names_every_divergence_class_in_one_pass() {
3307        let (bytes, _) = census_fixture();
3308        let reader = FttsqReader::open(&bytes).expect("readable");
3309
3310        let manifest = ArtifactManifest::new("deliberately wrong")
3311            // Right name, wrong shape AND wrong dtype: both must be reported, not just the first.
3312            .expect(ExpectedArtifactTensor {
3313                name: "micro.body".to_owned(),
3314                shape: vec![16, 4],
3315                dtype: StoredDtype::Q4,
3316                access_class: AccessClass::HotRecurrentMicrodecoder,
3317            })
3318            // Correct in every respect except where it lives — the silent one.
3319            .expect(ExpectedArtifactTensor {
3320                name: "text_embedding.weight".to_owned(),
3321                shape: vec![4, 4],
3322                dtype: StoredDtype::Bf16,
3323                access_class: AccessClass::HotRecurrentTalker,
3324            })
3325            // Required but absent.
3326            .expect(ExpectedArtifactTensor {
3327                name: "codec.decoder.weight".to_owned(),
3328                shape: vec![2],
3329                dtype: StoredDtype::Q8,
3330                access_class: AccessClass::HotCodecDecoder,
3331            });
3332
3333        let report = manifest.audit(&reader);
3334        assert!(!report.is_green());
3335        assert_eq!(report.count_of("shape_mismatch"), 1, "{}", report.render());
3336        assert_eq!(report.count_of("dtype_mismatch"), 1, "{}", report.render());
3337        assert_eq!(
3338            report.count_of("wrong_access_class"),
3339            1,
3340            "a tensor in the wrong access class still produces correct audio while destroying \
3341             residency — the census is the only thing that catches it:\n{}",
3342            report.render()
3343        );
3344        assert_eq!(report.count_of("missing"), 1, "{}", report.render());
3345
3346        let rendered = report.render();
3347        for expected in [
3348            "micro.body",
3349            "text_embedding.weight",
3350            "codec.decoder.weight",
3351            "ACCESS_CLASS",
3352            "SHAPE",
3353            "DTYPE",
3354            "MISSING",
3355        ] {
3356            assert!(
3357                rendered.contains(expected),
3358                "census report is missing `{expected}`:\n{rendered}"
3359            );
3360        }
3361
3362        assert!(reader.verify_census(&manifest).is_err());
3363    }
3364
3365    /// An artifact carrying tensors nobody expected is a *different checkpoint*.
3366    #[test]
3367    fn unexpected_tensors_are_reported_as_extra() {
3368        let (bytes, _) = census_fixture();
3369        let reader = FttsqReader::open(&bytes).expect("readable");
3370        let manifest = ArtifactManifest::new("partial").expect(ExpectedArtifactTensor {
3371            name: "micro.body".to_owned(),
3372            shape: vec![8, 8],
3373            dtype: StoredDtype::Q8,
3374            access_class: AccessClass::HotRecurrentMicrodecoder,
3375        });
3376        let report = manifest.audit(&reader);
3377        assert_eq!(report.count_of("extra"), 1, "{}", report.render());
3378        assert!(report.render().contains("text_embedding.weight"));
3379    }
3380
3381    #[test]
3382    fn quantized_dtype_sizes_are_exact_including_the_odd_q4_tail() {
3383        assert_eq!(StoredDtype::Bf16.storage_bytes(10), Some(20));
3384        assert_eq!(StoredDtype::F32.storage_bytes(10), Some(40));
3385        assert_eq!(StoredDtype::Q8.storage_bytes(10), Some(10));
3386        // Two elements per byte, rounding up: an odd count still occupies a whole trailing byte.
3387        assert_eq!(StoredDtype::Q4.storage_bytes(10), Some(5));
3388        assert_eq!(StoredDtype::Q4.storage_bytes(11), Some(6));
3389        // Overflow is reported, never wrapped into a small, plausible-looking size.
3390        assert_eq!(StoredDtype::F32.storage_bytes(u64::MAX), None);
3391    }
3392
3393    #[test]
3394    fn wire_strings_round_trip_for_every_enum_value() {
3395        for class in [
3396            AccessClass::HotRecurrentMicrodecoder,
3397            AccessClass::HotRecurrentTalker,
3398            AccessClass::HotCodecDecoder,
3399            AccessClass::ColdTextEmbedding,
3400            AccessClass::EnrollmentSpeakerEncoder,
3401            AccessClass::EnrollmentCodecEncoder,
3402            AccessClass::Metadata,
3403        ] {
3404            assert_eq!(AccessClass::parse(class.as_str()), Some(class));
3405        }
3406        for dtype in [
3407            StoredDtype::Bf16,
3408            StoredDtype::F32,
3409            StoredDtype::Q8,
3410            StoredDtype::Q4,
3411        ] {
3412            assert_eq!(StoredDtype::parse(dtype.as_str()), Some(dtype));
3413        }
3414        assert_eq!(AccessClass::parse("HOT_SOMETHING"), None);
3415        assert_eq!(StoredDtype::parse("f16"), None);
3416    }
3417}