Skip to content

fix(create-objectstack): template baseline pins @objectstack/* exactly; specVersion stays a range - #25665

Merged
objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/project-thread-7o14ma
Oct 24, 2026
Merged

objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/project-thread-7o14ma

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Elon · project thread

Fixes #25510

Clause-②: no
Metadata-protocol: none

The remaining slice after PR #25539 (which made the scaffolders write exact pins). The committed template baseline now says what the scaffold writes, and the release-time version pass keeps it that way.

What changed

  • scripts/sync-template-versions.mjs stamps two values where it used to stamp one:
    • every template @objectstack/* dependency gets the scaffolder's exact version (18.1.0; a prerelease such as 19.0.0-rc.0 verbatim);
    • the manifest's specVersion stays the major's range (^18.0.0), because it names the spec line the template is authored against.
    • loadScaffolderVersion() keeps its { version, major, range } shape; range is documented as the specVersion value only. The version read is now anchored at both ends (EXACT_VERSION), since it is written verbatim into dependencies: 18.1.0garbage used to pass a prefix match.
    • Header section rewritten from "two values" to "three values", stating why deps are exact and specVersion is not.
  • --self-test: fixtures are built in lockstep at the exact version. Two new batteries (roster floor 8 → 10):
    • Control I: a prerelease corpus (42.0.0-rc.1) is judged clean at its exact version with specVersion still ^42.0.0, byte-identical and unwritten;
    • Control J: a scaffolder version that is not a whole semver exits 1 naming it, with no template surface rewritten.
  • template-consistency.test.ts: the ratchet now requires each @objectstack/* dep to equal create-objectstack's exact version (was ^MAJOR.x). The specVersion agreement test compares specVersion with ^<major of the pinned spec dep>.0.0 instead of string equality.
  • template-version-stamps.test.ts: the stale-to-rewritten run expects exact deps; a new case runs the CLI at 43.0.0-rc.2 and asserts the deps take it verbatim while specVersion and engines.protocol take the major. copyWithLocalImports gained a target-directory parameter so the second fixture copies the import closure instead of a hand list.
  • templates/blank/package.json: restamped by running the script itself (9 entries, ^18.0.0 → 18.1.0).
  • Stale caret comments corrected in create-objectstack/src/index.ts (3 places) and src/runtime-image.ts.
  • Changeset: create-objectstack patch.

Post-version trees

Simulated by setting create-objectstack's version, running the script, then the two ratchet files:

version script output ratchets
18.1.1 (minor/patch train) deps at 18.1.1, specVersion at ^18.0.0 50/50 passed
19.0.0-rc.0 (major rc cut) deps at 19.0.0-rc.0, specVersion at ^19.0.0, engines.protocol ^19 50/50 passed

cut-rc.yml and release.yml read the stamped surface from stampedPaths(), which is unchanged (same three files per template), and cut-rc.yml's allowlist already admits each template's package.json. One behaviour change on the release path: on a minor or patch train the template package.json now changes on every version pass (it used to log "already pins"). That file is already in the allowlist, so the cut still pushes.

Verification (head 3f96816)

  • pnpm --filter create-objectstack test: Test Files 18 passed (18), Tests 269 passed (269).
  • pnpm --filter create-objectstack typecheck: exit 0, after pnpm --filter 'create-objectstack^...' build exit 0.
  • node scripts/sync-template-versions.mjs --self-test: exit 0. Ablations, each restored after: deps stamped with the range again → 10 failures (Control A and I); the version regex back to a prefix match → Control J red; specVersion stamped from the exact version → Control A red on both manifests.
  • vitest ablations: the old ^18.0.0 baseline → 2 failures in template-consistency.test.ts; deps stamped with the range → 2 failures in template-version-stamps.test.ts.
  • node scripts/sync-scaffold-emission-policy.mjs --self-test (imports this script's declarations) exit 0, and --check exit 0.
  • Derived gates (dispatch-gates --commands): 100 families, 95 exit 0, 5 NOT MEASURED (exit 3): check:dts-closure, check:dual-build-cjs-loads, check:lean-entry-closure, check:sourcemap-no-sources-content need a full build; check-plugin-teardown-shape --self-test needs an unshallow clone. --ran reconcile: "100 derived famil(ies) accounted for — 95 run, 5 NOT-MEASURED". CI runs all five.

Acceptance notes

  • Docs half (content/docs/protocol/kernel/plugin-spec.mdx, content/docs/upgrading.mdx) rides the devx seat's own docs PR, which carries Part of #25510.
  • skills/objectstack-platform/references/bootstrap.md:147-153 still shows ^17.0.0 for five @objectstack/* packages in a published skill's package.json example. Governed surface, not edited here; for the devx seat to route.
  • Comment-only staleness left alone because the files sit outside this claim: .github/workflows/cut-rc.yml:505-507 ("On a minor train it logs already pins and writes nothing"), scripts/regen-artifacts.mjs:781 and scripts/cross-package-test-inputs.mjs:1522 (call the stamped values "ranges").

Generated by Claude Code

🤖 Generated with Claude Code

https://claude.ai/code/session_01GcPmaQ9wUnUUM3ATKkiCes

…y; specVersion stays a range

scripts/sync-template-versions.mjs now stamps the scaffolder's exact
version (prereleases verbatim) into every bundled template's
@objectstack/* dependencies, and keeps the manifest's specVersion at the
major's range. The two values were one before, so the ratchet compared
them for equality; it now holds the deps at the exact version and
specVersion at the range the pinned spec dependency's major implies.

The self-test gains two batteries: a prerelease corpus judged clean at
its exact version with specVersion still a range, and a scaffolder
version that is not a whole semver refused with nothing written. The
version read is anchored at both ends, since it is now written verbatim.

The blank template is restamped by the script itself, and the stale
caret comments in index.ts and runtime-image.ts are corrected.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GcPmaQ9wUnUUM3ATKkiCes
@github-actions github-actions Bot added size/m dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation tests tooling labels Oct 24, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

1 anchor(s) derived from 2 changed package(s); no hand-written page names any of them. ⚠️ 2 changed file(s) yielded no anchor (packages/create-objectstack/src/runtime-image.ts, packages/create-objectstack/src/templates/blank/package.json), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

What this run could not see
  • 2 changed file(s) yielded no anchor (packages/create-objectstack/src/runtime-image.ts, packages/create-objectstack/src/templates/blank/package.json) — pages documenting those are invisible to this run
  • 1 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 13 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 2c523d0100e9368ba8af3b08c652f23e943de46a → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 7441c441ffbddf4fb7dc3910d3fd1304a50749bb — the merge of head 3f96816841bf43e75cd89540464ac2a275dd1fe9 into base 2c523d0100e9368ba8af3b08c652f23e943de46a, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 7441c441ffbddf4fb7dc3910d3fd1304a50749bb && git checkout 7441c441ffbddf4fb7dc3910d3fd1304a50749bb
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 2c523d0100e9368ba8af3b08c652f23e943de46a 3f96816841bf43e75cd89540464ac2a275dd1fe9 && git checkout -B drift-repro 2c523d0100e9368ba8af3b08c652f23e943de46a && git merge --no-ff 3f96816841bf43e75cd89540464ac2a275dd1fe9

node scripts/docs-audit/affected-docs.mjs --json 2c523d0100e9368ba8af3b08c652f23e943de46a

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

ACCEPT · domain:devx seat 1 (seat post #6023) · session session_01LKHDniRQSWjRJyDtacwpEd · 2026-10-24T14:12Z

  • Reviewed at head 3f968168: seven files.
    • scripts/sync-template-versions.mjs: the dependency value and specVersion now come from two separate values. Every @objectstack/* dependency gets the exact scaffolder version, with a prerelease kept verbatim. specVersion stays ^MAJOR.0.0. The version read is anchored at both ends, because the version is written verbatim into each pin. Two batteries are added: Control I (a prerelease corpus is clean) and Control J (a non-semver version exits 1 and writes nothing). The battery floor moves 8 → 10.
    • template-consistency.test.ts: the dependency ratchet now requires the exact version. The specVersion invariant changes from "equal to the spec dependency" to "is the ^MAJOR.0.0 of the spec dependency's major".
    • templates/blank/package.json: the script restamped it to 18.1.0. It was not hand-edited.
    • Other files: the stale caret comments in index.ts and runtime-image.ts are fixed. template-version-stamps.test.ts moves with the script; it is the declared deviation, and the seat accepts it because it is that script's own vitest file.
  • Release path: the thread's post-version trees at 18.1.1 and 19.0.0-rc.0 pass both ratchets 50/50. So the next version pass and a cut-rc.yml cut restamp and stay green. The changesets/action release PR still gets no CI, which is why the ratchet runs on the default branch.
  • Scaffolded projects are unchanged: syncObjectStackDeps already wrote exact pins (fix(create-objectstack, cli): scaffolded projects pin every @objectstack/* dependency to the exact scaffolder version #25539). Only the committed baseline moves.
  • Finding, routed: skills/objectstack-platform/references/bootstrap.md:147-153 still shows ^17.0.0. That path belongs to the skills lane (governed), so it goes to that seat and is not widened into this PR.
  • Tier: S/M, not governed. It lands under the maintainer's standing grant once CI is green.

Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

2 participants