§4 Model bundle manifests (ADR-0007)
4.1 The problem
1.0 §6.1 says “Weight pinning is mandatory” and the schema constrains weights_sha256 to 64 hex characters. Nothing says what is hashed. A modern checkpoint is not one file: it is a config, a tokenizer, and one or more shards, sometimes present in two formats at once. Measured today: one implementation folds file names into the digest preimage with no length framing, and hashes whatever the local cache happens to hold — so two conformant producers of the same space_id emit different weights_sha256. The mandatory pin is unverifiable.
4.2 Normative text (DRAFT)
ADR-0007 is accepted in principle, revised. The revision is the one that matters most: weights_sha256 is not redefined. Retrofitting a preimage onto a published field silently changes the meaning of values already in circulation, which is a major change wearing a minor's clothing. weights_sha256 keeps its 1.0 reading — E29's reading — forever: SHA-256, lowercase hex, over an artifact the Producer chooses and SHOULD document, comparable only within a producer. The 1.1 field is new, and it carries its own digest.
spaceDescriptor.model.bundledeclares the file set the checkpoint consists of, and carries the digest of that declaration:
bundle_digest_algnames the construction intrinsically."ver-bundle-manifest/1"is the following definition, and a future construction is a new identifier rather than a new reading of this one:
manifest_sha256:= SHA-256( RFC 8785 JCS serialization of{"files": [ … ]}), over exactly the entries carried infiles, pre-sorted byte-wise ascending by the UTF-8 encoding ofpath.There is no separate
canonicalizationmember: a decorative member that every producer sets to the same constant is a member that tells a verifier nothing, and folding it into the algorithm identifier removes the possibility of a manifest whose two self-descriptions disagree.
manifest_sha256is the cross-producer pin.weights_sha256is not, and MUST NOT be compared across producers (E29). A Consumer comparing two descriptors for the samespace_idcomparesmanifest_sha256.pathgrammar.pathis relative to the checkpoint root./is the only separator. Segments are non-empty and are never.or..; there is no leading/; and every character is drawn from the printable portable set[A-Za-z0-9._-]. **Backslash, colon, NUL and control characters are rejected** — the schema encodes both an anchored pattern and a complement-class rejection, so a validator's reading of$cannot admit a trailing newline (VER-1.1-draft.md§15, findingREV-05). A wider character repertoire — Unicode NFC, case-sensitive comparison — may be defined later by a newbundle_digest_alg; it is deliberately not defined now, because path canonicalization across platforms is where this class of digest goes wrong.- Paths MUST be unique within a manifest, and entries MUST be pre-sorted byte-wise ascending by the UTF-8 encoding of
pathas stored — a verifier checks the order it received rather than sorting first, so a manifest whose order was tampered with is detected rather than repaired. Duplicate or aliasing paths (two entries naming one file) are non-conformant.- **Verification MUST resolve every
pathbeneath a verified root and MUST NOT follow symbolic links out of it.** The grammar makes a traversal payload unrepresentable; this rule makes a symlinked traversal unusable, which the grammar cannot reach.byte_lengthis the file's exact length in octets and is present for framing: with lengths in the manifest, the concatenation ambiguity ofSC-03cannot arise.sha256is the file's own digest, over its bytes and nothing else — in particular the filename is not folded in (SC-01).- **Membership is defined by the space publisher, and the digest proves the integrity of the LIST, not its completeness.** A manifest with correct per-file digests but a missing shard is a self-consistent manifest describing an incomplete bundle. No digest can attest to a file nobody listed. The bundle MUST list every file that affects the model's output and MUST NOT list files that do not (READMEs, licences,
.gitattributes); where a checkpoint is published in two weight formats, the bundle lists one.- “Affects the model's output” means the forward pass this space declares, not everything the checkpoint can do. A
kind: "visual"space over a joint image-text checkpoint lists the image tower's files and MUST NOT list the tokenizer, which cannot affect an image embedding; ajoint_visual_textspace over the same checkpoint lists both, because both towers are in its declared geometry. Two spaces over one checkpoint that declare different forward passes therefore have different bundles and differentmanifest_sha256values. That is correct and not a collision: they are different spaces, and 1.0 §6.2 already makes them so.- Artifacts outside the checkpoint are pinned in
model.artifacts[].
4.3 model.artifacts[] and model.revision
spaceDescriptor.model.artifactsis an optional array of typed non-checkpoint artifacts that affect the space's output:
roleis one oftokenizer,pca_basis,codebook,processor,normalizer,other. The set is closed in 1.1 and is a registry candidate (§2.4).- At least one of
path(same grammar as a bundle path) anduriis REQUIRED;sha256andbyte_lengthare always REQUIRED, so an artifact named by URI is pinned by construction (§15.3).- These artifacts are part of the space's identity under 1.0 §6.2.
bundledescribes a checkpoint; stretching it into a general artifact manifest would make the file list depend on how a Producer happens to package its pipeline.- Where an artifact is also named by an E13 preprocessing member — the text half's
tokenizer_sha256— the two values MUST be equal.preprocessingkeeps the E13 member because E13 defines the full descriptor for 1.0.x andVER405grades it;artifacts[]is the general typed form.
model.checkpoint_uriis a new optional member naming where the checkpoint is remotely resolvable.checkpoint_uripresent ⇒model.revisionREQUIRED (schema-encoded).revisionis an immutable revision — a commit identifier or content digest, never a mutable branch name.A Producer whose checkpoint is remotely resolvable MUST state both
checkpoint_uriandrevision. The schema can only enforce the implication, because it cannot tell whether a barecheckpointstring names a remote repository; the unconditional half is profile material. An unpinned remote checkpoint means onespace_iddenotes different weights over time, which is a 1.0 §6.2 immutability violation (SC-05).
Interpretation for 1.0 Records. A 1.0 Record carries weights_sha256 with no stated construction. A Consumer MUST NOT compare weights_sha256 values across Producers; in 1.0 — and in 1.1 — it is a within-producer consistency signal only. This interpretation is recorded in the 1.0.1 errata annex as item E29, which also asks 1.0.x Producers to document which artifact they hashed.
