II.
Page overview
Reference · livepage:docs-specs-platform-gaps-phase1-pkg-02-versioning
WP-PKG-02 record: lockstep kept, changesets removed, wildcard internal deps eliminated overview
Inspect the raw attributes, linked wiki pages, and inbound or outbound graph edges for page:docs-specs-platform-gaps-phase1-pkg-02-versioning.
Attributes
nodeKind
Page
sourcePath
docs/specs/platform-gaps/phase1/pkg-02-versioning.md
sourceKind
repo-docs
title
WP-PKG-02 record: lockstep kept, changesets removed, wildcard internal deps eliminated
displayName
WP-PKG-02 record: lockstep kept, changesets removed, wildcard internal deps eliminated
slug
docs/specs/platform-gaps/phase1/pkg-02-versioning
articlePath
wiki/docs/specs/platform-gaps/phase1/pkg-02-versioning.md
article
# WP-PKG-02 record: lockstep kept, changesets removed, wildcard internal deps eliminated
Executed 2026-09-29 on branch `phase1/rename-train` of `a5c-ai/fabric` (base `3ed7ac0a3`, the WP-RN-06 head plus two CI-triage commits; draft PR #4 open). Spec: `docs/specs/platform-gaps/40-packaging.md` §2.2 (decision) and "WP-PKG-02" (objective, affected paths, acceptance criteria, test requirements); spec 15 §4.2 / §4.3 G2(d); intent `00-intent.md` D10 (break freely, major bump). Builds on `rn-05-rename.md` (every internal edge already renamed and exact-pinned 7.0.0, G2(d) unconditional) and `rn-06-bins.md`.
The public repository `a5c-ai/babysitter` was never written to: `gh api repos/a5c-ai/babysitter --jq .pushed_at` → `2026-09-16T19:59:50Z` at start and end (only GET calls were issued); `git remote -v` → `upstream no_push (push)`. Nothing was published to any registry, no tag was created (tag count 3, unchanged), no PR was opened.
## 1. What was removed
| Path | Content | Why it was live |
|---|---|---|
| `.changeset/config.json` | changesets config, `fixed` group for the five adapters packages | the spec's named file (§2.2, affected paths) |
| `.changeset/observer-dashboard-replacement.md` | one stranded pending changeset | the spec's named file |
| `.changeset/agent-mux.config.json`, `.changeset/README.agent-mux.md` | a second config + README carried in by the agent-mux merge, instructing `npx changeset` and describing a `release.yml` that does not exist here | same dead machinery; the WP deletes the directory, and the orchestrator gate is `! test -d .changeset` |
Live references rewritten (each told a contributor or an agent to add a changeset):
| Path | Before | After |
|---|---|---|
| `packages/adapters/meta/github/PULL_REQUEST_TEMPLATE.md` | "## Changeset — Ran `npm run changeset`" checkbox | "## Versioning — no version bump; lockstep train (`docs/VERSIONING.md`)" |
| `packages/adapters/meta/legacy-root/CONTRIBUTING.md` | "## Release flow (changesets)" with the preferred `npm run changeset` flow | "## Release flow (historical)" — superseded note pointing at `docs/VERSIONING.md`; the manual steps kept as history only |
| `packages/adapters/processes/integrate-harness.js` | task title "Docs + changeset", task text "and a changeset entry", instruction `npm run changeset — minor` | title "Docs", instruction "Do not bump any package version: packages ride the lockstep release train" |
| `packages/adapters/skills/integrate-harness/SKILL.md` step 11 | "**Changeset** — `npm run changeset`, pick `minor`" | "**Versioning** — nothing to do … do not bump any `version` field" |
| `packages/adapters/todos.md:54` | historical "[x]" note naming the changesets scripts | same note plus "(Historical: removed in WP-PKG-02 — docs/VERSIONING.md)" |
Proof — `git grep -n -i changeset -- . ':!package-lock.json'` after the change returns only: marketplace blueprint content that *describes the changesets tool to blueprint users* (`blueprints/a5c/marketplace/blueprints/{changesets,changelog-enforcer,autorelease}/*.md`, `.claude/skills/assimilate-popular-workflows/SKILL.md:258`), Atlas catalogue data (`packages/atlas/graph/**` — the `library:changesets` node, Ecto changesets, an extensions list), library reference material (`library/specializations/**/README.md`, `claudekit-code-review.js` `changeSet` variable), an archived plan (`docs/adapters/archive/...`), the specs that record the decision, and the three historical notes listed above. None is a mechanism, workflow, script or manifest of this repository. `.github/`, `scripts/` and the root `package.json` contain no occurrence at all (case-sensitive grep, the orchestrator gate). `package-lock.json` has 0 `@changesets` entries (it never did).
## 2. Wildcard / range internal edges
Scan (scratch script over all 223 tracked `package.json` outside `node_modules`/`dist`, internal = 43-entry publishable inventory ∪ every `@fabricstack/*`-named manifest, 68 names): **1** non-exact internal edge in the whole tree.
| Manifest | Edge | Fix |
|---|---|---|
| `library/specializations/meta/harnesses/pi/examples/package.json:11` | `"@fabricstack/orchestrator-sdk": "^0.0.180"` (the RN-05 name sweep renamed the package but left the pre-rename range) | pinned to `7.0.0` |
| `packages/atlas/webui/package.json:16` (the spec's example, `"@a5c-ai/atlas": "*"`) | already `"@fabricstack/catalog": "7.0.0"` — pinned by WP-RN-05's manifest pass | no change needed; it is now covered by the new rule and by the AC2 fixture |
The example manifest is not a workspace member, so `package-lock.json` is untouched (md5 `ab51e0832bee9404974780a1329a8898`, identical to the RN-05 value; `npm run guard:packages` "lockfile sync" green). No `workspace:*`, `latest` or `~` internal edge exists anywhere in the tree.
## 3. The new rule (`verify-manifests`)
`scripts/lib/release-version.cjs` `collectVersionDivergences` (single resolver, FIX-001) now:
- computes the **internal name set** = synchronized private-scope manifest names ∪ the publishable inventory (`listPublishablePackages`) ∪ every root-workspace member name (`resolveWorkspaceDirs`), derived at run time (FIX-005 discipline); the inventory/workspace sources are optional so the rule still works inside a fixture root or an extracted publication tarball, and can only grow the set;
- classifies every internal dependency specifier with `classifyUnpinnedSpecifier`: exact `X.Y.Z[-prerelease][+build]` → pin; otherwise `wildcard` (`*`, `x`, empty), `dist-tag "<name>"` (`latest`, `staging`, …), `workspace: protocol`, `caret range`, `tilde range`, `range` (comparators, `||`, hyphen, `7.x`, `7`), `non-registry specifier` (`file:`, `npm:`, `git`, `github:`, `owner/repo`);
- emits a divergence `{ rule: 'unpinned-internal-dependency', kind, manifestPath, line, field, subject, actual, expected }` for each, **before and instead of** the plain version mismatch, so the message names the actual problem;
- attaches a 1-based `line` (`manifestLineOf`, located in the raw manifest text inside the dependency field's object) to every manifest-level divergence, and the CLI prints each entry as `- <manifest>:<line> <field> <name>: <actual> (expected …)`.
External dependencies (`zod`, `@a5c-ai/compendium`, …) are untouched; the legacy-scope rule G2(d) still fires first and unchanged.
Fixture transcript (`"@fabricstack/catalog": "*"` plus a `^7.0.0` devDependency, real `scripts/` copied into a temp root):
```text
exit 1
2 manifest value(s) diverge from release version 7.0.0. Publishing from this workspace would publish or promote a different version (docs/release-incident-2026-08-13.md). Run `node scripts/sync-workspace-versions.mjs --version 7.0.0` first; 2 of them are internal dependencies that are not exact pins (wildcard, dist-tag, workspace: protocol or semver range) — the checked-in tree must pin every internal edge to the one train version (docs/VERSIONING.md):
- packages/consumer/package.json:8 dependencies @fabricstack/catalog: "*" (expected exact pin 7.0.0; wildcard is not allowed on an internal dependency (lockstep train, docs/VERSIONING.md, spec 40 §2.2))
- packages/consumer/package.json:12 devDependencies @fabricstack/harness-sdk: "^7.0.0" (expected exact pin 7.0.0; caret range is not allowed on an internal dependency (lockstep train, docs/VERSIONING.md, spec 40 §2.2))
```
Real tree: `node scripts/release-version.cjs verify-manifests --version 7.0.0` → `All synchronized manifests are at release version 7.0.0.` (exit 0).
### Fixture tests — `scripts/__tests__/verify-manifests.test.mjs`, `npm run test:verify-manifests` (8/8)
1. AC2 literal: `"@fabricstack/catalog": "*"` → exit 1, `packages/consumer/package.json:<line> dependencies @fabricstack/catalog: "*" (expected exact pin 7.0.0; wildcard is not allowed …)`; the external `zod`/`typescript` ranges in the same manifest are not reported.
2. `^7.0.0` → exit 1 with `caret range` at file:line.
3. Every shape (`*`, `latest`, `workspace:*`, `^`, `~`, `>=7.0.0 <8.0.0`, `7.x`, `||`, `file:`) trips exactly one `unpinned-internal-dependency` divergence with its own `kind` and the line read back from disk.
4. A private workspace member outside the private scope (`fixture-private-tool`) is internal by workspace-set membership: `"*"` on it trips; `zod` is not internal.
5. A wrong exact pin (`6.0.3`) is still the classic mismatch (no rule), and `sync-workspace-versions.mjs` repairs it to a passing tree.
6. Classifier unit cases (exact with channel prerelease/build → pin; `''`/`x` → wildcard; `7` → range; `staging` → dist-tag; `npm:` → non-registry; `undefined` → wildcard).
7. An exact-pinned lockstep fixture passes.
8. The real tree passes at the root version, and `collectVersionDivergences` over it is `[]`.
The file matches `scripts/__tests__/*.test.mjs`, so it also runs under `npm run test:release-tooling` in `ci.yml` (Release tooling tests) and `release-artifact-verifier.yml`. In addition, `ci.yml`'s dependency-free `publish-isolation` job gained the step "Lockstep manifests and exact internal pins (G2(d), WP-PKG-02)": `node scripts/release-version.cjs verify-manifests --version "$(node -p "require('./package.json').version")"` followed by `npm run test:verify-manifests` — the rule now bites on every PR against the checked-in tree, not only in the publication workspace (spec 15 §4.3 G2 "required check on every PR", item (d)).
## 4. `docs/VERSIONING.md`
New one-page posture document: §1 lockstep train (43 publishables + root + private-scope workspaces + plugin manifests at one version; internal = inventory ∪ workspace set; exact pins only; whole-train consumption), §2 the single resolver and its subcommands as the ONLY mechanism, §3 D10 at train level (majors mark breaking trains; the compatibility obligation is met in-repo) and **how 7.0.0 was chosen** (rename + bin retirement + exports maps are breaking for every consumer → major over the public 6.0.x lineage; next breaking train 8.0.0), §4 changesets removal (dead machinery, no workflow consumer, spec input 3 wire-or-remove) and the **rejected alternative** (per-package semver via changesets) with four reasons (no consumer resolves per-package signals; independent releases require floating internal ranges; a second version-computing mechanism re-opens FIX-001; D10 is simpler per train), §5 contributor rules. `docs/release-pipeline.md` links to it from its FIX-001 section. `markdownlint-cli2`, `docs-style-check.cjs` and `docs-link-check.cjs` all pass on the new/edited docs.
## 5. Gates (all run from `C:/work/fabric` on the final tree)
Orchestrator gate verbatim — `! test -d .changeset && ! grep -rqn "changeset" .github scripts package.json && npm run test:verify-manifests && node scripts/release-version.cjs verify-manifests --version 7.0.0 && echo PKG02-PASS` — tail:
```text
# tests 8
# pass 8
# fail 0
All synchronized manifests are at release version 7.0.0.
PKG02-PASS
```
| Check | Result |
|---|---|
| `npm run test:release-tooling` before | 149 tests, 112 pass, 37 fail (inherited FIX-010/FIX-011 workflow-shape + npm-stub-on-PATH set; names saved) |
| `npm run test:release-tooling` after | 157 tests, 119 pass, 38 fail = the 37 inherited names + 8 new WP-PKG-02 tests passing + **one flake**: `hooks-atlas-ownership.test.mjs` "a hooks leaf resolves Atlas through its OWN dependency in a non-hoisted clean consumer" failed only in the parallel full run — its packed verifier rebuilt `@fabricstack/catalog` while another test file was regenerating the same library JSON (`validate-library-bridge.mjs`: "Unterminated string in JSON at position 21532219"). Rerun of that file alone: the test passes (5/6; the 1 failure, "the hooks CLI installs, imports and runs from its packed tarball", is in the inherited list). Failing-name set after, minus that flake, equals the set before: 0 new failing names. No test deleted or skipped. |
| `npm run test:publish-guards` | 17/17 (incl. G2(d) over the real tree with the new rule active) |
| `node scripts/ci/publish-isolation-guard.cjs --self-test` | self-test passed; repository scan clean (101 files, 0 findings, 8 legacy-train notices) |
| `node scripts/ci/no-double-rename.cjs --self-test` | 10/10; repository scan 59 manifests, 0 findings |
| `node scripts/ci/public-push-guard.cjs` | scanned 313 files, 0 findings |
| `npm run guard:packages` | passed (lockfile sync, native bindings, metadata, arch boundaries) |
| `npm run verify:metadata` | passed; publishable inventory 43 |
| `node scripts/release-version.cjs resolve --branch main --sha 3ed7ac0a308f --print releaseVersion` | `7.0.0` |
| `node scripts/release-version.cjs resolve --branch staging --sha 3ed7ac0a308f` | `7.0.1-staging.3ed7ac0a308f` / dist-tag `staging` / tag `babysitter/staging/v7.0.1-staging.3ed7ac0a308f` — the FIX-001 staging shape (patch+1 prerelease of the root 7.0.0); the tag prefix is still `babysitter` (WP-RN-07 retargets it to `fabric/`) |
## 5a. Review round 1: the lockfile changed once, for a dependency-declaration fix
The md5 in §2 (`ab51e0832bee9404974780a1329a8898`) describes the tree up to the review. WP-RN-05 review finding M6 (console-tui's bin crashed on every install: `packages/genty/tui` declared `react ^18.3.1` while the workspace root resolves React 19, and `--legacy-peer-deps` hoisted ink 5's `react-reconciler@0.29` next to `react@19`) was fixed as a declaration change — `@fabricstack/console-tui`, `@fabricstack/console-tui-plugins` and `@fabricstack/harness-cli` now declare `ink ^6.0.0` (requires React 19) and `react ^19.2.4`, matching the root — plus `jsdom ^30.1.1` as a devDependency of `@fabricstack/console-ui` (its `SessionDetailScreen` test declares `@vitest-environment jsdom`; the package was never installed, so the "Realtime flow tests" CI step failed identically on staging — oracle run 36638300466). `package-lock.json` was regenerated with `npm install --legacy-peer-deps` from the HEAD lockfile (never deleted; the two stale nested `react@18.3.1` nodes npm leaves behind as `invalid` were pruned and the install re-run): **4 transitive version changes** (jsdom's `@csstools/css-*` ×3, `tough-cookie`), 77 added nodes (the ink 6 / react-reconciler 0.33 / jsdom 30 closures), 25 removed (the ink 5 closure and the nested React 18 copies); every internal pin is still `7.0.0` (`verify-manifests` green). An `npm dedupe` attempt that re-resolved 274 unrelated packages was discarded.
## 6. Not done / deferred, and honesty notes
- `sync-workspace-versions.mjs` still rewrites any internal range to the release version inside the publication workspace (that is its job). The new rule guards the **checked-in** tree: PR CI and the pre-publication `verify-manifests` both refuse a wildcard before the sync could paper over it.
- The `blueprints/**` changesets content is product material teaching the tool to blueprint users; it is not this repository's versioning and was intentionally left.
- `resolve --branch staging` prints the staging prerelease, not `7.0.0`; `--branch main` prints `7.0.0`. Both reported verbatim above.
- Environment: vitest/rolldown and lightningcss bindings are linux-only in this checkout (as in RN-05/RN-06), so no vitest suite ran locally; nothing in this WP touches a vitest-tested package source. `npm ci` was not run (per instruction).
- The pre-commit hook (`.githooks/pre-commit`, `validate-library-js-syntax.cjs --staged`) ran unchanged on the commit.
documents
[]
Outgoing edges
None.
Incoming edges
contains_page1
- page:docs·PageBabysitter Docs