Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,23 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.9.0] - 2026-08-14

First release of the XChain Platform release train. Every component in the train
now shares one platform version, so "XChain 0.9.0" names an exact, reproducible
set of software rather than a rough era.

### Changed
- Adopted the platform version stream. This component moves from `0.0.26` to
`0.9.0`. **The number is lower but the release is newer**: the platform stream
starts at 0.9.0 for the testnet series, and 1.0.0 is reserved for mainnet.

<!-- ------------------------------------------------------------------------
Versions BELOW this line are this component's own legacy stream, from
before the release train. They are kept for history and are NOT comparable
to the platform versions above: a higher legacy number is an older release.
------------------------------------------------------------------------ -->

## [0.0.26] - 2026-08-13

### Fixed
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "xchain-node",
"version": "0.0.26",
"version": "0.9.0",
"description": "xchain-node allows users to install, configure and run XChain platform nodes.",
"license": "AGPL-3.0-or-later",
"repository": {
Expand Down
60 changes: 39 additions & 21 deletions src/release-manifest.json
Original file line number Diff line number Diff line change
@@ -1,25 +1,43 @@
{
"_comment": [
"Release manifest: the pinned component set for one platform release train.",
"Written at release-ceremony step 6 (claude/specs/living/release-management.md",
"section 6) AFTER every sibling repo has merged and tagged, because the pins",
"record each tag's master merge-commit SHA and those commits do not exist",
"until the merges do.",
"",
"xchain-node is the only dense-tagged train member: checking out",
"xchain-node vX.Y.Z self-contains the full pinned set, so `install vX.Y.Z`",
"reproduces an exact release and `install` with no ref resolves the latest",
"xchain-node release and installs that release's manifest.",
"",
"Sparse lockstep: a component appears here at EVERY train, pinned to its",
"highest tag <= this train's version. A component that did not change this",
"train keeps its previous tag; that is the resolution rule, not a gap.",
"",
"`components` is EMPTY until the first train (0.9.0) is cut. While it is",
"empty every install resolves by branch exactly as it did before this file",
"existed, so nothing here changes behaviour pre-adoption."
"Pinned component set for XChain Platform v0.9.0.",
"Written at ceremony step 6 from the ACTUAL tagged master merge commits.",
"xchain-node is the carrier and is not listed: checking out its tag IS this manifest."
],
"platform_version": null,
"released": null,
"components": {}
"platform_version": "0.9.0",
"released": "2026-08-14",
"components": {
"xchain-vm": {
"tag": "v0.9.0",
"commit": "d29fba335d98cd156c5ee927a89eccdda1aaaf01"
},
"xchain-decoder": {
"tag": "v0.9.0",
"commit": "622ce5ab616f605f990692771a6682cb730fb2f4"
},
"xchain-indexer": {
"tag": "v0.9.0",
"commit": "ea53a7f99255fa20f8bacb2aee5cbc9f0b3ad1dd"
},
"xchain-hub": {
"tag": "v0.9.0",
"commit": "3a9e1b5fd3ba115f12826ea88a0868a0d10f5b5a"
},
"xchain-sync": {
"tag": "v0.9.0",
"commit": "dc75649f3469a8fa32c4a1ea7749d05da4a319f7"
},
"xchain-encoder": {
"tag": "v0.9.0",
"commit": "ea785f6da4281ebc45912f7dea1e9e062a4e8f44"
},
"xchain-utxo-tracker": {
"tag": "v0.9.0",
"commit": "21fdfd80660bd86d09d91402e23b873f0cee8574"
},
"xchain-explorer": {
"tag": "v0.9.0",
"commit": "ffc73bf4d33716e9932a87d2405b5d42e69092ba"
}
}
}
46 changes: 37 additions & 9 deletions test/unit/ReleaseManifestService.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -275,15 +275,43 @@ describe('ReleaseManifestService', () => {
})

describe('the shipped manifest', () => {
it('is valid JSON with an empty component set until the first train', () => {
// Guards the pre-adoption promise: while `components` is empty every
// install resolves by branch exactly as it did before these rails existed.
const shipped = require('../../src/release-manifest.json')
const real = proxyquire('../../src/services/ReleaseManifestService', {
'../GitHubDownloader': { githubApiHeaders: () => ({}), githubRateLimitError: () => null }
})
expect(real.manifestHasPins(shipped)).to.equal(false)
expect(real.getComponentPin(shipped, 'xchain-vm')).to.equal(null)
// Was "components is empty until the first train". That was the correct
// invariant right up to the moment 0.9.0 was cut, and it is now the wrong
// one: the shipped manifest carries real pins. What has to hold from here
// on is that every pin is USABLE, because a malformed one is the failure
// this whole file exists to prevent.
const shipped = require('../../src/release-manifest.json')
const real = proxyquire('../../src/services/ReleaseManifestService', {
'../GitHubDownloader': { githubApiHeaders: () => ({}), githubRateLimitError: () => null }
})

it('declares a platform version and a release date', () => {
expect(shipped.platform_version).to.match(/^\d+\.\d+\.\d+$/)
expect(shipped.released).to.match(/^\d{4}-\d{2}-\d{2}$/)
})

it('pins every component to a tag and a full 40-hex commit', () => {
expect(real.manifestHasPins(shipped)).to.equal(true)
for (const name of Object.keys(shipped.components)) {
const pin = real.getComponentPin(shipped, name)
expect(pin, `${name} must resolve`).to.not.equal(null)
expect(pin.commit, `${name} commit`).to.match(/^[0-9a-f]{40}$/)
expect(pin.tag, `${name} tag`).to.match(/^v\d+\.\d+\.\d+$/)
}
})

it('pins every component at the platform version it ships in', () => {
// A component pinned at some other tag would mean the manifest and the
// release number disagree about what "XChain 0.9.0" contains.
for (const [name, pin] of Object.entries(shipped.components)) {
expect(pin.tag, `${name} tag matches the train`).to.equal('v' + shipped.platform_version)
}
})

it('does not pin xchain-node itself', () => {
// node is the carrier: checking out its tag IS this manifest, so listing
// it would be a self-reference that can never be written before the fact.
expect(shipped.components['xchain-node']).to.equal(undefined)
})
})
})
Loading