Skip to content

fix(cli): declare a pnpm floor in the scaffolded package.json - #10932

Merged
os-elon merged 1 commit into
mainfrom
claude/issue-10497-scaffold-pnpm-floor
Aug 21, 2026
Merged

fix(cli): declare a pnpm floor in the scaffolded package.json#10932
os-elon merged 1 commit into
mainfrom
claude/issue-10497-scaffold-pnpm-floor

Conversation

@os-elon

Copy link
Copy Markdown
Collaborator

Part of #10497

Both scaffold paths write a settings-only pnpm-workspace.yaml with no packages: key. Early pnpm 10 refuses that file outright — pnpm install exits 1 with ERROR packages field missing or empty before resolving a single dependency, so a brand-new project cannot be installed at all.

Per the triage ruling on #10497, this takes the cheapest sound route: declare a >=10.15 floor so the failing band is refused by the package manager itself, without deciding the packages: [] vs packages: ['.'] workspace-shape question. The rendered pnpm-workspace.yaml is untouched by this PR.

Step 1 was a measurement

Neither scaffold path pinned a floor at head: grep for packageManager across packages/cli/src and packages/create-objectstack/src returns nothing, and the only engines in scaffolded output is engines.protocol (the ADR-0087 handshake in objectstack.config.ts, not a package-manager range). The CLI rendered exactly name, version, private, type, scripts, dependencies, devDependencies. So the floor route was live rather than already satisfied.

Measured behaviour

The card's repro reproduced exactly before any edit (pnpm 10.0.0 exit 1, 10.15.0 exit 0). Then measured on a project scaffolded by the real CLI (objectstack init my-app -t app --no-install), one clean install per pnpm version, each with its own store:

pnpmbeforeafter
10.0.0 – 10.4.0packages field missing or emptyunchanged — see the residue below
10.5.0 – 10.14.0packages field missing or emptyERR_PNPM_UNSUPPORTED_ENGINE — "Your pnpm version is incompatible ... Expected version: >=10.15"
10.15.0installsinstalls (exit 0, zero engine-gate hits)

The 10.0–10.4 boundary was bisected across every minor from 10.1.0 to 10.13.0, not assumed.

The residue is real, and it is the fenced decision

The floor is a diagnosis, not a repair. pnpm 10.0.0–10.4.0 parse pnpm-workspace.yamlbefore they read engines, so they still print the raw workspace error — no floor value changes that, because those versions never reach the engines check on this path. Closing that remaining sliver requires deciding what a single-package scaffold should declare under packages:, which triage routed to a decision card; #10498 and #10499 are queued against that same rendered file and are not touched here.

Why engines.pnpm and not packageManager

Measured both. packageManager: "pnpm@10.15.0" auto-switches and makes 10.5–10.14 succeed rather than refuse, which looks stronger — but it buys nothing on 10.0–10.4 (same raw error), it would declare the project pnpm-only when objectstack init deliberately hands off to npm, yarn or bun via detectPackageManager, and it pins one exact version that goes stale on every pnpm release. npm, yarn and bun all ignore engines.pnpm, so the floor costs them nothing — the same principle the workspace file already states about itself. A test pins the choice so a future edit does not quietly add the stamp.

Tests

Reverse-verified from the committed state. Removing the floor from both paths turns the new assertions red — create-objectstack 1 failed | 101 passed, CLI init 2 failed | 43 passed — and restoring returns 102 passed / 45 passed. Both suites resolve through source, not dist (the CLI test imports ../src/commands/init relatively; the consistency test reads the template with fs.readFileSync), so this ablation needs no rebuild and none was required for it to be valid.

An existing test that re-declared init.ts's package.json inline "to mirror it" now calls the real renderer instead — the hand-copied mirror would have kept passing however far init.ts drifted from it.

Gates

Gate set re-derived from the actual changeset with node scripts/pm/dispatch-gates.mjs (no hand-supplied paths) at d346c39 with a clean tree; all 21 matched and convention-triggered families green, including check-nul-bytes: OK, check-type-check-coverage --re-measure: OK — 33 ledger entr(ies) re-measured ... none above its recorded number (run after building the closure, as lint.yml does), check-engine-double-contract: OK and check:template-version-sync. No threshold moved and no baseline raised.

Also filed while measuring, unrelated cause, not addressed here: #10931 (a scaffolded project still reports four unmet @better-auth/utils peers on first install).


Generated by Claude Code

Both scaffold paths write a settings-only pnpm-workspace.yaml with no
`packages:` key, which early pnpm 10 refuses outright — `pnpm install`
exits 1 with "ERROR packages field missing or empty" before resolving a
single dependency, so a brand-new project could not be installed at all.
Declaring `engines.pnpm: ">=10.15"` makes pnpm report its own version
instead. Measured, one clean install per pnpm version, each with its own
store: 10.5.0-10.14.0 now refuse with ERR_PNPM_UNSUPPORTED_ENGINE naming
the range; 10.0.0-10.4.0 parse the workspace file before reading engines
so they are unchanged; >=10.15.0 installs as before.
engines.pnpm rather than a packageManager stamp: npm, yarn and bun ignore
it, so the scaffold keeps working for all four package managers the CLI
hands off to.
Part of #10497
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019bmVFqoQPq63zhKrxdYG1r
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/cli, objectstack-blank, touching 3 documentable anchor(s).

3 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/deployment/cli.mdx(via os init (command))
  • content/docs/getting-started/examples.mdx(via os init (command))
  • content/docs/getting-started/your-first-project.mdx(via os init (command))

1 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v17.mdx(via os init (command))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/create-objectstack/src/templates/blank/package.json) — pages documenting those are invisible to this run
  • 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 — 23 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 3d7deb70005ce2297b5b1a311b629106c3c39bb8packageMentionDocs.

Which tree this was computed on

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

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

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 3d7deb70005ce2297b5b1a311b629106c3c39bb8 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation dependencies Pull requests that update a dependency file tests tooling labels Aug 21, 2026
@os-elon
os-elon marked this pull request as ready for review August 21, 2026 18:58
@os-elon
os-elon added this pull request to the merge queueAug 21, 2026
Merged via the queue into main with commit 9d101d2Aug 21, 2026
35 checks passed
@os-elon
os-elon deleted the claude/issue-10497-scaffold-pnpm-floor branch August 21, 2026 19:11
@github-actions

Copy link
Copy Markdown
Contributor

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

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

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

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

    ✗ Build failed in 5.91s
    

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

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

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

历史信号:

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

分诊清单:

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

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

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

Labels

dependenciesPull requests that update a dependency filedocumentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@os-elon