Skip to content

fix(create-objectstack,cli): the scaffolded pnpm-workspace.yaml is one comment line per block, and both scaffold paths write the same bytes - #22229

Merged
objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-22162-scaffold-workspace-minimal
Oct 8, 2026
Merged

objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-22162-scaffold-workspace-minimal

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #22162
Clause-②: no

A new project's pnpm-workspace.yaml is now a 21-line settings file. Both scaffold paths, npm create objectstack and objectstack init / a standalone objectstack create, write the same bytes. The four configuration blocks are unchanged, and each now has one comment line naming its reason. The measurements and version history the file used to carry now live in the init.ts docblocks, next to the values they explain.

# Older pnpm needs this key; empty because this project is not a monorepo.
packages: []

# Build-script approvals for pnpm 10.0–10.25, which do not read allowBuilds.
onlyBuiltDependencies:
  - better-sqlite3
  - esbuild

# The same approvals for pnpm 10.26 and later; pnpm 11 reads only this key.
allowBuilds:
  better-sqlite3: true
  esbuild: true

# Upstream peer ranges measured harmless; this only silences the warning.
peerDependencyRules:
  allowedVersions:
    'better-auth>better-sqlite3': '13'
    '@better-auth/core>@better-auth/utils': '0.5.0'
    '@better-auth/oauth-provider>@better-auth/utils': '0.5.0'
    '@better-auth/scim>@better-auth/utils': '0.5.0'
    '@better-auth/sso>@better-auth/utils': '0.5.0'

Readings (on origin/main 7d7943dd, then on this branch's head)

producer before after
create-objectstack blank template 85 lines, 4,700 bytes: 66 comment, 5 blank, 14 config 21 lines, 722 bytes: 4 comment, 3 blank, 14 config
renderPnpmWorkspaceYaml() (os init, standalone os create) 74 lines, 3,762 bytes: 57 comment, 3 blank, 14 config the same 722 bytes (cmp exit 0)
  • H1, the two outputs before: not byte-identical (first difference at line 10, byte 501). The configuration was identical: with comments stripped and blank lines dropped, both sides hash to sha256 prefix 9992ee6451e59720. Both sides still hash to that prefix after the change. os init and os create call the same renderer. The BEFORE render was taken through the real init.ts import; a source-extraction instrument rendered the same bytes (cmp exit 0), and that instrument is what rendered BASE afterwards.

  • H2, the configuration stays as it is: held. Keys, values and order are unchanged. Every comment-stripped configuration assertion in init.test.ts, template-consistency.test.ts and create.test.ts passes without an edit.

  • One PM reading is partly false: the consistency tests did not assert configuration only. scaffold-workspace-consistency.test.ts's second limb, "states the same pnpm version boundary for each key", reads the PROSE. It parsed a # allowBuilds ... definition list out of the header comment and required each producer to name a dotted pnpm version for each approval key. A one-line comment does not have that shape. I kept the assertion and adapted its reader to the one-line shape: it reads the comment line directly above each key, still requires a version from each producer, and still compares the two. That is why the two approval lines name 10.0–10.25 and 10.26.

  • H3, where the rationale went: each fact is now next to the value or test that owns it.

    fact in the old file(s) now lives in
    packages: key: pnpm 9.x and 10.0–10.4 refuse the file without it; not ['.'] renderPnpmWorkspaceYaml docblock and SCAFFOLD_PNPM_RANGE docblock (already there); init.test.ts and template-consistency.test.ts comments (already there)
    which pnpm reads allowBuilds versus onlyBuiltDependencies; pnpm 11's hard error renderPnpmWorkspaceYaml docblock, per-version table (already there)
    better-sqlite3 is the native SQLite driver, driver-sql's optional dependency; esbuild compiles objectstack.config.ts SCAFFOLD_BUILT_DEPENDENCIES docblock (added; checked against driver-sql's optionalDependencies and the CLI's bundle-require loader)
    npm, yarn and bun ignore the file SCAFFOLD_BUILT_DEPENDENCIES docblock (already there)
    better-auth on better-sqlite3: 1.7.1 behavioural, 1.7.2 structural SCAFFOLD_ALLOWED_PEER_VERSIONS docblock (already there)
    the same, re-read 2026-09-11 on 1.7.3 (only the renderer's prose had it) SCAFFOLD_ALLOWED_PEER_VERSIONS docblock (added)
    the four @better-auth/* utils skews, the symbols measured, and their retirement at the pnpm 10.31 floor SCAFFOLD_ALLOWED_PEER_VERSIONS docblock (already there)
    the retired @better-auth/scim>better-call entry and its dates SCAFFOLD_ALLOWED_PEER_VERSIONS docblock and inline note (already there); its absence is pinned in both packages
    the peer rules suppress the report only, and the lockfile is byte-identical SCAFFOLD_ALLOWED_PEER_VERSIONS docblock (already there)

    The renderPnpmWorkspaceYaml docblock now maps each block to the docblock that holds its reasons. The note in template-consistency.test.ts above the workspace blocks points there as well. pkg-utils.ts was named as a candidate home. It only syncs the @objectstack/* ranges and owns none of these facts, so nothing moved there. No docs page was added (the seat's scope cut), and no link was needed.

  • H4, both scaffolders render the same file: they do now, byte for byte, and a new limb holds it (below).

  • H5, size ranking in a scaffolded project: I scaffolded with --skip-install --skip-skills from the BASE build and from this branch's source. Before, the 22 files ranked: README.md 6,491, AGENTS.md 5,514, .github/copilot-instructions.md 5,514 (a byte copy of AGENTS.md), pnpm-workspace.yaml 4,700, objectstack.config.ts 4,032. That is 4th by bytes, and 3rd as the card counted it, with the AGENTS.md copy folded in. After, pnpm-workspace.yaml is 722 bytes and ranks 10th of 22. The scaffolded file is byte-identical to the template in both runs.

Two departures from the card's sketch, both forced by existing pins

  1. Each comment sits on its own line above its key, not inline after it. Every configuration reader in the three suites strips FULL-LINE comments only (^\s*#.*$). An inline packages: [] # ... fails declares it EMPTY (the reader sees [] # ...), and an inline comment on allowBuilds: breaks the ^allowBuilds:\n block readers.
  2. The order stays packages, onlyBuiltDependencies, allowBuilds, peerDependencyRules. The sketch swapped the middle two. The adds no other top-level setting tests pin this order in both packages, and H2 keeps it.

New pins (packages/cli/test/scaffold-workspace-consistency.test.ts)

  • renders the same bytes in both scaffold paths: renderPnpmWorkspaceYaml() equals the template file, byte for byte. This also ends the drift class seen in [finding] The scaffold pnpm-workspace.yaml that objectstack init renders still explains the RETIRED @better-auth/scim>better-call peer rule as if it were live — and both consistency tests strip comments before asserting, so no gate reads it #17093, where prose that nothing compared shipped wrong in one of the two files. There is now one text to keep true.
  • %s carries at most one short comment line per block, run for each producer. Every line containing # must be a full-line comment directly above a top-level key, at most 80 columns wide. Two comment lines in a row break the rule, and so does a comment inside or after a setting. Why a width and not a line cap: a line count alone lets one block spend what the others save, and a one-line rule with no width lets a single line hold a paragraph. The pair bounds the comment to a few hundred bytes. No value in this file can contain # (package names, versions, true), so any # is a comment.
  • The existing limbs (build grants, peer map, per-key version boundary) are unchanged as assertions. When the bytes differ, they still say which declaration differs.

Ablation (one-time; no permanent test file)

Run from the committed fix (e95920e0). Each leg wrote BASE's bytes over one file and checked that the write landed (on-disk blob hash equals the BASE blob, old anchor An explicit EMPTY workspace counted 1, new anchor Older pnpm needs this key counted 0). It then ran the suite and restored with git checkout HEAD -- under an EXIT INT TERM trap. The restore was checked by hash (on-disk blob equals the HEAD blob), with git diff HEAD empty and git status clean. Both subjects resolve to source: the template is read with fs from src/, and the renderer is imported from ../src. No dist sits in the path, so neither leg needed a rebuild. The direction was predicted before each run, and the observed result matched.

leg red green
old template put back byte identity; npx create-objectstack comment budget (66 breaches: 65 lines not directly above a key, plus old line 17 at 81 columns); per-key version boundary (the old template has a blank line above each approval key) grants, peer map, objectstack init budget
old renderer put back byte identity; objectstack init comment budget (54 breaches); per-key version boundary grants, peer map, npx create-objectstack budget

In both legs, the one old comment line that already sat directly above packages: [] was correctly not flagged.

Tests (head e95920e0)

  • pnpm --filter @objectstack/cli exec vitest run --project unit --maxWorkers=2: 261 of 263 files passed and 3848 tests passed (29 skipped). The other 2 files (published-subpath-console.pin, published-subpath-hook-body.pin) refused with "packages/cli is not built", a prerequisite and not a verdict. I built the CLI and re-ran them: 2 files and 29 tests passed. The integration tier is left to CI: the diff touches no integration file and no spawn entry point.
  • pnpm --filter create-objectstack test: 16 files, 249 tests passed.
  • pnpm --filter @objectstack/cli typecheck (tsc --noEmit plus check:test-typecheck over tsconfig.test.json, which covers test/): exit 0, "3 file(s) / 28 error(s) / 6 pinned signature(s) held", unchanged.
  • pnpm --filter create-objectstack typecheck: exit 0. Its include is src, so it covers the edited test.

Gates

All at head e95920e0. I derived the list with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands from the delivered change set (5 paths against merge base 7d7943dd). It is the same 68 commands as the dispatch list. I added pnpm lint and recorded each exit code before any pipe.

  • Reconciliation: dispatch-gates --ran reports "68 derived, 68 run, 0 NOT-MEASURED, 0 UNRUN" and "a DERIVED zero — all 68 recorded an exit code and none of them is 3". pnpm lint is listed as outside the derivation.
  • pnpm lint (eslint . --no-inline-config, the whole repo): exit 0, no findings printed.
  • On the first pass, 4 gates exited 3 with PREREQUISITE NOT MET (unbuilt workspace), which measures nothing: check:dual-build-cjs-loads, check:i18n, check:i18n-coverage and check:i18n-walk-parity. I built the workspace (turbo run build, exit 0) and re-ran them, all exit 0:
    • "106 published require entry point(s) across 66 package(s) load"
    • "OK (9 package(s) — all bundles in sync, no undeclared authoring keys)"
    • "OK (13 config(s), 621 baselined untranslated string(s), none new)"
    • "11 declared group(s), 9 walked, 2 exempted"
  • The dist-reading gates (check:published-files, check:dts-closure, check:lean-entry-closure, check:sourcemap-no-sources-content) were re-run on the fresh build, all exit 0.

Acceptance notes

  • One claim from the old template was not carried over: "Both ship prebuilt binaries, so an unapproved build degrades rather than breaks". It contradicts the renderer's prose and the SCAFFOLD_BUILT_DEPENDENCIES docblock, which both say that without the build better-sqlite3 ships no usable binding and serve fails with "Could not locate the bindings file". I did not re-measure which one is right. Dropping it from the user's file removes the contradiction either way, and the docblock keeps its own measured statement.
  • Facts dropped from the user's file as history only, all still held in source: the @better-auth/scim>better-call retirement and its dates, and the dated re-reads. [finding] The scaffold pnpm-workspace.yaml that objectstack init renders still explains the RETIRED @better-auth/scim>better-call peer rule as if it were live — and both consistency tests strip comments before asserting, so no gate reads it #17093 (closed) and the docblocks keep them.
  • The scaffold-workspace-consistency.test.ts header said the prose was "NOT compared ... on purpose". That design is superseded by the ruling that both paths render one file. The header now says so and gives the reason.
  • origin/main gained 4 commits since 7d7943dd (b8feb550, 35396b5e, 3b493184, 7b926f76). None of them touches packages/cli or packages/create-objectstack, so no merge was taken here, and CI's merge ref covers the joint state.
  • Already-scaffolded projects are untouched. Their longer comments can stay or go; the settings are identical.

Files

  • packages/create-objectstack/src/templates/blank/pnpm-workspace.yaml: the 21-line file.
  • packages/cli/src/commands/init.ts: the renderer emits the same 21 lines; its docblock maps blocks to reasons; SCAFFOLD_BUILT_DEPENDENCIES and SCAFFOLD_ALLOWED_PEER_VERSIONS gain the facts only the old files carried.
  • packages/cli/test/scaffold-workspace-consistency.test.ts: the two new limbs, the one-line version reader, and the header.
  • packages/create-objectstack/src/template-consistency.test.ts: a pointer note to where the reasons live (comment only).
  • .changeset/22162-scaffold-workspace-minimal.md: create-objectstack patch and @objectstack/cli patch (the rendered output of os init / os create changes).

Generated by Claude Code

…e comment line per block, and both scaffold paths write the same bytes

The blank template carried 66 comment lines and the os init / os create
render 57, each explaining measurements and version history the new
project's reader does not decide. Both now emit the same 21-line file: the
four configuration blocks unchanged, each under one comment line naming its
reason. The facts the two files carried that the init.ts docblocks did not
(esbuild's role, better-sqlite3 as driver-sql's optional dependency, the
2026-09-11 better-auth 1.7.3 re-read) move into those docblocks.

scaffold-workspace-consistency.test.ts gains two limbs: the two producers
render the same bytes, and each stays within one comment line per block,
directly above its key, inside 80 columns. Its per-key version-boundary
limb reads the new one-line shape.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RWZbGvPFcRKvUqASZtunCU
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling labels Oct 8, 2026
@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/cli, objectstack-blank, touching 2 documentable anchor(s). ⚠️ 1 changed file(s) yielded no anchor (packages/create-objectstack/src/templates/blank/pnpm-workspace.yaml), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

5 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, read off packages/cli/src/commands/init.ts))
  • content/docs/getting-started/examples.mdx (via os init (command, read off packages/cli/src/commands/init.ts))
  • content/docs/getting-started/your-first-project.mdx (via os init (command, read off packages/cli/src/commands/init.ts))
  • content/docs/plugins/index.mdx (via os init (command, read off packages/cli/src/commands/init.ts))
  • content/docs/protocol/kernel/index.mdx (via os init (command, read off packages/cli/src/commands/init.ts))

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

  • content/docs/releases/v17/17-1.mdx (via os init (command, read off packages/cli/src/commands/init.ts))
  • content/docs/releases/v17/17-4.mdx (via os init (command, read off packages/cli/src/commands/init.ts))
  • content/docs/releases/v17/17-5.mdx (via os init (command, read off packages/cli/src/commands/init.ts))
  • content/docs/releases/v17/17-7.mdx (via os init (command, read off packages/cli/src/commands/init.ts))

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/pnpm-workspace.yaml) — pages documenting those are invisible to this run
  • 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 — 28 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 7b926f76007316ec13d2b17ec4b0a316b94e4d7e → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 139a81292d6bb3a00b5a0d5ce6a39cda4decfac3 — the merge of head e95920e028bf731347515a3a613f3cacd5752175 into base 7b926f76007316ec13d2b17ec4b0a316b94e4d7e, 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 139a81292d6bb3a00b5a0d5ce6a39cda4decfac3 && git checkout 139a81292d6bb3a00b5a0d5ce6a39cda4decfac3
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 7b926f76007316ec13d2b17ec4b0a316b94e4d7e e95920e028bf731347515a3a613f3cacd5752175 && git checkout -B drift-repro 7b926f76007316ec13d2b17ec4b0a316b94e4d7e && git merge --no-ff e95920e028bf731347515a3a613f3cacd5752175

node scripts/docs-audit/affected-docs.mjs --json 7b926f76007316ec13d2b17ec4b0a316b94e4d7e

⚠️ 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 7b926f76007316ec13d2b17ec4b0a316b94e4d7e → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 8, 2026 07:56
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 8, 2026 07:56
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 8, 2026
Merged via the queue into main with commit 31cd210 Oct 8, 2026
39 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-22162-scaffold-workspace-minimal branch October 8, 2026 09:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

2 participants