Skip to content
VERASPEC
Repository
Versioning policystable

§3 The frozen-artifact rule

A published artifact is never modified. Not to fix a typo, not to correct a digest, not to repair an example.

standards/ver/1.0.0/ is immutable. The files in it are byte-identical to what was published on 21 August 2026, digests recorded in its RELEASE.md, and the annotated Git tag ver/1.0.0 marks the commit that introduced the directory — created by step 4 of the publication checklist in §3.3, which is also where a reader verifies it. It is frozen with its defects — including a 96-hex pdq/1.0 value and a placeholder raw digest — because a consumer who pinned those bytes must be able to keep resolving them.

Corrections ship as a new release directory. The originals stay at their historical locations (spec/, lexicons/org/verstandard/record.json) because running code and published prose reference those paths; the freeze copies, it never moves.

Anyone editing a file under standards/ver/1.0.0/ has made a mistake. The repository treats that directory as append-never.

3.1 Schema $id policy

Every published schema carries a $id that is unique to its release and is never reused, redirected, or overwritten:

Release$id
1.0.0https://verstandard.org/schemas/ver-record-1.0.json
1.0.1https://verstandard.org/schemas/ver-record-1.0.1.json
1.1 drafthttps://verstandard.org/schemas/ver-record-1.1-draft.json

A $id is a permanent identifier for one exact set of constraints. Serving different bytes at a published $id silently changes what every cached consumer believes conformance means — which is the failure this policy exists to prevent.

The 1.0.0 $id had no patch component, and could not have grown one without being reused. This is why 1.0.1 took a new identifier rather than a suffix (finding SCH-19).

3.2 Release directory layout

text
standards/ver/<release>/
ver-record.schema.json the schema for that release
example-record*.json the golden example(s) for that release
RELEASE.md contents, source paths, SHA-256 of every other file
… release-specific artifacts (annex, draft prose,
wire-profile lexicon when the release reissues it)

A release names its examples for what they demonstrate — 1.1-draft ships example-record-lineage.json — so the glob, not a fixed filename, is the rule.

RELEASE.md is mandatory and records the SHA-256 of every file in the directory other than RELEASE.md itself, which cannot contain its own digest. Verify a release by recomputing those digests, and verify RELEASE.md by the tag that pins the commit (§3.3). A release without recorded digests cannot be verified, and an unverifiable release is not a release.

3.3 Publication checklist

A release is published by a single commit, then tagged. Run these from the repository root, in order; step 4 is what makes §3's tag claim true rather than merely asserted.

bash
# 1. Every file in every release directory must have its digest recorded in
# that directory's RELEASE.md — and RELEASE.md itself must not.
.venv/bin/python - <<'PY'
import hashlib, pathlib, sys
missing = 0
for release in sorted(pathlib.Path("standards/ver").glob("*/RELEASE.md")):
text = release.read_text(encoding="utf-8")
files = sorted(p for p in release.parent.rglob("*")
if p.is_file() and p.name != "RELEASE.md")
for path in files:
digest = hashlib.sha256(path.read_bytes()).hexdigest()
if digest not in text:
print(f"UNRECORDED {path}: {digest}")
missing += 1
print(f"{release}: {len(files)} file(s) checked")
sys.exit(1 if missing else 0)
PY
# 2. The published example must validate against its own release's schema.
PYTHONPATH=packages/ver-validator/src .venv/bin/python -m ver_validator.cli \
--schema 1.0.1 standards/ver/1.0.1/example-record.json
# 3. Commit the release directory. One commit, nothing else in it.
git add standards/ver/1.0.0 && git commit -m "publish VER 1.0.0 frozen artifact set"
# 4. Tag that commit, annotated, before pushing.
git tag -a ver/1.0.0 -m "VER 1.0.0 frozen artifact set"
# 5. Verify the tag resolves to the commit that introduced the directory.
git tag -l 'ver/*'
git rev-parse ver/1.0.0^{commit}
git log --oneline -1 ver/1.0.0
# 6. Push commit and tag together.
git push origin HEAD --follow-tags

One tag per published release directory, named ver/<release>, annotated, never moved and never deleted.

The same immutability doctrine governs the standards trees outside standards/ver/: a published registry snapshot (standards/registry/snapshots/<n>/, immutable per that registry's own README once its digest is recorded), a published decoder-corpus release (standards/decoder-corpus/<n>/), and a published reference-set manifest (standards/reference-sets/) are corrected by publishing a successor, never by editing in place. Their release mechanics live in their own READMEs; the never-edit rule lives here. A draft directory is not tagged until it ratifies. standards/ver/1.0.1/ is tagged ver/1.0.1 by the same steps with the paths substituted.

3.4 The publication bar

§3.3 is the mechanics of publishing. This is the bar a release must clear before anyone runs it. It applies to every release from 1.1 onward.

A release MUST NOT be published until all six hold:

  1. Two independent implementations, built by parties that did not write the specification, have each produced and consumed records of the release and agree on the results.
  2. Cross-implementation interop evidence is published — what was exchanged, what was compared, and what differed — not merely asserted.
  3. A published decoder corpus exists for anything the release makes a pixel-level claim about (errata E12), so that “conformant decoder” names a test rather than an intention.
  4. A conformance runner executes in CI against the release's own fixtures, positive and negative, on every commit that touches the standards tree.
  5. An external review period has opened and closed, with the comments and their dispositions published.
  6. A public governance process exists: who decides, how a change is proposed, how an objection is heard, and how a decision is recorded.

1.0.0 and 1.0.1 predate this bar and are not held to it retroactively. They were preservation acts: 1.0.0 froze bytes that were already in circulation so that a consumer who pinned them can keep resolving them, and 1.0.1 corrected the published set without changing a single record's shape. Applying a bar retroactively to a freeze is incoherent — the freeze exists precisely because the artifacts were already published — and unfreezing them to meet it would destroy the property the freeze provides. What 1.0.x owes instead is what it has: an errata annex, a compatibility matrix, and a ledger that says which of its claims did not survive contact with an audit.

1.1 is explicitly held to the bar. Rows 9, 11, 12, 14 and 17 of standards/ver/1.1-draft/RELEASE.md are the tracking rows for conditions 3, 4, 4, 1–2 and 5–6 of this list; each row carries its current state, and a row stays BLOCKING there until its condition holds (as of 31 August, rows 14 and 17 do; rows 9, 11 and 12 are discharged and say so). A ratified 1.1 without them would repeat the mistake this branch exists to answer: 1.0 was published as a standard with no reference corpus, no conformance runner, no second implementation and no governance, and the 189-finding audit is what that cost.