Skip to content
VERASPEC
Repository
Contributingstable

Standards-track rules

These five are not style preferences. Each one exists because breaking it destroys a property the project is built on.

1. Frozen release directories are immutable

standards/ver/1.0.0/ and standards/ver/1.0.1/ are published, frozen artifact sets. Never modify, replace or delete anything under them — not to fix a typo, not to correct a digest, not to fix a defect you have proven is real. 1.0.0 is frozen with its defects, catalogued in `docs/standards/VER-1.0-errata.md`.

spec/ and lexicons/ are the published historical locations of the 1.0.0 artifacts and stay exactly where they are: the freeze copies, it never moves, because running code and published prose reference those paths.

A correction ships as a new release directory, under the publication checklist in `VERSIONING.md` §3.3 — the commit, the annotated ver/<release> tag, the recorded digests, the example validating against its own schema. From 1.1 onward a release must also clear the six-condition publication bar in §3.4. Publishing is a checklist, not a habit.

Corollary: a normative change is only ever made in a new release directory or in the current draft (standards/ver/1.1-draft/, unratified — do not implement against it for production). Editing prose in place to mean something new is the one thing this project cannot do.

2. The error-code registry is append-only

VERxxx conformance codes are a stable public registry (ADR-0003). Never renumber a code. Never reuse a retired number. Never change what an existing code means. A tool, a fixture or a downstream policy file may be pinning any of them.

Adding a code is fine: take the next free number in the right stage band, add it to the registry, add its fixture (rule 3), and update the counts in packages/ver-validator/README.md and conformance/README.md. Retiring one means marking it retired, not freeing the number.

Changing a code's severity is a compatibility event, not an edit: it changes the standards result for records already in circulation. It goes in `COMPATIBILITY.md` with the rest.

3. Conformance fixtures: one per code, real digests, manifest-first

The corpus in conformance/ is executable, not illustrative. Its full contract is `conformance/README.md`; the parts that get missed:

  • Exactly one fixture per active code, or an explicit coverage_exemptions entry saying why it cannot have one. A code with neither, or two fixtures claiming the same code, is a corpus defect and tests/test_conformance_fixtures.py will say so.
  • One seeded defect per fixture, in an otherwise complete and self-consistent record. A fixture wrong in two ways cannot tell you which rule fired.
  • expect_codes is an exact set, not a floor. Where a defect provably drags another code with it, list both and explain the coupling in notes.
  • Every digest, length and byte string is computed, never invented. No hand-typed hex. Where the true input is not carried by the record, the value is a real digest of a documented preimage (sha256("ver-conformance-corpus/1.0.1 <purpose>"); 1.1-draft fixtures use the ver-conformance-corpus/1.1-draft prefix), which anyone can recompute. Per annex E26 those stand in for measured values and MUST NOT be cited as interoperability evidence.
  • manifest.json is the contract. A fixture that is not in the manifest is not in the corpus. Name the file after its code, and re-run the corpus after any edit — editing a signed fixture invalidates its signature.

4. Decisions go in an ADR

Anything that constrains the standard's future — a field's meaning, a requirement level, a versioning rule, a validator's strictness — is decided in `docs/adr/` before it is implemented, as ADR-NNNN-kebab-slug.md with the existing sections: Status, Context, Options (with the ones rejected, and why), Decision, Consequences.

Status is Proposed, Accepted, Rejected or Superseded by ADR-NNNN, with the date and — where one exists — a pointer to where the decision is implemented. An acceptance may be qualified, and the qualification belongs in the status line rather than buried in the prose: the existing ADRs carry Accepted — conditionally (conditions enumerated and tracked), Accepted — principle adopted, construction revised, and similar. An accepted ADR is not edited into a different decision later; it is superseded by a new one.

Do not resolve an open ADR by implementation. Shipping code that assumes an answer is how a decision gets made without being decided.

5. The ledger records everything

Every finding — patched, deferred, or knowingly left alone — is a row in `docs/standards/VER-1.0-errata.md` with a disposition. documented — not patched is a legitimate outcome; an undocumented absence is not. Record-affecting changes also get a row in `COMPATIBILITY.md`, and shipped changes go in `CHANGELOG.md`.

Before changing anything under standards/, packages/ver-validator/ or conformance/, read the errata ledger and COMPATIBILITY.md first. Most surprising-looking behaviour there is deliberate and already has a row.