II.
Page overview
Reference · livepage:docs-versioning
Versioning overview
Inspect the raw attributes, linked wiki pages, and inbound or outbound graph edges for page:docs-versioning.
Attributes
nodeKind
Page
sourcePath
docs/VERSIONING.md
sourceKind
repo-docs
title
Versioning
displayName
Versioning
slug
docs/versioning
articlePath
wiki/docs/VERSIONING.md
article
# Versioning
**Posture (spec 40 §2.2, WP-PKG-02): semver encodes the release train; within a
train every package is exact-pinned lockstep; majors mark breaking trains.**
This page is the one place that states how versions are chosen in this
repository. There is exactly one mechanism — `scripts/release-version.cjs`, the
single resolver — and nothing else assigns, bumps or infers a version.
## 1. The lockstep train
- All 43 publishable `@fabricstack/*` packages (inventory derived at run time by
`scripts/lib/publishable-packages.cjs`, never hand-maintained) plus the root
manifest, the private workspace members in the private scope and the plugin
manifests (`plugins/*/plugin.json`, `versions.json`) carry **one** version:
the train version. Today that is `7.0.0`.
- Every **internal dependency** — any dependency whose name is in the
publishable inventory or the workspace set — is pinned to that exact version.
`*`, `latest`, `workspace:*`, `^`/`~` and comparator ranges are forbidden on
internal edges (external dependencies keep whatever ranges they need).
- A consumer therefore installs a whole train: `@fabricstack/orchestrator@7.0.0`
resolves to `@fabricstack/orchestrator-sdk@7.0.0`, which resolves to
`@fabricstack/harness-sdk@7.0.0`, and so on. Mixed trains cannot be assembled
by accident, which is the property that makes a private product with a
handful of whole-train consumers (the platform itself, the Form 1 archives,
enterprise installs via dist-tags) supportable.
## 2. The single resolver
`scripts/release-version.cjs` (library: `scripts/lib/release-version.cjs`,
tests: `scripts/__tests__/release-version.test.mjs`,
`scripts/__tests__/verify-manifests.test.mjs`) is the only place the released
version is decided (FIX-001, `docs/release-incident-2026-08-13.md`):
| Command | Role |
| --- | --- |
| `resolve --branch <b> --sha <short-sha>` | Produces THE release identity once: `main` releases the root version `X.Y.Z`; `staging` / `develop` release `X.Y.(Z+1)-<branch>.<sha>` on their own channels. Any other branch is a hard error. |
| `verify --branch --sha --version` | Validates an explicitly passed version against the resolver instead of re-reading manifests. |
| `sync-workspace-versions.mjs --version <v>` | Writes the resolved version into every synchronized manifest and every internal dependency pin (publication workspace only). |
| `verify-manifests --version <v>` | Proves the tree IS at `<v>`: every synchronized version, every internal pin, and — since WP-PKG-02 — that no internal dependency carries a wildcard, dist-tag, `workspace:` protocol or range. Failures are reported as `<manifest>:<line>`. Runs on every PR (`ci.yml`, publish-isolation job) and before every publication. |
| `assert-channel-tags`, `ensure-tag`, `tag-source`, `from-tag` | Channel promotion and tag provenance; read-only against the registry except `npm view`. |
The tree is committed **at** the train version. Nobody edits a `version` field
by hand and nobody adds a per-package bump: a train moves when the resolver says
so, and `verify-manifests` refuses a tree that disagrees with itself.
## 3. Breaking changes: D10 at train level
Intent decision D10 (`docs/specs/platform-gaps/00-intent.md`): *break freely,
major bump; no compatibility shims; in-repo consumers migrated in the same
effort.* Under the lockstep posture that rule is expressed **once per train**,
not per package:
- A train that contains any breaking change to any published surface bumps the
train **major**. Minor and patch trains carry only additive or corrective
changes across the whole set.
- Because every package shares the number, a consumer reads the major as "this
train broke something somewhere" and runs the migration for the train — there
is no per-package matrix to reconcile.
- The compatibility obligation is satisfied inside the repository: the same
change that breaks an API migrates every in-repo consumer, and the exact pins
guarantee no stale package can observe the old surface.
### How 7.0.0 was chosen
The public train (`a5c-ai/babysitter`, `@a5c-ai/*`) is at lockstep `6.0.x`. The
RN-1 cutover (spec 15) renames every package into the private `@fabricstack/*`
scope, retires duplicate bins, and (spec 40 §2.1) lands `exports` maps that make
deep imports of `dist/` unreachable. Each of those is a breaking change for
every consumer, so by the rule above the first private train is a **major**
bump over the last public one: `6.0.x → 7.0.0`. It is `7.0.0` rather than any
other number because the private train continues the public lineage (a
consumer migrating from `@a5c-ai/babysitter-sdk@6.0.x` to
`@fabricstack/orchestrator-sdk@7.0.0` sees the expected ordering) while making
it impossible to confuse the two trains. Subsequent breaking trains bump to
`8.0.0`, `9.0.0`, … ; non-breaking trains move the minor or patch.
## 4. Changesets: removed, and why
The repository once carried the changesets tool (`.changeset/config.json`, a
`fixed` group for the adapters packages, and one stranded changeset file).
Research for spec 40 (`docs/specs/platform-gaps/research/packaging-state.md`
§1.1) found it referenced by **no workflow**: contributors were occasionally
told to "add a changeset", but nothing ever consumed the files. Spec input 3
of that research required *wire-or-remove*. WP-PKG-02 removed it:
- `.changeset/` is deleted (config, the stranded changeset, and the
agent-mux-era config/README copies).
- No workflow, script or root/package manifest references the tool; the
contributor-facing instructions in `packages/adapters/**` (PR template, the
historical CONTRIBUTING, the `integrate-harness` process and skill) now say
the opposite: do not bump versions.
- The repository therefore carries **one** versioning mechanism, the resolver,
and one exact-pin rule enforced by `verify-manifests`.
### Alternative considered and rejected: per-package semver via changesets
Wiring changesets (each PR declares a per-package bump; a "Version Packages"
PR aggregates them; internal dependencies float on `^` ranges so packages can
release independently) was the other coherent half of wire-or-remove. It was
rejected because:
1. **Nobody resolves against per-package signals.** Consumers of a private
product take whole trains via dist-tags; 43 independently versioned
manifests would decorate the registry with information no consumer uses.
2. **Independent versions require floating internal ranges.** Per-package
releases only work if `@fabricstack/orchestrator-sdk` may depend on
`^7.0.0` of `@fabricstack/harness-sdk`; that reintroduces the mixed-train
installs and the "which version is actually running" ambiguity the
incident-hardened lockstep scheme exists to prevent (`docs/release-incident-2026-08-13.md`).
3. **Two mechanisms, one truth.** Changesets would compute versions in a
second place, exactly the "several places each infer the released version"
failure mode FIX-001 closed. The single resolver is never bypassed.
4. **D10 is simpler at train level.** One major per breaking train is a
clearer contract for a whole-train consumer than a per-package matrix of
majors that all ship together anyway.
The rejected alternative remains a valid design for a public, ecosystem-facing
package set; it is the wrong fit for a private platform consumed as a unit.
## 5. Rules for contributors
- Never edit a `version` field or an internal dependency pin by hand.
- When adding an internal dependency, pin it to the current train version
exactly (copy the root `package.json` `version`); `npm run test:verify-manifests`
and `node scripts/release-version.cjs verify-manifests --version <train>` must
stay green.
- A change that breaks a published surface is fine (D10) — note it in the PR so
the next train is cut as a major; migrate every in-repo consumer in the same PR.
- Release mechanics (tags `fabric/<branch>/v<version>`, Artifact Registry
publication, channel promotion) are described in `docs/release-pipeline.md`;
the operator procedures — normal train, dry-run rehearsal, half-published-train
recovery from the immutable tag, dist-tag repair, public-namespace checks — are
in [docs/RELEASE-RUNBOOK.md](RELEASE-RUNBOOK.md). Both consume the resolver's
output and never re-derive it.
documents
[]
Outgoing edges
None.
Incoming edges
contains_page1
- page:docs·PageBabysitter Docs