# LAVS-001 versioning policy

| | |
|---|---|
| **Status** | Adopted |
| **Version** | 1.0 |
| **Date** | 2026-05-27 |
| **Applies to** | All LAVS-001.X spec minor revisions |
| **License** | CC BY 4.0 |
| **Companion section** | `LAVS-001.md` §11 (Forward Compatibility) |

This document formalizes the versioning policy that lets independent implementers ship a LAVS-001 decoder confident it will not break on a future spec minor revision they have not yet read. It supersedes any informal guidance about versioning in the main spec and is the load-bearing document for the open-spec / wide-implementation strategy.

The format has two version numbers:

- **Spec version** (`specMajor.specMinor`) — what shape of file this is. v1.X minors are additive; v2.0 is breaking. Encoded in the header as two LEB128 unsigned integers per §3.2.
- **Runtime ABI version** — what visual output is expected. Independent of spec version; tracks observable rendering changes (default tone mapping, default rotation order, etc.). Encoded as a single LEB128 in the header per §3.3.

This document is about spec versioning. Runtime ABI is a separate axis covered briefly at the end.

---

## 1. What constitutes a v1.X minor bump

A v1.X bump is **additive and backward-compatible**. Specifically, a v1.X bump:

- **MAY** introduce new Core types in the reserved 20–99 range (e.g., `Environment` at type 19 was added in v1.2 from the previously-reserved 19–99 range).
- **MAY** introduce new property IDs in any namespace that was previously unused or unreserved. The conventional namespaces and their unused ranges are documented in [`PROPERTY-NAMESPACES.md`](./PROPERTY-NAMESPACES.md).
- **MAY** introduce new feature flags in `Profile.features` (e.g., `environment` was added in v1.2). Lenient consumers warn on unknown flags; strict consumers refuse. A v1.X file MUST set all required feature flags for the features it actually uses.
- **MAY** introduce new wire-type values in the 2-bit tag table (only if room exists — currently all four wire types are allocated, so this would require a new wire-type encoding mechanism, which would itself be a major bump).
- **MAY** add new optional properties to existing Core types. The new property must have a property ID drawn from the type's namespace block.

A v1.X bump **MUST NOT**:

- Remove any existing Core type ID.
- Repurpose any existing Core type ID.
- Remove any existing property ID.
- Repurpose any existing property ID (the type-to-property mapping is part of the binding contract).
- Change the wire type of any existing property.
- Change the header layout (field count, field order, field widths).
- Change the file structure regions (`header → dictionary → scene → edition → CRC`).
- Change the CRC32 polynomial or parameters.
- Change the LEB128 encoding semantics.

The shorthand: **anything you could read at v1.0 must still parse as v1.X**, possibly with unknown-but-skippable property IDs and unknown-but-skippable Core types.

---

## 2. What constitutes a v2.0 major bump

A v2.0 bump is **breaking** and may violate any of the prohibitions in §1. A v2.0 spec stands on its own; v1.X consumers MUST refuse v2.0 files. v2.0 producers MUST NOT emit files claiming `specMajor = 1`.

A v2.0 bump SHOULD be reserved for changes that genuinely cannot be expressed additively. Likely v2.0 changes (not committed, illustrative only):

- Replacing the property dictionary with a fixed-width wire-type registry.
- Changing the CRC scope (e.g., expanding to cover the full file rather than just the edition block).
- Replacing the LEB128 encoding with a faster fixed-width scheme.
- Replacing the YXZ rotation order with an axis-angle representation.

Project Lavos LLC commits to no v2.0 publication before 2027 at the earliest, and to a minimum 6-month transition window during which both v1.X and v2.0 are supported by the reference runtime. This commitment protects implementers who ship v1.X-only decoders.

---

## 3. Reader contract

A LAVS-001.X reader (for any specific minor X) **MUST**:

- Refuse to read files where `specMajor != 1`.
- Read files where `specMajor == 1 && specMinor <= X` cleanly.
- Read files where `specMajor == 1 && specMinor > X` **as far as possible**, treating unknown Core types and unknown property IDs as skippable via the property dictionary's wire-type tag table (§5.2). The reader MUST NOT panic, abort, or crash on unknown IDs from a higher minor.
- Issue a warning when refusing or skipping unknown feature flags. A Strict-conformant reader (§13) MAY refuse files with unknown required flags; a Lenient-conformant reader (§13) skips them.

A reader **SHOULD**:

- Surface the spec minor it implements (e.g., via a User-Agent-style identifier or a programmatic API). Buyers/verifiers benefit from knowing what minor a viewer can handle.
- Surface which features are recognized vs unknown when a file declares unknown feature flags.

The reader contract is the load-bearing guarantee. If an implementer ships a v1.1-only decoder today, it will keep working on v1.2 files (with Environment objects silently skipped — losing IBL contribution but rendering the scene), on v1.3 files (with HDR Environment data silently skipped), and so on. The format's spec minors are forward-compatible by design.

---

## 4. Encoder contract

A LAVS-001.X encoder **MUST**:

- Declare `specMajor = 1` and `specMinor = X` in the header, where X is the highest minor whose features are actually used. An encoder emitting a v1.2 Environment object MUST declare `specMinor = 2`. An encoder emitting only v1.0 features MUST declare `specMinor = 0`.
- Include in `Profile.features` every flag corresponding to a feature the file actually uses. Producers omitting a required flag produce a malformed file.
- Use property IDs and Core type IDs only from the spec at the declared minor or earlier. An encoder declaring `specMinor = 1` MUST NOT emit Type 19 (Environment, introduced in 1.2).

An encoder **SHOULD**:

- Not declare a higher minor than necessary. A v1.0-only scene declared as v1.2 is technically valid (a v1.2 reader handles all v1.0 files) but unnecessarily narrows the reader pool.
- Document its supported minor in its output (e.g., in `Edition.provenance` or via build metadata).

---

## 5. Runtime ABI versioning

Runtime ABI version (§3.3) is independent of spec version. It tracks **observable behavioral changes** that affect rendering output for files that were structurally identical between ABI versions.

Examples that would bump ABI:
- A change in default tone-mapping operator.
- A change in default rotation order interpretation.
- A change in default interpolation curves for animations.

Bumping ABI does not require bumping spec major or minor. A v1.0 spec, ABI 2 runtime will render v1.0 ABI 1 files with the v2-era defaults — which may produce visually different output than the original ABI 1 renderer would have.

Producers MUST stamp the ABI version they were authored against. Consumers SHOULD render files at ABI ≤ their own implementation's ABI faithfully; they MAY refuse files with higher ABI versions, or render them with a warning.

ABI versions and spec versions are independent axes. v1.2 ABI 1 is a valid combination. v1.0 ABI 3 is a valid combination. The reference runtime currently implements v1.2 ABI 1.

---

## 6. Implementer checklist

When implementing a LAVS-001 decoder for the first time:

1. **Decide your minor target.** v1.0 is the minimum viable target — it gets you a usable decoder. v1.2 is the current latest minor and gets you Environment IBL support.
2. **Implement reader-contract behavior**: refuse `specMajor != 1`; tolerate `specMinor > X` by skipping unknown IDs.
3. **Run the conformance test suite** against your decoder's output on the canonical reference editions. Suite source at [`scripts/conformance.ts`](../scripts/conformance.ts); documentation at [`https://lavos-pubkey.projectlavos.com/conformance.md`](https://lavos-pubkey.projectlavos.com/conformance.md). If you disagree with the suite on any check for any file, that's a divergence to report — file an issue.
4. **Cross-link your implementation** to this versioning policy in your README so users know what minor you implement and what the forward-compat guarantees are.

When considering whether a change is a v1.X bump or a v2.0 bump:

- Can a v1.0 reader still parse the file with this change in place? If yes, it's a v1.X bump.
- Does the change break any property-ID-to-wire-type binding? If yes, it's v2.0.
- Does the change move bytes around in the header? If yes, it's v2.0.

When in doubt, treat the change as v2.0 and submit a proposal for deliberation rather than ship it as v1.X.

---

## 7. Open questions deferred

These are real questions in the versioning space that this policy does NOT resolve, because resolving them deserves deliberation:

- **Should the dictionary support implementer-defined wire types?** Currently the 2-bit tag space is fully allocated (varint, string, float, color). New wire types would require a 3-bit table, which is a major bump.
- **Should ABI versions be tied to spec minor versions?** Currently they're independent; tying them would simplify the matrix but lose flexibility.
- **Should there be a "deprecation" lifecycle for properties before removal in v2.0?** Currently deprecation is not a defined state — properties are present in v1.X or absent in v2.0.

These are noted for future spec deliberation, not policy commitments.

---

*This document is binding policy for LAVS-001.X spec versioning. Updates to this document are themselves additive — clarifications add detail without contradicting prior versions of this policy.*
