From d8a9a54f31247d80d004c1f55a3e9d35c8829c72 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 16 Oct 2026 07:07:20 +0000 Subject: [PATCH 1/2] fix(cli): os init / os create emit overrides.vitest so the scaffold installs on npm 10 vitest 4.1.12 peers @vitest/browser-webdriverio ^4.1.11, whose only release peers vitest 4.1.11 exactly. npm 10 reaches that contradiction through optional vitest peers in the @objectstack/cli closure and crashes (edgesOut of null) on every os init template, app and empty included. Every standalone os init / os create manifest now emits overrides.vitest from SCAFFOLD_VITEST_RANGE (SCAFFOLD_NPM_OVERRIDES), and the stamp script gains an overrides.vitest POLICY_STAMPS row so the blank template's override is generated rather than hand-kept. Claude-Session: https://claude.ai/code/session_018t6a5rvqPRvEDMCVVW9kYT Co-authored-by: Claude --- .changeset/23805-scaffold-vitest-override.md | 19 +++ packages/cli/src/commands/create.ts | 8 ++ packages/cli/src/commands/init.ts | 63 +++++++++- .../cli/test/scaffold-npm-overrides.test.ts | 110 ++++++++++++++++++ .../src/template-consistency.test.ts | 6 +- scripts/sync-scaffold-emission-policy.mjs | 88 ++++++++++++-- 6 files changed, 282 insertions(+), 12 deletions(-) create mode 100644 .changeset/23805-scaffold-vitest-override.md create mode 100644 packages/cli/test/scaffold-npm-overrides.test.ts diff --git a/.changeset/23805-scaffold-vitest-override.md b/.changeset/23805-scaffold-vitest-override.md new file mode 100644 index 00000000000..2b57b03185a --- /dev/null +++ b/.changeset/23805-scaffold-vitest-override.md @@ -0,0 +1,19 @@ +--- +'@objectstack/cli': patch +--- + +Clause-②: no + +`os init` (every template: `app`, `plugin`, `empty`) and `os create plugin` now emit +`"overrides": { "vitest": "~4.0.0" }` in the scaffolded `package.json`, so the project installs on +npm 10, the npm Node 22 bundles. Since vitest 4.1.12 shipped a peer set that contradicts itself, +`npm install` of a fresh `os init` project crashed on npm 10.9.x with +`Cannot read properties of null (reading 'edgesOut')`. The override routes every `vitest` edge in +the npm graph to the range the scaffold declares; npm 11 installs the same tree, and pnpm ignores +the field. + +The override equals `devDependencies.vitest` wherever the manifest declares vitest directly, +because npm refuses an override that differs from the direct dependency (`EOVERRIDE`). In an `app` +or `empty` project, add vitest at the same range (`npm install -D vitest@~4.0.0`); a bare +`npm install -D vitest` is refused with `EOVERRIDE` while the override is in place. Existing +projects can add the same `overrides` block by hand. `os create plugin --in-repo` is unchanged. diff --git a/packages/cli/src/commands/create.ts b/packages/cli/src/commands/create.ts index d2c94f6a66f..097cae42e44 100644 --- a/packages/cli/src/commands/create.ts +++ b/packages/cli/src/commands/create.ts @@ -134,6 +134,7 @@ import { NPM_PACKAGE_NAME_MAX_LENGTH, renderPnpmWorkspaceYaml, renderScaffoldTsconfig, + SCAFFOLD_NPM_OVERRIDES, SCAFFOLD_PNPM_RANGE, SCAFFOLD_TSCONFIG_INCLUDE_SRC_ONLY, SCAFFOLD_TYPES_NODE_RANGE, @@ -408,6 +409,13 @@ export const templates: Record = { typescript: SCAFFOLD_TYPESCRIPT_RANGE, vitest: SCAFFOLD_VITEST_RANGE, }, + // The same npm override `os init` emits, from the same constant, so + // it always equals `devDependencies.vitest` above (npm refuses one + // that differs: `EOVERRIDE`) — see SCAFFOLD_NPM_OVERRIDES for the + // npm 10 crash it prevents. Standalone only: npm reads `overrides` + // from the ROOT manifest alone and pnpm never reads it, so inside + // this monorepo's workspace the block would be inert. + ...(standalone ? { overrides: { ...SCAFFOLD_NPM_OVERRIDES } } : {}), }), 'tsconfig.json': (name: string) => standalone diff --git a/packages/cli/src/commands/init.ts b/packages/cli/src/commands/init.ts index 044c086ec0f..295e1067e54 100644 --- a/packages/cli/src/commands/init.ts +++ b/packages/cli/src/commands/init.ts @@ -390,9 +390,13 @@ export const SCAFFOLD_TYPESCRIPT_RANGE = '^5.3.0'; * `--cache` and a manifest holding only this one devDependency (the full matrix * is in the PR that set this value): on npm 10.9.4 and 10.9.9, `npm install` * exits 1 with `Cannot read properties of null (reading 'edgesOut')` from - * arborist's `#loadPeerSet`, and npm 11.21.0 installs it. `~4.0.0` installs on - * all three (vitest 4.0.18, vite 7.3.7), and so do both CLI-emitted plugin - * manifests. The cost: a scaffold gets vitest 4.0.18 rather than 4.1.11. + * arborist's `#loadPeerSet`, and npm 11.21.0 installs it. `~4.0.0` installed on + * all three (vitest 4.0.18, vite 7.3.7), and so did both CLI-emitted plugin + * manifests. The cost: a scaffold gets vitest 4.0.18 rather than 4.1.x. + * + * ⚠️ The range alone stopped being enough, so it is also emitted as an npm + * OVERRIDE — see {@link SCAFFOLD_NPM_OVERRIDES}, which is built from this + * constant, so the declared range and the override cannot disagree. * * The cause is upstream, in the peer graph. Every vitest 4.1.x admits vite 8. * vite 8 declares an optional `@vitejs/devtools` peer, which reaches @@ -415,6 +419,56 @@ export const SCAFFOLD_TYPESCRIPT_RANGE = '^5.3.0'; */ export const SCAFFOLD_VITEST_RANGE = '~4.0.0'; +/** + * The npm `overrides` block every STANDALONE scaffold emits: `os init`'s three + * templates, `os create plugin` outside the monorepo, and — by generation, not + * import — `create-objectstack`'s bundled template (the `overrides.vitest` row + * in `scripts/sync-scaffold-emission-policy.mjs`'s `POLICY_STAMPS`). + * + * Why it exists. vitest 4.1.12 (published 2026-10-15) peers + * `@vitest/browser-webdriverio ^4.1.11`, and that package's only match, 4.1.11, + * peers `vitest: 4.1.11` exactly — a peer set that contradicts itself. A + * scaffold reaches it even with vitest pinned to `~4.0.0` at its root, through + * OPTIONAL `vitest` peers deeper in the closure (`@objectstack/metadata-core` + * peers `^4.0.0`, better-auth peers `^2 || ^3 || ^4`), which npm 10 resolves to + * vitest's newest 4.x. Measured 2026-10-16, each manifest in an empty + * directory with a fresh `--cache`, against the published `18.0.0-next.2`: + * + * without the override npm 10.9.9 `os init -t app`, `-t plugin` and + * `-t empty` all exit 1 with `Cannot + * read properties of null (reading + * 'edgesOut')`; `os create plugin` (no + * `@objectstack/cli` in its closure) + * exits 0 + * with the override npm 10.9.9 all four exit 0 + * npm 11.21.1 all four exit 0 + * + * So the override goes on EVERY template, not only the ones that declare + * vitest: `-t app` and `-t empty` declare none and crashed all the same, + * because what they share is `@objectstack/cli`. Where a manifest does declare + * vitest, the override equals that declaration — npm refuses an override that + * differs from the direct dependency's own spec (`EOVERRIDE`). + * + * The cost, measured on an emitted `-t app` (npm 10.9.9): a later bare `npm + * install -D vitest` is refused with `EOVERRIDE: Override for vitest@* + * conflicts with direct dependency`; `npm install -D vitest@~4.0.0` (this + * range) installs. Loud and self-describing, where the alternative was a + * project that could not install at all. pnpm ignores npm's `overrides` field, + * so pnpm users see no change. + * + * ⛔ Not `"$vitest"`: it needs a direct dependency to point at (two of the + * three `os init` templates have none), and where it does apply, npm stops + * hoisting `@objectstack/metadata-core` and nests copies of it (measured on the + * bundled template) — a duplicated module is a duplicated `instanceof` identity. + * + * The re-check that lets this go: the same empty-directory install of an + * emitted `-t app` manifest WITHOUT this block, on the newest npm 10 and on npm + * 11. When both exit 0, delete it here and its `POLICY_STAMPS` row together. + */ +export const SCAFFOLD_NPM_OVERRIDES: Readonly> = Object.freeze({ + vitest: SCAFFOLD_VITEST_RANGE, +}); + /** The `@types/node` range a scaffolded project declares. */ export const SCAFFOLD_TYPES_NODE_RANGE = '^22.0.0'; @@ -495,6 +549,9 @@ export function renderScaffoldPackageJson( scripts: template.scripts, dependencies: template.dependencies, devDependencies: template.devDependencies, + // Every template, whether or not it declares vitest itself — without it + // npm 10 cannot install any of them (see SCAFFOLD_NPM_OVERRIDES). + overrides: { ...SCAFFOLD_NPM_OVERRIDES }, }; } diff --git a/packages/cli/test/scaffold-npm-overrides.test.ts b/packages/cli/test/scaffold-npm-overrides.test.ts new file mode 100644 index 00000000000..20f76e9de73 --- /dev/null +++ b/packages/cli/test/scaffold-npm-overrides.test.ts @@ -0,0 +1,110 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +/** + * PIN — every standalone manifest `os init` and `os create` emit carries the + * npm `overrides` block, built from the one vitest constant. + * + * ## The defect + * + * vitest 4.1.12 shipped a self-contradicting peer set (it peers + * `@vitest/browser-webdriverio ^4.1.11`, whose only release peers `vitest: + * 4.1.11` exactly). npm 10 — the npm Node 22 bundles — reaches it through + * OPTIONAL `vitest` peers in the `@objectstack/cli` closure, even with vitest + * pinned to `~4.0.0` at the project root, and its arborist crashes with + * `Cannot read properties of null (reading 'edgesOut')`. Measured 2026-10-16: + * `os init -t app`, `-t plugin` and `-t empty` all failed `npm install` on npm + * 10.9.9; with `"overrides": { "vitest": "~4.0.0" }` all of them, and `os + * create plugin`, installed on npm 10.9.9 and npm 11.21.1. The full reading is + * on `SCAFFOLD_NPM_OVERRIDES` in `src/commands/init.ts`. + * + * ## What is asserted, and why no range is written down here + * + * Every expectation is DERIVED from the exported constants, never a literal: + * a pin comparing against `'~4.0.0'` would stay green on a tree where the + * constant moved and one emission was left behind — the drift it exists to + * catch. + * + * 1. Each emission's `overrides.vitest` IS `SCAFFOLD_VITEST_RANGE`. + * 2. Where an emission also declares `vitest` directly, the two are equal — + * npm refuses an override that differs from the direct dependency's own + * spec (`EOVERRIDE`), so a mismatch is an install failure of its own. + * 3. `os create plugin --in-repo` emits NO `overrides`: npm reads the field + * only from the root manifest and pnpm never reads it, so inside the + * workspace it would be a declaration nothing honours. + * + * That the emitted manifests really install is not decidable here; the npm + * matrix above is the measurement, and `create-objectstack`'s bundled template + * reaches the same constant through `scripts/sync-scaffold-emission-policy.mjs` + * (`pnpm check:scaffold-emission-policy`). + */ + +import { describe, it, expect } from 'vitest'; +import { + renderScaffoldPackageJson, + SCAFFOLD_NPM_OVERRIDES, + SCAFFOLD_VITEST_RANGE, + TEMPLATES, +} from '../src/commands/init.js'; +import { DEFAULT_PLACEMENT, templates } from '../src/commands/create.js'; + +const PROBE_NAME = 'overrides-probe'; + +type Manifest = { + dependencies?: Record; + devDependencies?: Record; + overrides?: Record; +}; + +/** Every standalone manifest the two commands emit, derived from the live maps. */ +function standaloneManifests(): Array<{ id: string; manifest: Manifest }> { + const out: Array<{ id: string; manifest: Manifest }> = []; + for (const [key, template] of Object.entries(TEMPLATES)) { + out.push({ id: `os init -t ${key}`, manifest: renderScaffoldPackageJson(PROBE_NAME, template) as Manifest }); + } + for (const [key, template] of Object.entries(templates)) { + const render = template.filesFor(DEFAULT_PLACEMENT)['package.json']; + if (!render) continue; + out.push({ id: `os create ${key}`, manifest: render(PROBE_NAME) as Manifest }); + } + return out; +} + +describe('npm overrides in every standalone scaffold manifest', () => { + it('harvests every emission, and the default placement IS standalone (control)', () => { + expect(DEFAULT_PLACEMENT).toBe('standalone'); + const ids = standaloneManifests().map((m) => m.id); + expect(ids).toEqual(expect.arrayContaining(['os init -t app', 'os init -t plugin', 'os create plugin'])); + // Assertion 2 below must range over something, or it certifies nothing. + const direct = standaloneManifests().filter((m) => m.manifest.devDependencies?.vitest !== undefined); + expect(direct.map((m) => m.id)).toEqual(expect.arrayContaining(['os init -t plugin', 'os create plugin'])); + }); + + it('the override map is built from the vitest constant', () => { + expect(SCAFFOLD_NPM_OVERRIDES.vitest).toBe(SCAFFOLD_VITEST_RANGE); + expect(Object.isFrozen(SCAFFOLD_NPM_OVERRIDES)).toBe(true); + }); + + it.each(standaloneManifests())('$id emits overrides.vitest = SCAFFOLD_VITEST_RANGE', ({ manifest }) => { + expect(manifest.overrides).toEqual({ ...SCAFFOLD_NPM_OVERRIDES }); + expect(manifest.overrides?.vitest).toBe(SCAFFOLD_VITEST_RANGE); + }); + + it.each(standaloneManifests())( + '$id: every override naming a direct dependency equals that dependency (npm EOVERRIDE)', + ({ manifest }) => { + const direct = { ...(manifest.dependencies ?? {}), ...(manifest.devDependencies ?? {}) }; + for (const [name, range] of Object.entries(manifest.overrides ?? {})) { + if (direct[name] === undefined) continue; + expect(range, `overrides.${name} vs the direct ${name} dependency`).toBe(direct[name]); + } + }, + ); + + it('os create --in-repo emits no overrides block — npm and pnpm both ignore it in a workspace member', () => { + const render = templates.plugin.filesFor('in-repo')['package.json']; + const manifest = render(PROBE_NAME) as Manifest; + // Control: the in-repo manifest is a real one, with vitest declared. + expect(manifest.devDependencies?.vitest).toBe(SCAFFOLD_VITEST_RANGE); + expect(manifest).not.toHaveProperty('overrides'); + }); +}); diff --git a/packages/create-objectstack/src/template-consistency.test.ts b/packages/create-objectstack/src/template-consistency.test.ts index 9a02a6fb7d7..4ef78cc9190 100644 --- a/packages/create-objectstack/src/template-consistency.test.ts +++ b/packages/create-objectstack/src/template-consistency.test.ts @@ -218,8 +218,10 @@ describe('bundled template declared version surfaces', () => { // The literal costs one thing instead, and loudly: npm refuses an override // that differs from the direct dependency's own spec (`EOVERRIDE: Override // for vitest@^4.1.0 conflicts with direct dependency`, measured), so the two - // move together. That is the second assertion. `devDependencies.vitest` is - // stamped from `SCAFFOLD_VITEST_RANGE` at build; this override is not. + // move together. That is the second assertion. Both are stamped from + // `SCAFFOLD_VITEST_RANGE` at build (the `devDependencies.vitest` and + // `overrides.vitest` rows of `scripts/sync-scaffold-emission-policy.mjs`), + // the constant `os init` / `os create` emit their own override from. // ⛔ Not a shipped `.npmrc` with `legacy-peer-deps`, which changes peer // resolution for every npm user of the scaffold. it('package.json overrides vitest to its own declared range, so npm 10 installs it', () => { diff --git a/scripts/sync-scaffold-emission-policy.mjs b/scripts/sync-scaffold-emission-policy.mjs index c42ff79acd0..9f598e21f5e 100644 --- a/scripts/sync-scaffold-emission-policy.mjs +++ b/scripts/sync-scaffold-emission-policy.mjs @@ -152,6 +152,18 @@ export const POLICY_STAMPS = [ // plugin scaffolds of `os init` and `os create` emit. The comment on the // constant holds why it is `~4.0.0` (npm 10 cannot install `^4.0.0`). }, + { + key: 'overrides.vitest', + block: 'overrides', + field: 'vitest', + constant: 'SCAFFOLD_VITEST_RANGE', + // The npm override every standalone `os init` / `os create` manifest + // emits from this same constant (`SCAFFOLD_NPM_OVERRIDES`, whose comment + // holds the npm 10 crash it prevents). Stamped rather than hand-kept for + // a second reason as well: npm refuses an override that differs from the + // direct `devDependencies.vitest` (`EOVERRIDE`), so the two rows above and + // here must move together, and only generation makes them. + }, ]; /** @@ -600,7 +612,7 @@ function main() { // ordinary work — and a battery BELOW its floor means cases stopped registering. const SELF_TEST_BATTERIES = Object.freeze({ 'A: a CLEAN corpus is REACHED, and left byte-identical and UNWRITTEN': 6, - 'B: DRIFT is rewritten, and --check reds on it first': 8, + 'B: DRIFT is rewritten, and --check reds on it first': 9, 'C: a renamed policy constant is a hard failure, never a silent skip': 3, 'D: a template omitting a stamped key exits 1 naming the path': 3, 'E: an unparseable template package.json exits 1 naming it': 2, @@ -608,8 +620,9 @@ const SELF_TEST_BATTERIES = Object.freeze({ 'G: the stamp table and the sibling rewriter do not both own a value': 2, 'H: the PUBLISHED restatement is READ — and never written': 16, 'I: the restatement table cannot go vacuous or invent a policy value': 4, + 'J: the npm OVERRIDE is stamped on its own — its drift reds even when devDependencies agrees': 10, }); -const SELF_TEST_BATTERY_FLOOR = 9; +const SELF_TEST_BATTERY_FLOOR = 10; const UNATTRIBUTED_BATTERY = '(no battery open)'; const SELF_TEST_VERDICT = 'sync-scaffold-emission-policy self-test reached its verdict'; @@ -632,7 +645,11 @@ function policySource(policy = SELF_TEST_POLICY) { ].join('\n'); } -function templatePkg(policy) { +/** + * A fixture template `package.json`. `overrideVitest` lets one case drift the + * override ALONE while `devDependencies.vitest` still agrees with the policy. + */ +function templatePkg(policy, { overrideVitest } = {}) { return `${JSON.stringify( { name: 'objectstack-fixture', @@ -644,6 +661,7 @@ function templatePkg(policy) { typescript: policy.SCAFFOLD_TYPESCRIPT_RANGE, vitest: policy.SCAFFOLD_VITEST_RANGE, }, + overrides: { vitest: overrideVitest ?? policy.SCAFFOLD_VITEST_RANGE }, }, null, 2, @@ -694,7 +712,13 @@ function restatementDoc({ function buildFixture( dir, - { templates = ['blank', 'second'], policy = SELF_TEST_POLICY, templatePolicy = policy, restatement = {} } = {}, + { + templates = ['blank', 'second'], + policy = SELF_TEST_POLICY, + templatePolicy = policy, + overrideVitest, + restatement = {}, + } = {}, ) { const scripts = join(dir, 'scripts'); mkdirSync(scripts, { recursive: true }); @@ -705,7 +729,7 @@ function buildFixture( writeFileSync(join(dir, POLICY_SOURCE), policySource(policy)); for (const template of templates) { mkdirSync(join(dir, TEMPLATE_DIR, template), { recursive: true }); - writeFileSync(join(dir, TEMPLATE_DIR, template, TEMPLATE_PKG_FILE), templatePkg(templatePolicy)); + writeFileSync(join(dir, TEMPLATE_DIR, template, TEMPLATE_PKG_FILE), templatePkg(templatePolicy, { overrideVitest })); } const carrier = join(dir, PUBLISHED_RESTATEMENTS[0].path); mkdirSync(dirname(carrier), { recursive: true }); @@ -775,12 +799,20 @@ function selfTest() { after.every( (t) => t.includes('"typescript": "^9.9.9"') && - t.includes('"vitest": "~8.8.0"') && t.includes('"pnpm": ">=99.1"') && t.includes('"@objectstack/spec": "^17.0.0"'), ), 'EVERY template now emits the policy, with the @objectstack/* ranges untouched', ); + // Parsed, not substring-matched: `vitest` is stamped into TWO blocks, so + // a `"vitest": "~8.8.0"` substring would hold with either one still stale. + ok( + after.every((t) => { + const pkg = JSON.parse(t); + return pkg.devDependencies.vitest === '~8.8.0' && pkg.overrides.vitest === '~8.8.0'; + }), + 'BOTH vitest stamps — devDependencies.vitest and overrides.vitest — now read the policy', + ); ok(runFixture(script, ['--check']).status === 0, '--check is green after the rewrite (idempotent)'); }, { templatePolicy: STALE }, @@ -904,6 +936,47 @@ function selfTest() { } ok(refusedEmpty, 'an EMPTY table is refused rather than reported as "everything checked"'); + // The override is the one stamp whose drift a sibling key MASKS: the same + // field name sits in `devDependencies`, already correct, so a check that read + // "is vitest right?" anywhere in the file would pass. This battery drifts the + // override ALONE and requires the verdict to name that block. + open('J: the npm OVERRIDE is stamped on its own — its drift reds even when devDependencies agrees'); + sandbox( + (dir, script) => { + const before = readTemplates(dir); + const parsed = JSON.parse(before[0]); + ok( + parsed.overrides.vitest === '^1.1.0' && parsed.devDependencies.vitest === '~8.8.0', + 'the fixture drifts the override ALONE — devDependencies.vitest already reads the policy', + ); + const check = runFixture(script, ['--check']); + ok(check.status === 1, '--check EXITS 1 when only the override has drifted'); + ok(check.output.includes('overrides.vitest is "^1.1.0"'), '--check names the override block and its found value'); + ok(!check.output.includes('devDependencies.vitest is'), '--check does not blame the agreeing devDependencies.vitest'); + ok(readTemplates(dir).every((t, i) => t === before[i]), '--check WROTE NOTHING'); + const write = runFixture(script); + ok(write.status === 0, 'the write run exits 0'); + const after = readTemplates(dir); + ok( + after.every((t, i) => t === before[i].replace('"vitest": "^1.1.0"', '"vitest": "~8.8.0"')), + 'the rewrite touches the override span and NOTHING else, byte for byte', + ); + ok(runFixture(script, ['--check']).status === 0, '--check is green after the rewrite (idempotent)'); + }, + { overrideVitest: STALE.SCAFFOLD_VITEST_RANGE }, + ); + sandbox((dir, script) => { + const pkg = JSON.parse(readFileSync(join(dir, TEMPLATE_DIR, 'second', TEMPLATE_PKG_FILE), 'utf8')); + delete pkg.overrides; + writeFileSync(join(dir, TEMPLATE_DIR, 'second', TEMPLATE_PKG_FILE), `${JSON.stringify(pkg, null, 2)}\n`); + const r = runFixture(script, ['--check']); + ok(r.status === 1, 'a template that drops the overrides block EXITS 1 — never a silent exemption'); + ok( + r.output.includes('second/package.json') && r.output.includes('declares no `overrides.vitest`'), + 'the failure names the template path and the missing override key', + ); + }); + const missing = Object.keys(SELF_TEST_BATTERIES).filter((n) => !opened.has(n)); const extra = [...opened.keys()].filter((n) => !(n in SELF_TEST_BATTERIES)); const below = [...opened].filter(([n, c]) => n in SELF_TEST_BATTERIES && c < SELF_TEST_BATTERIES[n]); @@ -927,7 +1000,8 @@ function selfTest() { 'BEFORE the rewrite and green after it; and a renamed policy constant, a template omitting a stamped key, ' + 'an unparseable template and an empty templates directory are each observed exiting 1 and naming the path. ' + 'The PUBLISHED restatement is observed COUNTED in the verdict, reddening --check when its fence drifts, ' + - 'naming that fence, and left byte-identical and unreached by the write run.', + 'naming that fence, and left byte-identical and unreached by the write run. The npm OVERRIDE is observed ' + + 'reddening --check on its own drift while devDependencies.vitest agrees, and rewritten alone.', ); return SELF_TEST_VERDICT; } From f8d18b09257057d12893525766d1d4b292e1c020 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 16 Oct 2026 07:56:52 +0000 Subject: [PATCH 2/2] test(create-objectstack): name the stamp script in words, not as a repo path check:cross-package-test-inputs reads a quoted repo-relative path in a test source as an input the test reads; the comment only describes where the override is generated, so it names the script without spelling the path. Claude-Session: https://claude.ai/code/session_018t6a5rvqPRvEDMCVVW9kYT Co-authored-by: Claude --- packages/create-objectstack/src/template-consistency.test.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/create-objectstack/src/template-consistency.test.ts b/packages/create-objectstack/src/template-consistency.test.ts index 4ef78cc9190..8c0afa5d2bf 100644 --- a/packages/create-objectstack/src/template-consistency.test.ts +++ b/packages/create-objectstack/src/template-consistency.test.ts @@ -220,8 +220,9 @@ describe('bundled template declared version surfaces', () => { // for vitest@^4.1.0 conflicts with direct dependency`, measured), so the two // move together. That is the second assertion. Both are stamped from // `SCAFFOLD_VITEST_RANGE` at build (the `devDependencies.vitest` and - // `overrides.vitest` rows of `scripts/sync-scaffold-emission-policy.mjs`), - // the constant `os init` / `os create` emit their own override from. + // `overrides.vitest` rows of the emission-policy stamp script's + // `POLICY_STAMPS`), the constant `os init` / `os create` emit their own + // override from. // ⛔ Not a shipped `.npmrc` with `legacy-peer-deps`, which changes peer // resolution for every npm user of the scaffold. it('package.json overrides vitest to its own declared range, so npm 10 installs it', () => {