ADR 0023 — release identity is the content digest; the epoch date is an atlas-owned label
- Status: accepted
- Date: 2026-06-14
- Relates: 0004 (the BOM this keys), 0006 (channels resolve a release id), 0008 (the release model), 0020 (the BOM is ground truth for tags)
- Motivating incidents: the horos
v0.1.0registration (#559) and the stele admission (in flight) — see below - Source: research-docs/0001 + its external-expert advice
One thing about what a release is was true but never written down, so adding a new component to a release demanded a hand-edit of every other component's date field. It is now stated, and the accidental check that enforced the unwritten assumption is removed:
- A release's IDENTITY is the full SHA-256 over its canonical coordinate set. That digest is the release; it is already the verified trust boundary (
verifyReleaseId).- The epoch date is an ATLAS-OWNED label, the human-readable prefix of the BOM key
<datePrefix>-<digest>— minted by atlas, never a fact about any published artifact.- Therefore a producer does not own the epoch. A component announced with its own publish date is normalized into the active epoch at assembly, not rejected.
Why this ADR exists
ADR 0004 makes the BOM the content-addressed record of a release; ADR 0008 is the release model. What neither wrote down is the relationship between a release's identity (the content digest) and the epoch date that labels it. Because that was left implicit, the importer grew a check that treated the date as a fact every component had to agree on — and that check turned "add one new component" into "re-stamp the date on all the others."
The horos
v0.1.0incident (#559). horos published with its own publish date,2026.06.13. The active release was2026.06.06. There was no way to make horos join in place: keep the header at2026.06.06andimportthrew “horos.json declares release "2026.06.13", expected "2026.06.06"”; advance the header to2026.06.13andimportthrew the same on the first existing, unchanged component. Registration required a governance PR that hand-normalized horos's date field to2026.06.06, leaving every coordinate exactly as emitted. It worked — but the manual edit was pure ceremony, andstele(the 8th component) hit the identical wall right after.What the incident proved. No check ever matches a published artifact against the epoch.
releaseFullDigesthashes only the canonical coordinate set;mintReleaseIdconcatenates the date as a prefix, never hashing it. The online audit probesgo.version/rs.ref/ts.version/source.commitagainst the live registries and never sends the epoch. The only reader that compared the manifest's epoch to anything (manifestAgreement'spublisher-releaserule) compared it solely torelease.json's header — atlas-owned metadata vs atlas-owned metadata. The date guards nothing about the artifacts. (An external expert engaged on the research-0001 question reached the same conclusion independently: “manifest.releaseis coordinator-owned placement metadata masquerading as a producer-owned artifact fact.”)
The check that was removed
Two sites enforced the unwritten "uniform date" assumption:
src/commands/substrate/import.mjsloadManifests— a hard throw whenm.release !== header.release. Removed. The assembled row never carriesrelease(toComponentRow), and the minted key's date prefix already comes solely from the header, so dropping the check changes no assembled bytes —importreproduces the committed BOM byte-for-byte.src/commands/substrate/verify-publisher.mjsmanifestAgreement— thepublisher-releaserule, a blockingerror. Downgraded to a non-blockinginfonote: a manifest whose epoch differs from the active header is expected (the producer stamped its publish date; atlas owns the active epoch), so it is surfaced for visibility but never reds the gate.
Decision
- The full SHA-256 over the canonical coordinate set is the release identity (already
true:
releaseFullDigest/verifyReleaseId; the 12-hex key suffix is a documented human label, never the trust boundary). - The epoch date prefix is an atlas-owned label, minted at assembly. Producers do not own it; it is never compared against any published artifact.
- Drop the uniform-date equality check in
importand downgrade thepublisher-releasere-assertion to a non-blocking note. - Migration is ignore-and-normalize, NOT forbid. The publisher-manifest schema keeps
releaseas an accepted, optional property (moved out ofrequired) so every existing producer manifest still validates and a producer may keep stamping its publish date harmlessly. Forbidding the field (the cleaner end state) is a producer-breaking schema migration reserved for a later, separately-greenlit event-sourcing change — explicitly not authorized here. - The cross-file mis-slot hygiene signal (a manifest physically in the wrong release
directory) is preserved as the downgraded
infonote, not silently removed.
Consequences
- + The horos/stele manual-edit class disappears. A newcomer's stale- or future-dated manifest validates and assembles with no hand-normalization; atlas stamps the active epoch.
- + It unblocks a one-command "add a component." With the epoch equality gone, a thin
atlas substrate assembleverb (a follow-up) can auto-stamp the epoch and reuse the existing re-cut machinery — "add one component" becomes a single command, not a governance hand-edit. - + No immutable BOM bytes change; fully reversible (re-add the throw / re-require the
field). Backward-compatible: every committed manifest still carrying
release: "2026.06.06"still validates and verifies green. - − The epoch is now decorative, which can mislead if read as a compatibility concept. It is a calendar label on an atlas assembly, nothing more; a semantic compatibility epoch (an ABI/universe number) is a separate idea, not this date.
- This ADR sets the identity model the follow-on work builds on (the
assembleverb, folding catalog identity into the digest, and the larger event-sourced-announcements decision). It does not authorize re-keying existing rows to the full 64-hex digest — that is a declined, history-rewriting change blocked by the immutability gate.