Skip to content
VERASPEC
Repository
VER 1.1 — draftdraft

§11 Spaces

11.1 Joint visual-text spaces

VER 1.0's kind is visual | text | fused, and 1.0 §8 requires a contextual embedding to carry “a text-space space_id”. CLIP- and SigLIP-class models — the dominant architecture for exactly this use case — are neither: one set of weights embeds images and text into one geometry, which is what makes a natural-language query comparable with a visual vector. Under 1.0 a Producer must either put text vectors in a kind: "visual" space or mint a second space_id over identical weights, and both are misdeclarations.

spaceDescriptor.kind gains joint_visual_text: a single space whose weights embed both images and text into one comparable geometry.

  • A canonical_visual embedding MAY be declared in a joint_visual_text space. 1.0 §6's requirement — “at least one embedding with `role: canonical_visualin a space ofkind: "visual"`” — is read in 1.1 as satisfied by kind ∈ {visual, joint_visual_text}.
  • A contextual embedding MAY be declared in a joint_visual_text space. 1.0 §8's “a text-space space_id” is read as “a space that admits text input”, i.e. kind ∈ {text, joint_visual_text}.
  • A joint_visual_text descriptor's preprocessing MUST state both halves: the visual members (resize, interpolation, antialias, crop, mean, std, channel order) and the text members (tokenizer id and hash, max length, truncation, casing). A joint space that states only one half is not a full descriptor (E13). Per-kind member lists are graded by the profile (VER405) rather than encoded — see §15.4.
  • 1.0 §8's prohibition is unchanged and is not weakened by co-location: a contextual vector MUST NOT alter, contribute to, or be fused into any canonical_visual embedding, even when both live in the same space.

11.2 hamming, tolerance_hamming_max, and quantized spaces

dtype: "binary" is legal in VER 1.0 and unusable: the metric enum offers only cosine | dot | l2, normalization is meaningless for bit vectors, dim's unit is undefined, and the inline vector array cannot carry bits.

Everything in this section that JSON Schema can express is now expressed. An earlier revision left it all in prose to protect the superset property; the result was a specification whose central claim about binary spaces was invisible to every validator.

  • spaceDescriptor.metric gains hamming.
  • For dtype: "binary": dim counts bits; metric MUST be hamming; normalization MUST be none. All three directions are schema-encoded, including the converse — metric: "hamming" MUST NOT appear on a space whose dtype is not binary.
  • The payload is carried as vector_b64 or vector_ref only: **the inline vector array MUST NOT be used for a binary embedding** (schema-encoded). A float array of ±1 is a different object from the packed bits the space defines, and publishing it under the space's dim would misstate the vector's length by a factor of eight.
  • Packing is 8 components per octet, most-significant-bit first within each octet, in ascending component order, ceil(dim / 8) octets total (E24). The byte-length arithmetic relates dim, dtype and a payload length; JSON Schema cannot compare two members, so it is profile-enforced (VER603VER605).
  • A binary space's reference claim is stated in Hamming distance. tolerance_hamming_max — the maximum Hamming distance in bits between a published reference vector and a re-computed one — is REQUIRED wherever a binary space carries a reference block, and tolerance_cosine, tolerance_cosine_p5 and tolerance_cosine_worst MUST NOT appear on it. Both halves are schema-encoded. The earlier ±1-expansion reading, under which a cosine floor was evaluated over the expanded bits, is withdrawn: it is monotone in Hamming distance but it is not the quantity a binary retrieval system measures, and expressing a bit-vector tolerance in a float-vector metric made binary conformance claims incomparable with the systems that consume them. tolerance_hamming_max MUST NOT exceed the space's dim; that bound compares two members and is profile-enforced.
  • For dtype: "int8": metric SHOULD be dot or l2. cosine over int8 vectors is well-defined but is a different geometry from the fp32 space it was quantized from, and the two MUST be declared as different space_ids regardless. This is a SHOULD and a cross-Record MUST; neither is encodable, and neither is claimed to be.

11.3 Descriptor binding

1.0 §6.2 declares descriptors immutable once published, but they are carried inline in every Record with no binding to the published form, so two Records can present contradictory descriptors under one space_id and nothing detects it.

spaceDescriptor gains two optional members:

  • descriptor_uri — where the authoritative published descriptor for this space_id is retrievable.
  • descriptor_sha256 — SHA-256 of the RFC 8785 JCS serialization of the published descriptor with the descriptor_sha256 member removed.

descriptor_uri present ⇒ descriptor_sha256 REQUIRED (schema-encoded, §15.3). A Consumer that holds two Records citing the same space_id with different descriptor_sha256 values MUST treat the vectors as incomparable and SHOULD report a drift violation of 1.0 §6.2. A Consumer that fetches descriptor_uri MUST verify it against descriptor_sha256.

11.4 Duplicate space_id becomes normative

E18 established as a profile rule that space_id is unique within a Record's spaces[]. In 1.1 it is normative specification text:

space_id MUST be unique within spaces[]. A Record carrying two entries with the same space_id — identical or not — is non-conformant, and a Consumer MUST reject it rather than resolve the duplicate by array order.

What changes in 1.1 is the status of the rule, not who enforces it: it is now a requirement of the standard rather than of one profile. The 1.1 schema still cannot express it — JSON Schema has no key-uniqueness keyword over an array of objects, and uniqueItems compares whole items, so two entries differing in any member slip past it — so enforcement remains in the profile validator (VER401). This is a limit of the language, not a compatibility trade-off, and §15.4 records it as such.

11.5 Fused spaces: model optional, recipe in identity

VER 1.0 requires model with family, checkpoint, and weights_sha256 on every space, including a kind: "fused" space that runs no forward pass and has no weights of its own. Producers satisfy it by copying an input space's model block, which is a fabricated pin.

  • spaceDescriptor.model is REQUIRED for `kind ∈ {visual, text, joint_visual_text}and OPTIONAL forkind: "fused"` (schema-encoded).
  • A fused space SHOULD instead declare derived_from: the two or more space_id values it is derived from.
  • The fusion recipe is part of the fused space's identity. 1.0 §6.2's rule reads, in 1.1: new weights, preprocessing, dtype, metric, **or fusion recipe** ⇒ new space_id. Changing method or weights while keeping the space_id is non-conformant (E20).
  • embeddings[].recipe.inputs are space_id values declared in the same Record (E20), and weights, when present, aligns 1:1 with inputs.

The model relaxation is a widening. The identity rule is prose that no schema can check: whether two descriptors with one space_id differ is a cross-Record question.

11.6 space_id syntax

The 1.0 pattern contradicts the prose it implements: it rejects a reverse-DNS authority whose first label contains a hyphen (which excludes every punycode/IDN domain and any organisation with a hyphenated name), and it rejects semver prerelease and build metadata.

The space_id pattern is widened so that:

  • the first authority label may contain hyphens — [a-z0-9][a-z0-9-]*, where 1.0.1 allowed [a-z0-9]+;
  • later labels keep the 1.0.1 character class exactly ([a-z0-9-]+);
  • the version part accepts optional lowercase prerelease and build metadata (1.0.0-rc.1, 2.1.0+build.7, 2.1.0-rc.1+build.7).
text
^[a-z0-9][a-z0-9-]*(\.[a-z0-9-]+)+/[0-9]+\.[0-9]+(\.[0-9]+)?(-[0-9a-z][0-9a-z.-]*)?(\+[0-9a-z][0-9a-z.-]*)?$

This is a pure widening: every space_id the 1.0.1 pattern accepts, this pattern accepts. That includes identifiers the 1.0.1 pattern admits by accident — org.-example/1.0, org.example-/1.0, acme.my-org.space-/2.1 — whose later labels begin or end with a hyphen. Tidying those edges would be a narrowing of a published pattern, and this is the one place in the draft where a narrowing would buy nothing: no space_id in circulation is made safer by rejecting an edge hyphen. New identifiers SHOULD NOT use a hyphen at the start or end of any label; a future major release may withdraw them.

Numeric version components SHOULD follow semver's no-leading-zeros rule; the pattern remains permissive on that point for the same reason.

Two constraints the syntax cannot carry, stated normatively: the authority label sequence MUST be a domain the descriptor's publisher controls, and a Producer MUST NOT mint identifiers under an authority it does not control. Registry-backed resolvability of space_id is deferred past 1.1.

The widening is verified over the grammar of the 1.0.1 pattern, not over a sample identifier: RELEASE.md § "Verification" carries a generator that emits identifiers from the 1.0.1 grammar — an exhaustive sweep of every one- and two-character label, which covers every hyphen position in them, plus a randomized sweep over longer labels, label counts and both version shapes — and checks each against the 1.1 pattern. A single-example regression test cannot establish a subset relation between two regular languages, and the earlier draft's did not: it missed a silent narrowing of exactly these edge forms.