1
0
Fork 0
10 SPEC
Caleb Brown edited this page 2026-04-09 11:21:57 -04:00

Epoch Semantic Versioning 1.0.0

Abstract

Epoch Semantic Versioning ("Epoch SemVer") is a versioning scheme that extends Semantic Versioning by introducing a fourth logical version component named epoch. Inspired by Anthony Fu's Epoch Semantic Versioning proposal.

Epoch SemVer preserves compatibility with existing SemVer-aware tooling by encoding the logical tuple (epoch, major, minor, patch) into a standard three-part Semantic Version string using a fixed multiplier.

This specification defines the logical version model, encoding and decoding rules, precedence, canonical metadata format, validation requirements, monorepo policies, and pre-release handling.

1. Introduction

Semantic Versioning provides a widely adopted model for expressing compatibility and change magnitude. In some environments, teams also need a visible mechanism for representing rare, high-signal generational shifts that are more significant than ordinary major-version changes.

Epoch SemVer addresses this need by adding an epoch component to the logical version model while keeping the externally visible version string SemVer-compatible.

This specification is intended primarily for internal packages, services, applications, and release workflows.

2. Conformance and Terminology

The key words MUST, MUST NOT, REQUIRED, SHALL, SHALL NOT, SHOULD, SHOULD NOT, RECOMMENDED, MAY, and OPTIONAL in this document are to be interpreted as described in RFC 2119.

For the purposes of this specification:

  • A logical version is the tuple (epoch, major, minor, patch).
  • An encoded version is the SemVer string derived from a logical version, optionally with a SemVer pre-release suffix.
  • A versioned unit is a package, service, application, library, module, or other independently versioned artifact.
  • A release state is the metadata state associated with a versioned unit at a particular logical version.
  • A canonical version file is a file named epoch-version.json containing the canonical version state for a versioned unit.

3. Logical Version Model

3.1 Components

Every Epoch SemVer logical version consists of exactly four components:

  • epoch
  • major
  • minor
  • patch

Each component MUST be an integer greater than or equal to 0.

3.2 Version Identity

The identity of an Epoch SemVer release is the logical tuple:

(epoch, major, minor, patch)

The following fields are not part of logical version identity:

  • preRelease
  • releasedAt
  • commitHash
  • codename

3.3 Intended Meaning

The intended meaning of the logical components is as follows:

Component Intended Meaning
patch Backwards-compatible fixes
minor Backwards-compatible additions
major Ordinary breaking changes
epoch Rare generational or landmark changes

These meanings are descriptive guidance for human consumers. Conforming tooling MUST follow the encoding, decoding, and precedence rules in this specification rather than attempting to infer semantics from release notes or implementation details.

4. Increment and Reset Rules

4.1 New Logical Versions

To create a new logical version, an implementation MUST increment exactly one logical component and MUST reset all less-significant logical components to 0.

4.2 Significance Order

The logical components are ordered from most significant to least significant as follows:

epoch > major > minor > patch

4.3 Reset Requirements

When incrementing a component, the following reset behavior MUST be applied:

Incremented Component Required Reset Behavior
patch No reset
minor patch = 0
major minor = 0, patch = 0
epoch major = 0, minor = 0, patch = 0

4.4 Uniqueness

A versioned unit MUST NOT publish two distinct releases with the same fully encoded version string.

A versioned unit MUST NOT publish two distinct releases with the same logical tuple and the same preRelease value.

4.5 Pre-release and Identity

A change to preRelease alone does not change the logical version identity.

5. Epoch Zero

When epoch = 0, Epoch SemVer behaves as ordinary Semantic Versioning with respect to the encoded version string:

encodedMajor = major
version      = major.minor.patch

If a pre-release identifier is present, the encoded version is:

version = major.minor.patch-preRelease

A project that never increments epoch therefore remains operationally equivalent to an ordinary SemVer project.

6. Encoding

6.1 Fixed Multiplier

Epoch SemVer MUST use a fixed multiplier of 1000.

The encoded major version MUST be computed as:

encodedMajor = epoch * 1000 + major

6.2 Major Range

The logical major component MUST satisfy:

0 <= major <= 999

If a versioned unit needs to exceed 999 major releases within a single epoch, it MUST increment epoch.

6.3 Encoded Version Form

The encoded version MUST be emitted as a valid Semantic Version string in one of the following forms:

<encodedMajor>.<minor>.<patch>
<encodedMajor>.<minor>.<patch>-<preRelease>

Build metadata MAY be appended using ordinary SemVer build metadata syntax, but such metadata is outside the scope of this specification and MUST NOT be stored as an Epoch SemVer logical component.

7. Decoding

Given an encoded version string of the form:

encodedMajor.minor.patch

or

encodedMajor.minor.patch-preRelease

the logical components MUST be recovered as follows:

  • epoch = floor(encodedMajor / 1000)
  • major = encodedMajor mod 1000
  • minor = minor
  • patch = patch

If present, preRelease is the SemVer pre-release suffix.

Example:

1003.2.0          => epoch=1, major=3, minor=2, patch=0
1003.2.0-beta.1   => epoch=1, major=3, minor=2, patch=0, preRelease=beta.1

8. Ordering and Precedence

8.1 Logical Ordering

Logical versions MUST be ordered lexicographically by:

(epoch, major, minor, patch)

8.2 Encoded Precedence

Encoded versions MUST be compared using ordinary Semantic Versioning precedence rules.

8.3 Monotonicity

If logical version A is greater than logical version B under the ordering defined in Section 8.1, then the encoded version of A MUST have greater SemVer precedence than the encoded version of B.

8.4 Pre-release Ordering

When two encoded versions share the same logical tuple, any precedence relationship involving preRelease MUST be determined exclusively by standard SemVer pre-release rules.

9. Canonical Version File

9.1 General Requirement

Each versioned unit MUST have exactly one applicable canonical version file named epoch-version.json.

This file MUST be the canonical source of truth for the logical version and release metadata of that versioned unit.

Derived version fields in other files, such as package.json versions, SHOULD be generated from the applicable canonical version file rather than maintained independently.

9.2 Single-unit Repositories

In a repository containing exactly one versioned unit, the canonical version file SHOULD be located at the repository root.

9.3 Multi-unit Repositories

A repository MAY contain multiple versioned units.

A multi-unit repository MUST adopt exactly one of the following policies for each versioned unit:

  1. Independent-version policy
    Each versioned unit has its own canonical version file, typically located at the root of that unit's directory.

  2. Shared-version policy
    A single canonical version file governs multiple versioned units.

A repository MAY apply the shared-version policy to one group of versioned units and the independent-version policy to another group of versioned units, provided that each versioned unit has exactly one applicable canonical version file.

9.4 Applicability

Tooling MUST derive the encoded version of a versioned unit from the canonical version file applicable to that unit under the repository's chosen policy.

10. Canonical File Format

10.1 Required Fields

A canonical version file MUST contain exactly the following top-level fields:

{
  "epoch": 0,
  "major": 0,
  "minor": 0,
  "patch": 0,
  "preRelease": null,
  "releasedAt": null,
  "commitHash": null,
  "codename": null
}

Additional fields MAY be allowed by implementation-specific tooling, but such fields are outside the scope of this specification and MUST NOT affect conformance.

10.2 Field Definitions

Field Type Requirements
epoch integer non-negative
major integer integer in the range 0..999
minor integer non-negative
patch integer non-negative
preRelease string or null valid SemVer pre-release identifier string, or null
releasedAt integer or null UTC Unix epoch timestamp in milliseconds, or null
commitHash string or null short Git SHA, or null
codename string or null non-empty, non-whitespace-only string, or null

11. Release State Semantics

11.1 Unreleased State

In an unreleased working state:

  • releasedAt MUST be null
  • commitHash MUST be null

11.2 Released State

In a released state:

  • releasedAt MUST be a UTC Unix epoch timestamp in milliseconds
  • commitHash MUST be a non-empty short Git SHA

releasedAt and commitHash MUST transition together from null to non-null.

11.3 Release Metadata Scope

releasedAt and commitHash are release metadata only. They are not part of logical version identity and MUST NOT affect version precedence.

12. Codename

12.1 Purpose

A codename identifies the current epoch using a human-friendly label.

Examples include "obsidian" and "aurora".

12.2 Codename Rules

  • If epoch > 0, codename MUST be present and MUST be a non-empty, non-whitespace-only string.
  • If epoch = 0, codename MAY be null.
  • A codename MUST remain unchanged across patch, minor, and major increments within the same epoch.
  • A codename MAY change only when epoch changes.

12.3 Codename Scope in Multi-unit Repositories

Under the independent-version policy, codename values apply per versioned unit.

Under the shared-version policy, a codename value applies to all versioned units governed by the shared canonical version file.

12.4 Codename Semantics

The codename is metadata only. It is NOT part of version identity, encoded version formation, or version precedence.

13. Derived Encoded Version

The encoded version of a versioned unit MUST be derived from epoch-version.json as follows:

encodedMajor = epoch * 1000 + major

If preRelease is null:

version = encodedMajor.minor.patch

If preRelease is not null:

version = encodedMajor.minor.patch-preRelease

Example canonical file:

{
  "epoch": 1,
  "major": 3,
  "minor": 2,
  "patch": 0,
  "preRelease": "beta.1",
  "releasedAt": null,
  "commitHash": null,
  "codename": "obsidian"
}

Derived encoded version:

1003.2.0-beta.1

14. Pre-release Identifiers

14.1 General

This specification adopts standard SemVer pre-release syntax without modification.

14.2 Canonical Representation

If present, the pre-release identifier MUST be stored in the preRelease field of the canonical version file.

14.3 Constraints

The preRelease field:

  1. MUST be either null or a valid SemVer pre-release identifier string.
  2. MUST NOT be treated as a fifth logical Epoch SemVer component.
  3. MUST affect encoded version precedence exactly as defined by Semantic Versioning.

15. Validation Requirements

An implementation conforms to this specification only if all of the following are true for every applicable canonical version file and derived encoded version:

  1. epoch, major, minor, and patch are integers greater than or equal to 0.
  2. major is less than or equal to 999.
  3. preRelease is either null or a valid SemVer pre-release identifier string.
  4. releasedAt is either null or an integer representing UTC Unix epoch milliseconds.
  5. commitHash is either null or a non-empty string.
  6. codename is either null or a non-empty, non-whitespace-only string.
  7. If epoch > 0, codename is non-null.
  8. If the state is unreleased, both releasedAt and commitHash are null.
  9. If the state is released, both releasedAt and commitHash are non-null.
  10. The encoded version is exactly (epoch * 1000 + major).minor.patch with an optional -preRelease suffix.
  11. The versioned unit has exactly one applicable canonical version file.
  12. The versioned unit's encoded version is derived from its applicable canonical version file.
  13. Any increment that creates a new logical version obeys the reset rules in Section 4.
  14. No two published releases for the same versioned unit share the same encoded version string.

16. Examples

16.1 Standard SemVer-compatible Version

{
  "epoch": 0,
  "major": 2,
  "minor": 4,
  "patch": 1,
  "preRelease": null,
  "releasedAt": 1743430920000,
  "commitHash": "e1a72f0",
  "codename": null
}

Derived encoded version:

2.4.1

16.2 First Epoch Release

{
  "epoch": 1,
  "major": 0,
  "minor": 0,
  "patch": 0,
  "preRelease": null,
  "releasedAt": 1743430920000,
  "commitHash": "a3f9c12",
  "codename": "obsidian"
}

Derived encoded version:

1000.0.0

16.3 Pre-release Example

{
  "epoch": 1,
  "major": 3,
  "minor": 2,
  "patch": 0,
  "preRelease": "beta.1",
  "releasedAt": null,
  "commitHash": null,
  "codename": "obsidian"
}

Derived encoded version:

1003.2.0-beta.1

16.4 Logical and Encoded Progression

Logical progression:

(0,1,0,0) -> (0,1,1,0) -> (0,2,0,0) -> (1,0,0,0) -> (1,1,0,0)

Encoded progression:

1.0.0 -> 1.1.0 -> 2.0.0 -> 1000.0.0 -> 1001.0.0

16.5 Independent-version Multi-unit Repository

packages/a/epoch-version.json
packages/b/epoch-version.json
services/api/epoch-version.json

Each versioned unit derives its encoded version from its own canonical version file.

16.6 Shared-version Multi-unit Repository

epoch-version.json
packages/a/package.json
packages/b/package.json
services/api/package.json

Each governed versioned unit derives its encoded version from the shared canonical version file.

17. Security and Operational Considerations

Epoch SemVer does not alter the security properties of Semantic Versioning itself.

However, implementations should consider the following operational risks:

  • Manual editing of derived version fields may cause divergence from the canonical version file.
  • In multi-unit repositories, ambiguous or undocumented version policy may cause tooling to derive the wrong encoded version.
  • Inconsistent interpretation of commitHash may reduce traceability across releases.
  • Unsynchronized release workflows may result in duplicate or conflicting encoded versions.

Implementations SHOULD validate canonical version files in CI and SHOULD generate derived version fields automatically.

18. IANA Considerations

This document has no IANA actions.

19. Non-normative Summary

Epoch SemVer is SemVer-compatible on the outside and four-part logical versioning on the inside.

Its defining rule is:

encodedMajor = epoch * 1000 + major

This preserves ordinary tooling compatibility while providing a structured way to represent rare, high-signal generational shifts.