Repository navigation
finding: on npm 10 (Node 22's bundled major), npm install of a scaffold that declares vitest ^4.0.0 (SCAFFOLD_VITEST_RANGE: objectstack init -t plugin, objectstack create plugin) crashes with "Cannot read properties of null (reading 'edgesOut')" #22912
Description
Activity
objectstack-fleet commented
on Oct 12, 2026 ContributorAuthorMore actionsTriage: first grade,
bug·priority:p2·domain:cli·area:devpath, intopm:queue. Re-measure first; if still broken, the shared range moves to one npm 10 installsTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-12T00:54Z. ⛔ Not a claim, ⛔ not a dispatch.- Lane:
SCAFFOLD_VITEST_RANGE(packages/cli/src/commands/init.ts, used bycreate.ts) isdomain:cli. - Why p2:
npm installof a plugin scaffold crashes on npm 10, which Node 22 bundles, andnpx create-objectstackfalls back to npm when pnpm is absent. That is the default path for a new plugin author. The cause is upstream (vite's optional devtools peers meeting npm 10's arborist), but the reach is ours. - Direction:
- Re-measure first, with the card's one-line manifest on npm 10 and on npm 11, on a clean cache. If upstream has fixed it, close with the reading.
- If it still crashes, the one shared range moves to the newest vitest range that installs on both npm 10 and npm 11. Use the narrowest change that does it, and record the measured matrix in the PR.
- A comment beside the constant names the upstream cause and the re-check that would let the range move back.
- ⛔ No shipped
.npmrcwithlegacy-peer-deps. ⛔ No per-scaffolder copy of the range.
- Pins:
- A test asserts every scaffolder reads
SCAFFOLD_VITEST_RANGE(one range, no copies). - The install matrix is recorded in the PR body. A live npm install is not a unit test.
- A test asserts every scaffolder reads
- Downstream: docs + scaffold:
plugin-spec.mdxstops promising the non-existent@objectstack/testingand points at@objectstack/verify; thecreate-objectstackblank template ships a test story (epic hotcrm#1579, step 5b) #15952's held scaffold half resumes when the one-line manifest installs on npm 10. docs + scaffold:plugin-spec.mdxstops promising the non-existent@objectstack/testingand points at@objectstack/verify; thecreate-objectstackblank template ships a test story (epic hotcrm#1579, step 5b) #15952 is its own seat's, and is not touched here. Clause-②: no. The template's dev range changes, not a published contract.
- Lane:
- addedarea:devpathThe road — create, dev, verify, publish/install, connect an agent, iterateThe road — create, dev, verify, publish/install, connect an agent, iteratebugSomething isn't workingSomething isn't workingpriority:p2Medium: important, M3Medium: important, M3and removed
on Oct 12, 2026 objectstack-fleet commented
on Oct 12, 2026 ContributorAuthorMore actionsClaim: PM loop round 2 · 2026-10-12T01:19Z
Session:session_01VoSxBQujKLZKPwK2u5ehQ6
Account:os-steve(the seat's linked user asGET /useranswers it; the card's assignee from this act)
Branch:claude/issue-22912-scaffold-vitest-range-npm10
Worktree:objectstack-issue-22912
Domain:domain:cli
Seat:domain:cli#2(seat post #22648)
File surface (read onorigin/main7de0265fc7; stop on a breach and explain it in the report):- First, a re-measure (triage's step 1): the card's one-line manifest
{"devDependencies":{"vitest":"^4.0.0"}}, on npm 10 (10.9.x, Node 22's bundled major) and on npm 11, each on a clean cache. If npm 10 now installs it, no code changes. The report carries the reading, and the seat closes the card with it. - Only if it still crashes:
packages/cli/src/commands/init.ts:SCAFFOLD_VITEST_RANGE(about:383) moves to the newest vitest range that installs on both npm 10 and npm 11, with a comment beside it naming the upstream cause and the re-check that would let it move back.- One unit test asserting that every scaffolder reads
SCAFFOLD_VITEST_RANGEand holds no copy of it (init.tsabout:832,create.tsabout:141/:409). packages/cli/test/scaffold-emission-policy.e2e.test.tsonly if its expected range is a literal.- One
.changeset/22912-*.md(@objectstack/clipatch).
- ⛔ No shipped
.npmrc/legacy-peer-deps. ⛔ No per-scaffolder range. ⛔ Nopackages/create-objectstackchange (docs + scaffold:plugin-spec.mdxstops promising the non-existent@objectstack/testingand points at@objectstack/verify; thecreate-objectstackblank template ships a test story (epic hotcrm#1579, step 5b) #15952's held scaffold half is that card's).
Container & model:S, judged asM(the install matrix must be measured on two npm majors before any edit, and the range chosen against it),mode:subagent,model: default tier(dispatch-gates --tier --repo objectstack-ai/objectstack: "no path-derived mandate"). No contract-review face:Clause-②: no(a template dev-dependency range, not a published contract), nopackages/spec, no governed path.
Clause-②: no
Responsibility:SCAFFOLD_VITEST_RANGE (this lane, packages/cli/src/commands/init.ts) is '^4.0.0', and npm 10's arborist crashes on vitest 4's peer graph through vite's optional devtools peers | the scaffolders already share one range constant | a plugin author on Node 22 without pnpm runs objectstack init -t plugin or objectstack create plugin, and the first npm install crashes with "Cannot read properties of null (reading 'edgesOut')" (measured by #15952's dev on npm 10.9.4 and 10.9.9)
Thread-read: 6115841300
Serial constraints cleared: none named by triage. No open PR touchesinit.ts,create.tsorscaffold-emission-policy.e2e.test.ts(all 11 open PRs' file lists read in this act). None of the openpm:dispatchedclaims declares them. The otherarea:devpathcards in flight are disjoint: [maintainer] validate: thefield-no-consumerswarning is one 856-character line, printed by validate, build and dev alike — one-line verdict +rule:id + a pointer to the full reasoning (os explain, which today takes only schema names) #22161 slice 12 holdspackages/lintrule files andrule-explanations.ts, and release(v18): the version-pr lane refuses the major boundary atValidate the post-version tree, so #21988 never refreshes into 18.0.0-next.0 — wire the lane's major-only gates in, and stop prerelease cuts re-dating the last GA #22085 holds no file surface. docs + scaffold:plugin-spec.mdxstops promising the non-existent@objectstack/testingand points at@objectstack/verify; thecreate-objectstackblank template ships a test story (epic hotcrm#1579, step 5b) #15952's held scaffold half (branchclaude/issue-15952-scaffold-held, no PR) waits on this card and holds nopackages/clifile. Seat 1 ([PM seat] domain:cli — ⏳ vacant #6024) is vacant with no holds.
Generated by Claude Code
- First, a re-measure (triage's step 1): the card's one-line manifest
objectstack-fleet commented
on Oct 12, 2026 ContributorAuthorMore actionsos-dev-report
{ "issue": 22912, "status": "done", "branch": "claude/issue-22912-scaffold-vitest-range-npm10", "pr": "https://github.com/objectstack-ai/objectstack/pull/22920", "session": "session_01VoSxBQujKLZKPwK2u5ehQ6 — mode:subagent, so this is the dispatching PM's harness-stamped id (the Claude-Session trailer on cd7e34ee27)", "premise_still_valid": true, "summary": "Re-measured from scratch: the card's one-line manifest {\"devDependencies\":{\"vitest\":\"^4.0.0\"}} still crashes on npm 10.9.4 and 10.9.9 (edgesOut, arborist #loadPeerSet) and installs on npm 11.21.0, so upstream has not fixed it. SCAFFOLD_VITEST_RANGE (packages/cli/src/commands/init.ts) moves ^4.0.0 -> ~4.0.0, the narrowest range that installs on both majors: it resolves vitest 4.0.18 + vite 7.3.7, and both real CLI-emitted plugin manifests (os init -t plugin, os create plugin) now install on npm 10.9.4, 10.9.9 and 11.21.0 (both crashed before). Measured mechanism: npm 10 crashes when vite 8 is in the tree (its optional @vitejs/devtools peer leads to @vitejs/devtools-vitest, which peers vitest@*) AND the root vitest is not the version `latest` names; every vitest 4.1.x admits vite 8, all 19 vitest 4.0.x admit only vite ^6 or ^7, and no vite 6/7 release declares the devtools peer. ^5.0.0 installs today only because it resolves to the current latest (exact 5.0.0 and 5.0.2 crash), so it was not chosen; that choice against the decision's word 'newest' is open question 1. The one-range pin already exists (nightly-tier scaffold-emission-policy.e2e.test.ts, constant-derived, unchanged); a copied-literal ablation turned it red (3 failed). No unit test added; one patch changeset; draft PR #22920 assigned os-steve.", "tests": "All at cd7e34ee27 (PR head, final commit; nothing pushed after). (1) Closure build `pnpm --workspace-concurrency=2 --filter '@objectstack/cli^...' build` via os-verify-lock: VERDICT command-exit 0 (held 7m09s). (2) `pnpm --filter @objectstack/cli typecheck`: VERDICT command-exit 0 (tsc --noEmit; check:test-typecheck OK, 3 file(s)/28 error(s)/6 pinned signature(s) held in the ledger). (3) `pnpm --filter @objectstack/cli exec vitest run --project unit --maxWorkers=2`: 'Test Files 282 passed (282) / Tests 4262 passed (4262)', VERDICT command-exit 0. Integration layer declared to CI (no integration-layer file or spawn entry touched). (4) `OS_TEST_TIERS=nightly pnpm --filter @objectstack/cli exec vitest run --maxWorkers=2 test/scaffold-emission-policy.e2e.test.ts`: 'Tests 10 passed (10)', VERDICT command-exit 0. (5) Ablation, one-shot, scripts/ablation-replace.mjs wrap mode inside the lock: create.ts `vitest: SCAFFOLD_VITEST_RANGE,` -> `vitest: '^4.0.0',`; on-disk proof 'anchor x1 -> x0, replace x0 -> x1, blob d2c94f6a66f5 -> 98d8393f80b4'; run 'Tests 3 failed | 7 passed (10)' (one range per dependency; exported constant; across ALL THREE scaffolders); restore 'blob == HEAD (d2c94f6a66f5) and git diff HEAD is empty'; git status --porcelain 0 lines after. Subject is imported by relative src path, so no dist rebuild applies. (6) Gates: `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` (no paths) = 59; all 59 run, each exit 0; `--ran` verdict: 'Run reconciliation — 59 derived, 59 run, 0 NOT-MEASURED, 0 UNRUN' (derived zero, all 59 exit-coded). check:dual-build-cjs-loads and check:i18n-coverage first exited 3 (PREREQUISITE NOT MET, 10 packages without dist/); after `turbo run build --concurrency=2` of those 10 under the lock (59 tasks, VERDICT command-exit 0, tree clean) both exited 0: '107 published require entry point(s) across 66 package(s) load' and 'check-i18n-coverage: OK (13 config(s), 621 baselined untranslated string(s), none new)'. check:scaffold-emission-policy: '2 carrier(s) emit the shared policy'. check:nul-bytes: 'OK (scanned 10955 text file(s)...)'. (7) Lint, proven narrowing: `pnpm exec eslint --no-inline-config --format json packages/cli/src/commands/init.ts` exit 0; JSON: files 1, errors 0, warnings 0; population from eslint itself: --print-config resolves 5 rules for the file (not ignored); invariance: config has no parserOptions.project / projectService (0 hits; --print-config shows null for both; the config's own comment states it never enables type-aware linting), so no untouched file's verdict can move. Repo-wide pnpm lint is CI's. (8) Install matrix: see install_matrix; measured with npm 10.9.9 / 11.21.0 installed into scratch prefixes and run by path (not npx); container global npm 10.9.4 unchanged.", "mcp_calls": "0 — no MCP GitHub tool called", "api_writes": "3 REST writes as objectstack-fleet[bot], through 2 fleet-write relay strokes (each one POST /repos/objectstack-ai/objectstack/dispatches from this session): stroke 1 (run 38196429205, success) = POST /repos/objectstack-ai/objectstack/pulls (draft) + POST /repos/objectstack-ai/objectstack/issues/22920/assignees [os-steve]; stroke 2 = POST /repos/objectstack-ai/objectstack/issues/22912/comments (this os-dev-report). One git push of the branch (fast-forward 7de0265fc7..cd7e34ee27) is not a REST write. Zero label writes.", "open_questions": [ { "question": "The decision says the range moves to 'the newest vitest range that installs on both npm 10 and npm 11'. Measured, ^5.0.0 is newer than ~4.0.0 and installs on both today. This PR ships ~4.0.0. Keep it?", "options": [ "A ~4.0.0 (this PR): a strict subset of today's ^4.0.0, so it admits no new version; it installs on npm 10 because vitest 4.0.x keeps vite on 7.x, where the vitest@* peer never enters the graph, so it does not depend on the `latest` tag. Cost: a scaffold gets vitest 4.0.18 instead of 4.1.11.", "B ^5.0.0: newest; installs today only because it resolves to exactly the current `latest` (5.0.3). Exact 5.0.0 and 5.0.2 crash on npm 10.9.9 the same way, so ^5 would crash again on npm 10 the day `latest` leaves 5.x, with no change in this repo. It is also a major bump of the scaffold's test runner. One-line change if chosen." ], "recommendation": "A, because 'installs on both' holds for ~4.0.0 independently of the registry's `latest` tag, while for ^5.0.0 it holds only until the next vitest major; it is also the narrowest change, as the decision's next sentence asks. The PM's mechanism assumption also preferred keeping vitest 4 if any 4.x works." } ], "out_of_scope_findings": [ "carrier: #15952's dev (held scaffold half, branch claude/issue-15952-scaffold-held) · noted, not filed: the one-line manifest now installs on npm 10 with ~4.0.0, but SCAFFOLD_VITEST_RANGE is not a POLICY_STAMPS row in scripts/sync-scaffold-emission-policy.mjs, so a vitest line added to create-objectstack's blank template would need a new stamp row to stay tied to this constant (check:scaffold-emission-policy today covers SCAFFOLD_PNPM_RANGE and SCAFFOLD_TYPESCRIPT_RANGE only). Also in the PR's Acceptance notes.", "carrier: 承接者:无 · noted, not filed: the one-range pin lives in a nightly-tier file (selected by its .e2e name), so a copied literal reds a day later, not on the PR. By design of the tier split; in the PR's Acceptance notes." ], "files_changed": [ ".changeset/22912-scaffold-vitest-range-npm10.md", "packages/cli/src/commands/init.ts" ], "line_budget": "52 changed lines (+49 / -3) across 2 files, per dispatch-gates on cd7e34ee27; under the 3000 human-merge threshold. No governed path.", "install_matrix": [ "npm 10.9.4 · node v22.22.0 · {\"devDependencies\":{\"vitest\":\"^4.0.0\"}} · fresh cache · exit 1 · vitest none / vite none · npm error Cannot read properties of null (reading 'edgesOut')", "npm 10.9.9 · node v22.22.0 · {\"devDependencies\":{\"vitest\":\"^4.0.0\"}} · fresh cache · exit 1 · vitest none / vite none · npm error Cannot read properties of null (reading 'edgesOut')", "npm 11.21.0 · node v22.22.0 · {\"devDependencies\":{\"vitest\":\"^4.0.0\"}} · fresh cache · exit 0 · vitest 4.1.11 / vite 8.3.4 · none", "npm 10.9.4 · node v22.22.0 · {\"devDependencies\":{\"vitest\":\"~4.1.0\"}} · fresh cache · exit 1 · vitest none / vite none · npm error Cannot read properties of null (reading 'edgesOut')", "npm 10.9.9 · node v22.22.0 · {\"devDependencies\":{\"vitest\":\"~4.1.0\"}} · fresh cache · exit 1 · vitest none / vite none · npm error Cannot read properties of null (reading 'edgesOut')", "npm 10.9.4 · node v22.22.0 · {\"devDependencies\":{\"vitest\":\"~4.0.0\"}} · fresh cache · exit 0 · vitest 4.0.18 / vite 7.3.7 · none", "npm 10.9.9 · node v22.22.0 · {\"devDependencies\":{\"vitest\":\"~4.0.0\"}} · fresh cache · exit 0 · vitest 4.0.18 / vite 7.3.7 · none", "npm 11.21.0 · node v22.22.0 · {\"devDependencies\":{\"vitest\":\"~4.0.0\"}} · fresh cache · exit 0 · vitest 4.0.18 / vite 7.3.7 · none", "npm 10.9.9 · node v22.22.0 · {\"devDependencies\":{\"vitest\":\"^5.0.0\"}} · fresh cache · exit 0 · vitest 5.0.3 / vite 8.3.4 · none", "npm 11.21.0 · node v22.22.0 · {\"devDependencies\":{\"vitest\":\"^5.0.0\"}} · fresh cache · exit 0 · vitest 5.0.3 / vite 8.3.4 · none", "npm 10.9.9 · node v22.22.0 · {\"devDependencies\":{\"vitest\":\"5.0.0\"}} · fresh cache · exit 1 · vitest none / vite none · npm error Cannot read properties of null (reading 'edgesOut')", "npm 10.9.9 · node v22.22.0 · {\"devDependencies\":{\"vitest\":\"5.0.2\"}} · fresh cache · exit 1 · vitest none / vite none · npm error Cannot read properties of null (reading 'edgesOut')", "EMITTED os init -t plugin (spec/cli ^17.7.0, typescript ^5.3.0, vitest ^4.0.0, before) · npm 10.9.4 · node v22.22.0 · fresh cache · exit 1 · vitest none / vite none · npm error Cannot read properties of null (reading 'edgesOut')", "EMITTED os create plugin (spec ^17.7.0, zod ^4.3.6, @types/node ^22.0.0, typescript ^5.3.0, vitest ^4.0.0, before) · npm 10.9.4 · node v22.22.0 · fresh cache · exit 1 · vitest none / vite none · npm error Cannot read properties of null (reading 'edgesOut')", "EMITTED os init -t plugin (vitest ~4.0.0) · npm 10.9.4 · node v22.22.0 · fresh cache · exit 0 · vitest 4.0.18 / vite 7.3.7 · none", "EMITTED os create plugin (vitest ~4.0.0) · npm 10.9.4 · node v22.22.0 · fresh cache · exit 0 · vitest 4.0.18 / vite 7.3.7 · none", "EMITTED os init -t plugin (vitest ~4.0.0) · npm 10.9.9 · node v22.22.0 · fresh cache · exit 0 · vitest 4.0.18 / vite 7.3.7 · none", "EMITTED os create plugin (vitest ~4.0.0) · npm 10.9.9 · node v22.22.0 · fresh cache · exit 0 · vitest 4.0.18 / vite 7.3.7 · none", "EMITTED os init -t plugin (vitest ~4.0.0) · npm 11.21.0 · node v22.22.0 · fresh cache · exit 0 · vitest 4.0.18 / vite 7.3.7 · none", "EMITTED os create plugin (vitest ~4.0.0) · npm 11.21.0 · node v22.22.0 · fresh cache · exit 0 · vitest 4.0.18 / vite 7.3.7 · none", "NOT MEASURED: npm 12.2.0 (npm's latest) refuses node v22.22.0 (engines ^22.22.2 || ^24.15.0 || >=26.0.0) and no other Node is on the box; pnpm/yarn/bun not measured; ~4.1.0 on npm 11 not measured" ], "gates": [ "node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0", "node scripts/check-adr-0087-registration.mjs --self-test :: exit 0", "node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0", "node scripts/check-changeset-no-major.mjs --self-test :: exit 0", "node scripts/check-ci-filter-parity.mjs :: exit 0", "node scripts/check-closing-keyword-parity.mjs :: exit 0", "node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0", "node scripts/check-comment-mask-adoption.mjs :: exit 0", "node scripts/check-comment-mask-adoption.mjs --self-test :: exit 0", "node scripts/check-comment-mask-corpus.mjs :: exit 0", "node scripts/check-dts-emitted.mjs --self-test :: exit 0", "node scripts/check-empty-changeset.mjs --base origin/main :: exit 0", "node scripts/check-empty-changeset.mjs --self-test :: exit 0", "node scripts/check-issue-citations.mjs :: exit 0", "node scripts/check-keyed-text-bounds.mjs :: exit 0", "node scripts/check-keyed-text-bounds.mjs --self-test :: exit 0", "node scripts/check-platform-object-tenancy-census.mjs :: exit 0", "node scripts/check-platform-object-tenancy-census.mjs --self-test :: exit 0", "node scripts/check-plugin-teardown-shape.mjs :: exit 0", "node scripts/check-plugin-teardown-shape.mjs --self-test :: exit 0", "node scripts/check-registry-log-declared.mjs :: exit 0", "node scripts/check-registry-log-declared.mjs --self-test :: exit 0", "node scripts/check-rest-log-spy-declared.mjs :: exit 0", "node scripts/check-rest-log-spy-declared.mjs --self-test :: exit 0", "node scripts/check-system-context-census.mjs :: exit 0", "node scripts/check-system-context-census.mjs --self-test :: exit 0", "node scripts/check-undeclared-dep-imports.mjs :: exit 0", "node scripts/check-undeclared-dep-imports.mjs --self-test :: exit 0", "node scripts/docs-audit/check-affected-docs.mjs :: exit 0", "node scripts/docs-audit/check-drift-comment.mjs :: exit 0", "node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0", "node scripts/release-pending-publish.mjs --self-test :: exit 0", "pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0", "pnpm check:changeset-gate-self-tests :: exit 0", "pnpm check:cross-package-test-inputs :: exit 0", "pnpm check:doc-authoring :: exit 0", "pnpm check:driver-memory-census :: exit 0", "pnpm check:dts-closure :: exit 0", "pnpm check:dual-build-cjs-loads :: exit 0", "pnpm check:gitlink-declared :: exit 0", "pnpm check:i18n :: exit 0", "pnpm check:i18n-coverage :: exit 0", "pnpm check:i18n-walk-parity :: exit 0", "pnpm check:issue-citations :: exit 0", "pnpm check:lean-entry-closure :: exit 0", "pnpm check:logger-receiver-detach :: exit 0", "pnpm check:nul-bytes :: exit 0", "pnpm check:objectui-changeset :: exit 0", "pnpm check:org-identifier :: exit 0", "pnpm check:page-declaration-shape :: exit 0", "pnpm check:pm-changeset-deadline-census :: exit 0", "pnpm check:published-files :: exit 0", "pnpm check:refd-timer-probe :: exit 0", "pnpm check:scaffold-emission-policy :: exit 0", "pnpm check:slot-lookup :: exit 0", "pnpm check:sourcemap-no-sources-content :: exit 0", "pnpm check:test-source-alias :: exit 0", "pnpm check:tier-file-adoption :: exit 0", "pnpm check:watch-hint-literal :: exit 0", "pnpm --filter @objectstack/cli typecheck :: exit 0", "pnpm --filter @objectstack/cli exec vitest run --project unit --maxWorkers=2 :: exit 0", "OS_TEST_TIERS=nightly pnpm --filter @objectstack/cli exec vitest run --maxWorkers=2 test/scaffold-emission-policy.e2e.test.ts :: exit 0", "pnpm --workspace-concurrency=2 --filter '@objectstack/cli^...' build :: exit 0", "pnpm exec eslint --no-inline-config --format json packages/cli/src/commands/init.ts :: exit 0", "ablation (copied literal in create.ts) scaffold-emission-policy.e2e.test.ts :: exit 1 (expected red; restored, git diff HEAD empty)" ], "deviations": [ "Branch fast-forwarded from 7de0265fc7 to origin/main 7dbb89dc5d before the first edit (it carried no commits); the push was a fast-forward, no force. BASE = 7dbb89dc5d. origin/main has since moved one commit (eab7236ff7, touches packages/cli/src/commands/verify.ts among others, not this diff's files); not merged.", "No unit test added: dispatch mechanism assumption 4 found an existing pin (scaffold-emission-policy.e2e.test.ts, nightly tier) and said 'If one does, say so'. The claim's file surface had listed 'one unit test'; the dispatch's narrower instruction was followed. The e2e file is unchanged (its expectation is the constant, not a literal).", "Range chosen is ~4.0.0, not ^5.0.0, though ^5.0.0 is the newer range that installs on both today: named as open question 1, not silently chosen.", "npm 10.9.9 and 11.21.0 were run from scratch-prefix installs by path, not npx (the order allows an equivalent); npm 12.2.0 NOT MEASURED (refuses node v22.22.0).", "Two derived gates (check:dual-build-cjs-loads, check:i18n-coverage) first exited 3 (PREREQUISITE NOT MET); 10 packages outside the cli closure were built under the lock to make them measurable; the --ran record carries their re-run exit 0.", "The PM's path-derived list had 66 commands; this no-path derivation on cd7e34ee27 has 59, and that is the list reconciled.", "The harness attribution reminder asked for a model-named Co-Authored-By trailer; the commit carries AGENTS.md's model-free pair (Claude-Session + Co-authored-by: Claude) instead, per the role file's precedence rule." ] }
Generated by Claude Code
objectstack-fleet commented
on Oct 12, 2026 ContributorAuthorMore actions✅ ACCEPT: PR #22920 at
cd7e34ee27. It lands when every check on the head is greendomain:cliseat 2 (#22648) ·os-steve· sessionsession_01VoSxBQujKLZKPwK2u5ehQ6· 2026-10-12T02:41Z · holder of claim6116057751(taken over after a container restart; the takeover dev reused the clean worktree). Report:os-dev-report6116769534. Thread-read: 6116769534.Checklist, read against GitHub:
- PR shape: draft, base
main, line 1Fixes #22912, line 2Clause-②: no, assigneeos-steve. The body carries no other closing keyword (1 match in a full-body scan). - Scope: 2 files, +49 / −3 (52 lines):
packages/cli/src/commands/init.ts(the constant and its comment) and the changeset. No.npmrc, no per-scaffolder range, nopackages/create-objectstack, nopackages/spec.check-governed-merges --pr 22920: not governed. - Re-measure first (triage step 1): the one-line manifest still crashes on npm 10.9.4 and 10.9.9 (
edgesOut, arborist#loadPeerSet) and installs on npm 11.21.0. Upstream has not fixed it, so step 2 applies. - The move (triage step 2):
SCAFFOLD_VITEST_RANGEgoes from^4.0.0to~4.0.0. The install matrix in the report and the PR body:- the one-line manifest and both CLI-emitted plugin manifests (
os init -t plugin,os create plugin) crashed on npm 10 before, and now install on 10.9.4, 10.9.9 and 11.21.0; - they resolve vitest 4.0.18 and vite 7.3.7;
~4.1.0still crashes.
- the one-line manifest and both CLI-emitted plugin manifests (
- The comment (triage step 3): it names the measured mechanism. vitest 4.1.x admits vite 8, whose optional devtools peer reaches a
vitest@*peer that npm 10's arborist crashes on; vitest 4.0.x stays on vite 6/7, which declare no such peer. It also names the re-check that lets the range widen again. - Open question 1 (
~4.0.0against^5.0.0), taken as A,~4.0.0:^5.0.0is newer and installs today, but only because it resolves to exactly the currentlatest(5.0.3); pinned 5.0.0 and 5.0.2 crash the same way. So "installs on both" holds for it only untillatestmoves past 5.x.~4.0.0holds independently of thelatesttag, is a strict subset of the old range, and is the narrowest change, as the decision's next sentence asks.- Read together, "newest range that installs on both" means newest DURABLY, and
~4.0.0is that.
- Pins:
- The one-range pin triage asks for already exists:
packages/cli/test/scaffold-emission-policy.e2e.test.tsreads the constant for all three scaffolders. So the claim's conditional "one unit test" was not added, and the dispatch's narrower instruction was followed. - An ablation copying a literal into
create.tsturned that pin red (3 failed), and the restore is proven against HEAD. - It is nightly-tier, so a copied literal reds a day later rather than on the PR. That is by the tier split, and noted in the PR's acceptance notes.
- The one-range pin triage asks for already exists:
- Changeset
.changeset/22912-scaffold-vitest-range-npm10.md:@objectstack/clipatch,Clause-②: no. Each sentence matches the diff, and it gives already-scaffolded projects their one-line remedy. - Tests and gates at
cd7e34ee27:@objectstack/cliunit: 282 files, 4262 tests. Typecheck exits 0. The nightly e2e: 10 tests.- 59 gates derived with no paths, 59 run, 0 NOT MEASURED. Lint is narrowed with the three-part proof over the one touched
.tsfile.
- No contract-review face:
Clause-②: no(a scaffold's dev-dependency range, not a published contract), nopackages/spec, no governed path. - CI on
cd7e34ee27at this stamp: reading the latest run per name, 16 success and 3 skipped (roster), and 13 in progress. It is not green yet. - Writes:
mcp_calls0;api_writes2 (pr_createwith its assignee, and the report).
Downstream, #15952's held scaffold half. That card's unlock condition read "the one-line manifest installs on npm 10". Measured here,
^4.0.0still does not install;~4.0.0does. So the held half resumes once this lands, if its blank template declares vitest atSCAFFOLD_VITEST_RANGE.scripts/sync-scaffold-emission-policy.mjshas no stamp row for that constant (it stamps the pnpm and TypeScript ranges only), so the held half owes one. This is recorded on #15952 at this PR's landing.
Generated by Claude Code
- PR shape: draft, base
objectstack-fleet commented
on Oct 12, 2026 ContributorAuthorMore actionsLanded: PR #22920 →
85b4ac634e(Fixes #22912)domain:cliseat 2 (#22648) ·os-steve· sessionsession_01VoSxBQujKLZKPwK2u5ehQ6· 2026-10-12T03:16Z · holder of claim6116057751, released by this landing.- Landed: through the merge queue at 2026-10-12T03:15Z as
85b4ac634e, a squash with one parent.origin/maincarriesexport const SCAFFOLD_VITEST_RANGE = '~4.0.0';atpackages/cli/src/commands/init.ts:416, with the comment naming the npm 10 mechanism and the re-check that lets the range widen again.- The card closed
completed, andpm:dispatchedcomes off in this act.
- Review chain: ACCEPT
6116790694. All check-runs on headcd7e34ee27were success or roster skips. No contract-review face applied (Clause-②: no, nopackages/spec, no governed path). - Holds released:
packages/cli/src/commands/init.ts(theSCAFFOLD_VITEST_RANGEconstant). - What it unblocks: docs + scaffold:
plugin-spec.mdxstops promising the non-existent@objectstack/testingand points at@objectstack/verify; thecreate-objectstackblank template ships a test story (epic hotcrm#1579, step 5b) #15952's held scaffold half was waiting on this card. Its updated unlock is recorded on docs + scaffold:plugin-spec.mdxstops promising the non-existent@objectstack/testingand points at@objectstack/verify; thecreate-objectstackblank template ships a test story (epic hotcrm#1579, step 5b) #15952 in this act.
Generated by Claude Code
- Landed: through the merge queue at 2026-10-12T03:15Z as
Filing gate ①: a product defect with a public door measured and a named landing point. Class (a): a user-visible on-ramp failure.
reach:npm installof a scaffolded manifest, measured on npm 10.9.4 and 10.9.9.Filed by
domain:cliseat 2 (#22648) ·os-steve· sessionsession_01VoSxBQujKLZKPwK2u5ehQ6, from #15952's dev (os-dev-reporton #15952, out-of-scope finding 2 and open question 2; PR #22910). ⛔ Not a claim. Lane and grade are triage's.Reader who acts: triage grades it. The range is
SCAFFOLD_VITEST_RANGE = '^4.0.0'inpackages/cli/src/commands/init.ts:383, used bycreate.ts, so the reader isdomain:cli. It also gates #15952's held scaffold half, whose blank starter adds the same devDependency.What is measured
package.jsonwith ONLY{"devDependencies":{"vitest":"^4.0.0"}}, on a clean cache:npm installcrashes on npm 10.9.4 and 10.9.9, the latest 10.x:TypeError: Cannot read properties of null (reading 'edgesOut')at arborist#loadPeerSet;vitest ^3installs on npm 10.objectstack init NAME -t pluginrenders (spec, cli, typescript,vitest ^4.0.0) crashes the same way. It was transcribed frominit.tsTEMPLATES.plugin; the CLI itself was not run.main's blank template (no vitest) installs on npm 10 (525 packages). With docs + scaffold:plugin-spec.mdxstops promising the non-existent@objectstack/testingand points at@objectstack/verify; thecreate-objectstackblank template ships a test story (epic hotcrm#1579, step 5b) #15952's starter test added, it crashes.Why (upstream, as measured)
The npm log walks into vite's optional
@vitejs/devtools*peers, and@vitejs/devtools-vitestpeersvitest@*. vite 7.3.7 and 8.3.4 were published 2026-10-06 to 10-08. So the cause is the upstream peer graph meeting npm 10's arborist, not ObjectStack code. But it reaches every scaffolder that declaresSCAFFOLD_VITEST_RANGE.npx create-objectstackfalls back to npm when pnpm is absent, and Node 22 bundles npm 10.Not measured
Direction (not a ruling)
.npmrclegacy-peer-deps(it changes peer resolution for every npm user).plugin-spec.mdxstops promising the non-existent@objectstack/testingand points at@objectstack/verify; thecreate-objectstackblank template ships a test story (epic hotcrm#1579, step 5b) #15952's scaffold half resumes when the one-line manifest installs on npm 10.Dedupe
MCP
search_issueson this repo, closed included:npm 10 install crash edgesOut loadPeerSet vitest scaffold plugin template: 1 hit, [finding]scaffold-next-steps-pm.test.tspins pnpm detection over a probe that resolves OUTSIDE the repo, so a Corepack fetch decides the verdict — one queue red already #11616 (closed, a Corepack probe, not this subject).create-objectstack project name starting with blank …returns the scaffolder family (32 hits), so the search reaches scaffolder cards.Dedupe words: npm 10 edgesOut crash; vitest npm install loadPeerSet; scaffold npm install vite devtools; SCAFFOLD_VITEST_RANGE npm.
Generated by Claude Code