Skip to content

fix(create-objectstack): scaffolder "Next steps" names the pm it actually used - #11013

Merged
os-elon merged 2 commits into
mainfrom
claude/issue-10322-scaffold-run-next-guidance
Aug 22, 2026
Merged

fix(create-objectstack): scaffolder "Next steps" names the pm it actually used#11013
os-elon merged 2 commits into
mainfrom
claude/issue-10322-scaffold-run-next-guidance

Conversation

@os-elon

Copy link
Copy Markdown
Collaborator

Fixes#10322

Premise check (Prime Directive #6)

Reproduced the "three different answers" state at the branch point (origin/main @ 434af3038):

SurfaceSaid
root README.md:49 (cluster A, untouched)npm create objectstack@latest my-app
scaffolder's printed "Next steps" (index.ts)hardcoded npm run dev / npm run validate
generated blank/README.mdpnpm install / pnpm dev

Still reproducible — confirmed by running the real, repo-built CLI (below). One part of the issue's premise does not hold at HEAD, though: "the generated README never mentions npm run validate" is false — blank/README.md's "Verify your changes" section has named pnpm validate since 7cf283a3fc (2026-06-22, well before this issue was filed). The real gap is narrower: that mention sits mid-document, not in the "Getting started" section a newcomer reads first — so I fixed that gap rather than treating the section as missing.

Part 1 — which package manager, re-derived at HEAD

detectPackageManager() (packages/create-objectstack/src/index.ts) still tries pnpm --version first and falls back to npm only when pnpm is unreachable — confirmed unchanged, matching triage's evidence.

Part 2 — is the install actually redundant? (measured, not assumed)

Built the scaffolder from repo dist and ran it for real, twice:

  • pnpm on PATH (this container's default): Done in 10.5s using pnpm v10.33.0, pnpm-lock.yaml written, node_modules/ populated — a real install, not a skip.
  • pnpm made unreachable (PATH pointed at a dir with only node+npm): fell back to npm install, package-lock.json written instead.

So the install is conditional — skippable via --skip-install, and on a real failure the scaffolder already tells the user to install manually. Deleting the pnpm install line from the generated README's "Getting started" section outright would strand exactly those two cases, and the README is also the project's durable, standalone doc (read again on a future re-clone, by a teammate who never ran the scaffolder at all) — re-running pnpm install when deps already exist is a harmless no-op. So I left that line in place; this is a stop-and-report per the dispatch brief, not a quiet deletion.

What was a real, fixable bug on this axis: the scaffolder's own dynamic output (its printed "Next steps" and the install-failure remedy) hardcoded npm regardless of which package manager the run had just used — so a user who watched pnpm install run was then told npm run dev. That's the code fix below.

The fix

packages/create-objectstack/src/index.ts: detect the package manager once, up front (a read-only <pm> --version probe — costs nothing even under --skip-install), and reuse that single value for the install command, the install-failure remedy, and every line of "Next steps". Verified against the rebuilt CLI in both branches:

# pnpm reachable
Next steps:
cd final-app
pnpm run dev # Start development server
pnpm run validate # Verify metadata: schema + predicates + bindings
# (run after every metadata edit — see AGENTS.md)
# pnpm unreachable (faked PATH)
Next steps:
cd my-app-npm
npm run dev # Start development server
npm run validate # Verify metadata: schema + predicates + bindings
# (run after every metadata edit — see AGENTS.md)

packages/create-objectstack/src/templates/blank/README.md: added one pointer sentence to pnpm validate right after the "Getting started" quickstart block (before the reader moves on to the REST/Console/MCP sections), linking to the existing fuller "Verify your changes" explanation rather than duplicating it.

Tests

Both index.ts and the README are exercised by new pins (packages/create-objectstack/src/):

  • scaffold-next-steps-pm.test.ts — runs the real CLI end-to-end via tsx (same no-build subprocess pattern as scaffold-description.test.ts; index.ts calls program.parse() at import time, so it can't be unit-tested directly), controlling PATH to exercise both the pnpm and the npm-fallback branch, plus a source-text pin on the install-failure remedy.
  • blank-readme-validate-disclosure.test.ts — the README is a static template file (only its H1 is rewritten at scaffold time, everything else copied byte-for-byte), so there's no code path to unit-test; this is a source-text pin, following the existing pattern in blank-template-console-disclosure.test.ts.

Reverse-verified: checked out origin/main's pre-fix index.ts/README.md on top of the new tests — 3 tests went red (exactly the ones exercising the fix) — then restored the committed fix and confirmed all 110 tests green again.

pnpm --filter create-objectstack typecheck → exit 0
pnpm --filter create-objectstack build → exit 0 (rebuilt before every check below)
pnpm --filter create-objectstack test → Test Files 11 passed (11), Tests 110 passed (110)

Gates (derived at final commit 8029fc20a0, via node scripts/pm/dispatch-gates.mjs)

Local gates the change surface matched, all green (verdict lines quoted, not bare exit codes):

  • pnpm check:changeset-gate-self-tests✓ ... all checks passed (both self-tests)
  • pnpm check:objectui-changeset✓ objectui-changeset-digest --self-test: all checks passed / ✓ objectui-range --self-test: all checks passed
  • pnpm check:slot-lookup✓ slot-lookup ratchet holds: 107 unswept site(s) ..., none new
  • pnpm check:template-version-sync✓ sync-template-versions --self-test: 40 assertions ...
  • pnpm check:test-source-aliascheck-test-source-alias OK — 72 packages ... scanned
  • pnpm check:type-source-resolutioncheck-type-source-resolution OK — 77 packages ... scanned
  • node scripts/check-adr-0087-registration.mjs✓ this PR adds no declared-breaking changeset
  • node scripts/check-changeset-no-major.mjs✓ This diff introduces no \major` bump.`
  • node scripts/check-ci-filter-parity.mjsOK: all 82 declared cross-package glob(s) ... covered
  • node scripts/check-empty-changeset.mjs✓ No empty-frontmatter changeset introduced
  • node scripts/check-plugin-teardown-shape.mjs✓ ... 0 known-unreached, SHRINK-ONLY, baseline fully burned down
  • node scripts/docs-audit/check-affected-docs.mjs✓ affected-docs self-test: 339 cases pass (exit 0)

Convention-triggered (this PR adds test files):

  • pnpm check:query-options-erasure, check:engine-double-contract (OK — 377 pinned, 133 in the DEBT ledger, 2 exempt), check:where-matcher (✓ ... 0 silently-wrong and 0 unjudged ..., none new) — all green.
  • pnpm check:type-check-coverage / check:type-check-debt — built the full workspace closure first (pnpm exec turbo run build --filter='./packages/*' --filter='./packages/*/*', 70/70 tasks successful), then check-type-check-coverage --re-measure: OK — 33 ledger entr(ies) re-measured ..., none above its recorded number. (One unrelated informational note: @objectstack/plugin-auth's TEST_DEBT ceiling has 12 spare raw errors it isn't touching — a pre-existing, unrelated ledger entry that could be lowered, not a regression from this diff.)

Scaffold-specific gates named in the brief:

  • Scaffold with repo dist (scaffold-e2e.yml) — path-triggered by packages/create-objectstack/**, so it will run in CI; it's explicitly not a locally-invocable check family (dispatch-gates: no-check-families in the workflow's own header) since it installs the generated project against the real npm registry and drives Docker. Ran its local-equivalent step by hand against the rebuilt CLI (node bin/create-objectstack.js e2e-app --skip-install --skip-skills) — succeeds, generates a valid project; the workflow's later steps (npm install, npm run validate, npm run build, Docker) never parse the CLI's stdout text, so this diff (console text + an additive README paragraph) can't affect them.
  • Validate Package Dependencies (validate-deps.yml) — checked its pull_request.paths filter: **/package.json, pnpm-lock.yaml, .changeset/config.json, pnpm-workspace.yaml, plus its own gate scripts. This PR touches none of those, so the job is not triggered by this diff.

Out of scope (left untouched, on purpose)

Changeset

.changeset/scaffold-next-steps-package-manager.mdcreate-objectstack: patch.

Generated by Claude Code


Generated by Claude Code

…ly used
npm vs pnpm vs `npm run`: a newcomer got three different answers to "what do
I run next" — the scaffolder's own printed "Next steps" hardcoded `npm run
dev` / `npm run validate` regardless of which package manager the run
actually installed with, while the generated blank/README.md consistently
said pnpm.
Read at HEAD: `detectPackageManager()` (packages/create-objectstack/src/index.ts)
still prefers pnpm and falls back to npm only when pnpm is unreachable —
confirmed still the tool's real behaviour, matching triage's evidence. Also
measured empirically with the built CLI: a real run with pnpm on PATH
installs with pnpm (pnpm-lock.yaml, "Done in ... using pnpm vX") and then
printed `npm run dev` / `npm run validate` as next steps — the exact
three-answers defect. A second real run with pnpm made unreachable (PATH
without it) correctly fell back to npm and produced package-lock.json,
confirming the fallback is real and must not be stranded by deleting the npm
path outright.
Fix: detect the package manager once, up front (a read-only `<pm> --version`
probe, so it costs nothing even under --skip-install), and reuse that one
value for the install command, the install-failure remedy, and every line of
"Next steps" — so the printed guidance always names the tool the run actually
used, in both the pnpm and the npm-fallback case. Pinned end-to-end via tsx
(scaffold-next-steps-pm.test.ts), exercising both branches by controlling
PATH.
Also names `validate` — the step the generated AGENTS.md calls unskippable —
in the blank template's "Getting started" section, not only in its later
"Verify your changes" section, so a newcomer reading top-to-bottom sees it at
first touch. The README is a static template file (only its H1 is rewritten
at scaffold time), so this is pinned as a source-text assertion
(blank-readme-validate-disclosure.test.ts) rather than a runtime test.
Root README.md's `npm create objectstack@latest` (cluster A) and the
scaffolder's `◆ Create ObjectStack v6.x` banner (#10325, shared index.ts,
still open) are deliberately untouched — out of this card's scope.
Fixes#10322
Claude-Session: https://claude.ai/code/session_019bmVFqoQPq63zhKrxdYG1r
@github-actions

github-actionsBot commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

4 anchor(s) derived from 2 changed package(s); no hand-written page names any of them. ✅

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/create-objectstack/src/templates/blank/README.md) — 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 45 of 221 client-bound route-ledger rows — the other 176 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run: node scripts/docs-audit/affected-docs.mjs --bridge-coverage

Coarse fallback — 8 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 f334d662ef3c2543d6add14b9140ab9350041cb2packageMentionDocs.

Which tree this was computed on

This run read content/docs from fe9e1d264fb1512d604f04e07a740e11173e5932 — the merge of head af84683da6c21d37254c62a0b82af692bdedd0ca into base f334d662ef3c2543d6add14b9140ab9350041cb2, 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 fe9e1d264fb1512d604f04e07a740e11173e5932 && git checkout fe9e1d264fb1512d604f04e07a740e11173e5932
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin f334d662ef3c2543d6add14b9140ab9350041cb2 af84683da6c21d37254c62a0b82af692bdedd0ca && git checkout -B drift-repro f334d662ef3c2543d6add14b9140ab9350041cb2 && git merge --no-ff af84683da6c21d37254c62a0b82af692bdedd0ca
node scripts/docs-audit/affected-docs.mjs --json f334d662ef3c2543d6add14b9140ab9350041cb2

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

…ge roster
The Lint & Repo Gates check:cross-package-test-inputs failure on #10322:
scaffold-next-steps-pm.test.ts's header comment cites
packages/cli/src/commands/init.ts in backticks (contrasting create-objectstack's
new detected-package-manager guidance with init.ts's own, already-threaded
"Next steps"), and the flat literal collector takes quoted repo-relative
paths without parsing comments. Per the roster's own settled precedent
(serve.ts, gen-sdui-manifest.sh, publish-smoke.sh — mentions rather than
reads), the fix is to declare the file, not reword the comment. Mirrors the
entry into turbo.json's create-objectstack#test inputs.
@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 32565262758 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Console Pin Gate — 失败步骤: Build the Console SPA at the pinned objectui SHA

    ✗ Build failed in 6.14s
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

跨 PR 相同签名(24h,按失败测试文件聚合):

  • ⚠️本次没有可用的聚合签名(日志里没有能解析出测试文件名的 FAIL 行)—— 这不是「没有同签名的其他 PR」,是这一轮没测到。跨 PR 聚合本次不可用,请手工比对其他 PR 的同类评论。
  • ⚠️ 24h 评论账本没读完(超过 5 页仍未读到窗口尽头),所以上面的「不同 PR 数」是下界,不是全量。

历史信号:

  • 本 PR 过去 24h 无队列失败记录(首次)。
  • 过去 24h 队列共有 92 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

Merged via the queue into main with commit 8d21f7aAug 22, 2026
34 checks passed
@os-elon
os-elon deleted the claude/issue-10322-scaffold-run-next-guidance branch August 22, 2026 09:44
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[finding] npm vs pnpm vs npm run: a newcomer gets three different answers to "what do I run next", and the generated README omits validate entirely

2 participants

@os-elon@claude