Skip to content

Commit 80ec71d

Browse files
fix(cli): retire the five stale scaffold peerDependencyRules entries (#25239)
<!-- claude-projects-attribution: {"github_login":"os-sales"} --> _Requested by **Sales** · [project thread](https://claude.ai/code/project/chan_01D5bWFY3rFzgJcytD5gD4Uu?thread=cmsg_01D5bWFY3rFzgJcytD5gD4UuDWJsBGQpadCH8JR2xZQ5re)_ Fixes #25123 Clause-②: no Metadata-protocol: none ## What On the better-auth `1.7.12` pin, all five `SCAFFOLD_ALLOWED_PEER_VERSIONS` entries suppress peer mismatches that no longer exist, so they retire at once (a ledger entry states a fact that holds; no staged grace): | Entry | Reading on `origin/main` 123ff6f | |:---|:---| | `better-auth>better-sqlite3` (`13`) | `better-auth@1.7.12` declares no `better-sqlite3` peer (lockfile `peerDependencies` block and `npm view`). Control leg: `npm view better-auth@1.7.3 peerDependencies` still names `better-sqlite3: ^12.0.0`. | | `@better-auth/core`, `/oauth-provider`, `/scim`, `/sso` each `>@better-auth/utils` (`0.5.0`) | All four at 1.7.12 peer `^0.4.2 \|\| ^0.5.0`; the tree resolves `0.5.0`. | The map is now empty, so both scaffold paths (`objectstack init` and `npx create-objectstack`) stop emitting a `peerDependencyRules` block; `renderPnpmWorkspaceYaml` already omitted it for an empty map. The docblock's better-call reading is corrected to what holds: stable scim peers `better-call` exactly at `1.4.4`, the resolved copy (it read `1.4.0` on the 1.7.3 pin). `allowedVersions` only silences a report, so the resolution a scaffold gets is unchanged. ## Files - `packages/cli/src/commands/init.ts`: map emptied; its docblock rewritten as the retired-entries record; the `renderPnpmWorkspaceYaml` docblock line about the peer block updated. - `packages/cli/test/init.test.ts`: presence pins flipped to per-key absence pins, a set pin (`toEqual({})`), a no-block pin, and a fixture-map pin for the render path; the top-level key list loses `peerDependencyRules`. - `packages/cli/test/better-sqlite3-peer-declaration.pin.test.ts`: the widening-equals-declared-major limb becomes an absence pin; the CLI's own 13.x declaration pins stay. - `packages/cli/test/scaffold-workspace-consistency.test.ts`: drops the non-vacuity guard that required a non-empty peer map (two empty maps are now the intended state; the byte-equality limb still holds both files). - `packages/create-objectstack/src/templates/blank/pnpm-workspace.yaml` + `src/template-consistency.test.ts`: same retirement in the bundled template, which is held byte-equal to the cli renderer. - `pnpm-workspace.yaml` (root): one comment sentence that called these entries' retirement conditions unmet. - `.changeset/25123-scaffold-peer-ledger-retired.md`: `patch` for `@objectstack/cli` and `create-objectstack` (the template ships in `dist/templates`). ## Verification (head 3737254) - Closure build under the verify lock: `pnpm --filter '@objectstack/cli^...' --filter 'create-objectstack^...' --filter @objectstack/cli --filter create-objectstack build` → `VERDICT command-exit 0`. - Targeted: cli `test/init.test.ts`, `test/better-sqlite3-peer-declaration.pin.test.ts`, `test/scaffold-workspace-consistency.test.ts` → 3 files, 63/63; create-objectstack `src/template-consistency.test.ts` → 1 file, 39/39. - cli unit layer (`vitest run --project unit`) → 324 files, 4775/4775; `create-objectstack` `pnpm test` → 18 files, 278/278; `pnpm --filter @objectstack/cli typecheck` and `pnpm --filter create-objectstack typecheck` → exit 0. The integration layer is left to CI (no spawn entry or integration file touched). - Mutation leg (one-shot, via `scripts/ablation-replace.mjs`, restored by `git checkout HEAD`, blob re-verified as `73c71be9f`): re-adding `'better-auth>better-sqlite3': '13'` to the map turns 7 tests red across the three cli files. Tests import `src/`, so no dist leg applies. - `pnpm lint` → exit 0. - `dispatch-gates --commands`: 81 derived; 79 exit 0; `check:dual-build-cjs-loads` and `check:i18n-coverage` exit 3 PREREQUISITE NOT MET (they need dist for packages outside this closure) → NOT MEASURED locally, CI runs them. ## Acceptance notes - The dispatch named `packages/cli/src/commands/init.test.ts`; the pins actually live at `packages/cli/test/init.test.ts`. - The bundled template's package is named `create-objectstack` (unscoped), so the changeset names it that way. --- _Generated by [Claude Code](https://claude.ai/code/session_01CxBKycaiUpA1yGx1XyoGqX)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent 345f0b1 commit 80ec71d

8 files changed

Lines changed: 150 additions & 338 deletions

File tree

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
'@objectstack/cli': patch
3+
'create-objectstack': patch
4+
---
5+
6+
fix(cli): retire the scaffold's five stale `peerDependencyRules` entries on the better-auth 1.7.12 pin
7+
8+
Clause-②: no
9+
Metadata-protocol: none
10+
11+
A project scaffolded by `objectstack init` or `npx create-objectstack` no longer carries a `peerDependencyRules.allowedVersions` block in its `pnpm-workspace.yaml`. All five entries suppressed peer mismatches that better-auth 1.7.12 no longer has: `better-auth` declares no `better-sqlite3` peer, and `@better-auth/core`, `@better-auth/oauth-provider`, `@better-auth/scim` and `@better-auth/sso` each peer `@better-auth/utils` as `^0.4.2 || ^0.5.0`, which the resolved 0.5.0 satisfies. `allowedVersions` only silences a report, so a scaffold resolves the same lockfile as before and its first `pnpm install` stays free of unmet-peer warnings. Projects scaffolded earlier can keep or delete the block; either way nothing changes.

‎packages/cli/src/commands/init.ts‎

Lines changed: 31 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -128,120 +128,38 @@ export const SCAFFOLD_BUILT_DEPENDENCIES = ['better-sqlite3', 'esbuild'];
128128
* states, keyed `<declaring package>><peer>` — pnpm's scoped `allowedVersions`
129129
* spelling, so each entry widens exactly one declaration and nothing else.
130130
*
131-
* Every one of them is reported by `pnpm install` on a brand-new scaffold, and
132-
* none is a real incompatibility. They are declared here because that report is
133-
* the first thing a newcomer sees, on the one screen where they are deciding
134-
* whether this project is solid, and there is nothing they did to cause it.
135-
*
136-
* - `better-auth>better-sqlite3` — better-auth peers `^12.0.0` while the tree
137-
* resolves 13.x. The peer is OPTIONAL and governs one configuration only: a
138-
* raw better-sqlite3 `Database` handed to better-auth's `database` option.
139-
* ObjectStack never does that — `AuthManager.createDatabaseConfig()` passes
140-
* an ObjectQL adapter factory (or `undefined`, better-auth's own in-memory
141-
* adapter). So the upstream range is stale and 13 is right — widening is
142-
* the correct remedy, not pinning our own declaration back to 12.
143-
*
144-
* RE-MEASURED on the pinned 1.7.2 (#16813). The original reading was taken
145-
* on 1.7.1 (commit 675ab574e) and was behavioural: better-auth's own Kysely dialect —
146-
* migrations, sign-up, sign-in, adapter find/update/delete — behaves
147-
* identically on better-sqlite3 13.0.3 and on 12.11.1. 1.7.2 makes that
148-
* structural instead of empirical: of the 464 files in the published
149-
* `better-auth@1.7.2` tarball, exactly ONE names better-sqlite3 —
150-
* `package.json`, i.e. the peer declaration itself. Zero code files
151-
* reference it (positive control: `kysely` names 9). better-auth never
152-
* imports the package; it accepts a `Database` the CALLER constructs and
153-
* hands it to Kysely, and its own sqlite test path uses node's built-in
154-
* `node:sqlite` `DatabaseSync`. There is therefore no better-auth call site
155-
* that could touch an API moved between better-sqlite3 12 and 13 — the
156-
* range is a statement about an instance we never supply. Re-read
157-
* 2026-09-11 on the 1.7.3 pin (the rendered file's prose carried this
158-
* reading until it shrank to one line per block): the published package
159-
* still names better-sqlite3 only in that peer declaration.
160-
*
161-
* ⚠️ TWO CORRECTIONS to what this entry used to say, both measured:
162-
* • the 13.x copy better-auth binds to is `@objectstack/cli`'s OWN
163-
* `optionalDependencies` entry, NOT `@objectstack/driver-sql`'s. On the
164-
* chain that actually reports (`cli` → `runtime` → `plugin-auth` →
165-
* `better-auth`) the CLI is the ancestor, so its copy is the one pnpm
166-
* resolves the peer against — pnpm names it in the warning itself
167-
* ("found 13.0.3 in @objectstack/cli"). Editing driver-sql alone would
168-
* not move this line.
169-
* • pinning the CLI back to `^12` is not a neutral alternative. Measured
170-
* on a bare project depending on `@objectstack/cli@17.3.0`, it clears
171-
* the report only by installing a SECOND native better-sqlite3
172-
* (12.11.1 alongside 13.0.3), and the 12 copy is dead weight — the CLI
173-
* loads better-sqlite3 itself (`src/utils/sqlite-occupancy.ts`) and
174-
* knex resolves 13.x through driver-sql regardless. This
175-
* `allowedVersions` entry clears the same report with the resolution
176-
* byte-identical (0 lines of lockfile diff).
131+
* An entry belongs here only while `pnpm install` on a brand-new scaffold
132+
* reports that skew and the skew is measured harmless: that report is the first
133+
* thing a newcomer sees, on the one screen where they are deciding whether this
134+
* project is solid, and there is nothing they did to cause it. An entry whose
135+
* skew is gone retires at once — a suppression with no skew behind it leaves
136+
* the NEXT real skew pre-silenced.
137+
*
138+
* EMPTY today, so `renderPnpmWorkspaceYaml` emits no `peerDependencyRules`
139+
* block at all. Every entry this map has carried is retired, each read on the
140+
* better-auth `1.7.12` pin off the lockfile's `peerDependencies` blocks and
141+
* the published manifests:
142+
*
143+
* - RETIRED: `better-auth>better-sqlite3` (`'13'`). better-auth used to peer
144+
* an optional `better-sqlite3@^12.0.0` while the tree resolves 13.x (1.7.3
145+
* still declared it). `better-auth@1.7.12` declares no better-sqlite3 peer
146+
* at all, so there is nothing left to widen. The CLI's own 13.x declaration
147+
* is held by `test/better-sqlite3-peer-declaration.pin.test.ts`.
148+
*
149+
* - RETIRED: `@better-auth/core`, `/oauth-provider`, `/scim` and `/sso`, each
150+
* `>@better-auth/utils` (`'0.5.0'`). The four used to peer an EXACT
151+
* `@better-auth/utils@0.4.2` while the tree resolves 0.5.0. At 1.7.12 all
152+
* four peer `^0.4.2 || ^0.5.0`, which the resolved 0.5.0 satisfies.
177153
*
178154
* - RETIRED (#3653): `@better-auth/scim>better-call` — the rc.1-era scim pin
179-
* peered an exact `better-call@1.3.7` against the host's 1.4.0, and this
180-
* map declared that skew away. Stable scim peers `better-call@1.4.0`, so
181-
* the skew is genuinely gone and the entry retired WITH the pin move, as
182-
* its ratchet demanded. Measured on 1.7.1 when #3653 landed; re-read
183-
* 2026-09-12 on `1.7.3`, the pin today, off the installed manifest —
184-
* `peerDependencies['better-call']` is the exact string `1.4.0`.
185-
*
186-
* - `<four>@better-auth/utils` — `@better-auth/core`, `/oauth-provider`,
187-
* `/scim` and `/sso` each peer an EXACT `@better-auth/utils@0.4.2`, while a
188-
* scaffolded tree hands them 0.5.0. The 0.5.0 comes from `better-call@1.4.0`
189-
* (better-auth's own HTTP layer), which DEPENDS on `^0.5.0`;
190-
* `@objectstack/plugin-auth` names the four packages as direct dependencies
191-
* without naming utils, so pnpm satisfies their peer from better-call's copy
192-
* rather than from better-auth's own exact 0.4.2 dependency.
193-
*
194-
* Measured compatible rather than assumed. Those four import exactly three
195-
* symbols across two subpaths — `base64`/`base64Url` (`/base64`),
196-
* `createHash` (`/hash`) and, in core only, `createRandomStringGenerator`
197-
* (`/random`). 0.5.0 exports all three with identical signatures; `/random`
198-
* is unchanged apart from formatting, `/base64` swaps `new Uint8Array(data)`
199-
* for a helper that IS `new Uint8Array(data)` on non-strings, and `/hash`
200-
* only widens its input coercion for views not backed by a plain
201-
* ArrayBuffer. Run against the input shapes those call sites actually pass,
202-
* 0.4.2 and 0.5.0 agree on every value; run end to end (better-auth with the
203-
* sso, oauth-provider and scim plugins), a tree where the four resolve 0.5.0
204-
* and one where they resolve 0.4.2 produce the same transcript — sign-up,
205-
* sign-in, session, both OAuth metadata documents, the RFC 7636 PKCE
206-
* challenge, and the SCIM and SSO endpoint outcomes.
155+
* peered an exact `better-call@1.3.7` against the host's 1.4.0. Stable
156+
* scim peers better-call exactly at the version the tree resolves: the
157+
* string `1.4.4` on the 1.7.12 pin today.
207158
*
208-
* ⛔ A resolution change is the WRONG remedy here, and was measured too:
209-
* pinning utils back to 0.4.2 clears the four lines only by dragging
210-
* `better-call@1.4.0` off its own declared `^0.5.0` — manufacturing one real
211-
* range violation to silence four benign ones.
212-
*
213-
* Spelled `0.5.0` exactly, not `0.5`: 0.5.0 is the version that was
214-
* measured, and a future 0.6.0 SHOULD report again rather than inherit this
215-
* finding.
216-
*
217-
* ⚠️ These four do NOT retire with the SCIM rc pin, even though one of them
218-
* names scim. Stable `@better-auth/scim@1.7.3` still peers
219-
* `@better-auth/utils@0.4.2` EXACTLY — re-read (2026-09-11) off the
220-
* installed package manifest, unchanged from 1.7.1 and 1.7.2 — so this skew
221-
* outlives that pin. They retire
222-
* when the four packages accept 0.5.0 upstream, or when
223-
* `SCAFFOLD_PNPM_RANGE` reaches `>=10.31` — pnpm 10.31 changed peer
224-
* resolution so that all four land on 0.4.2 by themselves. Measured on the
225-
* rendered scaffold, one clean resolve per pnpm version:
226-
*
227-
* pnpm 10.15.0 – 10.30.0 all four reported as unmet peers.
228-
* pnpm >= 10.31.0 resolved to 0.4.2; nothing to report.
229-
*
230-
* `allowedVersions` suppresses the report ONLY; it moves no resolution — the
231-
* lockfile a scaffold resolves is byte-identical with and without this block
232-
* (verified by digest on pnpm 10.15.0 and 10.30.0).
159+
* `allowedVersions` suppresses the report ONLY; it moves no resolution, so a
160+
* retirement leaves the lockfile a scaffold resolves unchanged.
233161
*/
234-
export const SCAFFOLD_ALLOWED_PEER_VERSIONS: Record<string, string> = {
235-
'better-auth>better-sqlite3': '13',
236-
// '@better-auth/scim>better-call' retired with the scim rc pin (#3653) —
237-
// stable scim peers better-call@1.4.0 exactly (re-read 2026-09-12 on the
238-
// 1.7.3 pin), the copy every install already resolves. init.test.ts pins
239-
// its ABSENCE now.
240-
'@better-auth/core>@better-auth/utils': '0.5.0',
241-
'@better-auth/oauth-provider>@better-auth/utils': '0.5.0',
242-
'@better-auth/scim>@better-auth/utils': '0.5.0',
243-
'@better-auth/sso>@better-auth/utils': '0.5.0',
244-
};
162+
export const SCAFFOLD_ALLOWED_PEER_VERSIONS: Record<string, string> = {};
245163

246164
/**
247165
* Lowest pnpm that can actually install this scaffold, declared as
@@ -696,8 +614,9 @@ export function renderScaffoldPackageJson(
696614
* allowBuilds and `SCAFFOLD_BUILT_DEPENDENCIES` (why each
697615
* package needs its build).
698616
* peerDependencyRules `SCAFFOLD_ALLOWED_PEER_VERSIONS`: one measured
699-
* paragraph per entry, its retirement condition,
700-
* and the retired `@better-auth/scim>better-call`.
617+
* paragraph per entry and the retired entries.
618+
* Emitted only while that map is non-empty; it is
619+
* empty today, so the block is absent.
701620
*
702621
* The file carried 57 comment lines here, and 66 in the template, until the
703622
* maintainer asked for it to stay minimal with the rationale moved into the

‎packages/cli/test/better-sqlite3-peer-declaration.pin.test.ts‎

Lines changed: 16 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
/**
44
* **The pin for `@objectstack/cli`'s own `better-sqlite3` declaration**, and
5-
* for its agreement with the scaffold widening that exists because of it
5+
* for the retirement of the scaffold widening that once existed because of it
66
* (#16813).
77
*
88
* ## The report this is about
@@ -49,20 +49,15 @@
4949
* missing was not a different range but a gate holding the range to the
5050
* reasoning that justifies it, which is what this file is.
5151
*
52-
* ## Why the two assertions are one pin and not two
52+
* ## The scaffold widening is retired
5353
*
54-
* `SCAFFOLD_ALLOWED_PEER_VERSIONS['better-auth>better-sqlite3']` widens
55-
* better-auth's peer to a **major**, and the only reason that major is right
56-
* is the declaration below. Held apart, either can move alone and stay green:
57-
* a declaration lifted to `^14` leaves the scaffold suppressing a report it no
58-
* longer matches (so the NEXT real skew arrives pre-silenced), and a
59-
* declaration dropped to `^12` leaves a suppression with no skew behind it —
60-
* the exact shape `init.test.ts` refuses for the retired
61-
* `@better-auth/scim>better-call` entry. So the major is read out of the
62-
* manifest and compared, rather than written down twice.
63-
*
64-
* `init.test.ts` pins the widening's literal value and its rendering; this
65-
* file pins what the widening is ABOUT. Neither restates the other.
54+
* `SCAFFOLD_ALLOWED_PEER_VERSIONS['better-auth>better-sqlite3']` used to widen
55+
* better-auth's peer to this declaration's major, and this file held the two
56+
* equal. `better-auth@1.7.12` declares no better-sqlite3 peer at all, so the
57+
* report above no longer exists and the widening retired with it: a
58+
* suppression with no skew behind it leaves the NEXT real skew pre-silenced.
59+
* This file now pins its absence; the 13.x declaration below stands on the
60+
* readings above on its own.
6661
*
6762
* The manifest read stays inside this package (`test/` → package root), so it
6863
* is not a `check:cross-package-test-inputs` escape and needs no declaration.
@@ -103,28 +98,26 @@ describe('@objectstack/cli better-sqlite3 declaration (#16813)', () => {
10398
expect(manifest.dependencies?.['better-sqlite3']).toBeUndefined();
10499
});
105100

106-
it('keeps the declaration on one major, so the scaffold can widen to it', () => {
101+
it('keeps the declaration on one major, so the retreat check below can read it', () => {
107102
const declared = manifest.optionalDependencies?.['better-sqlite3'] ?? '';
108103
expect(
109104
soleMajor(declared),
110105
`better-sqlite3 is declared "${declared}"; this pin needs a range admitting exactly one major`,
111106
).not.toBeNull();
112107
});
113108

114-
it('widens better-auth\'s peer to the major this package actually declares', () => {
115-
// ⛔ Not "to 13" — that is `init.test.ts`'s assertion. This one says the
116-
// widening and the declaration are the SAME major, so neither can move
117-
// without the other.
118-
const declared = manifest.optionalDependencies?.['better-sqlite3'] ?? '';
119-
expect(SCAFFOLD_ALLOWED_PEER_VERSIONS[WIDENING_KEY]).toBe(soleMajor(declared));
109+
it('carries no scaffold widening for better-auth\'s retired better-sqlite3 peer', () => {
110+
// better-auth@1.7.12 declares no better-sqlite3 peer, so there is nothing
111+
// to widen. Re-adding the entry needs a new measured skew, not a revert.
112+
expect(SCAFFOLD_ALLOWED_PEER_VERSIONS[WIDENING_KEY]).toBeUndefined();
120113
});
121114

122115
it('refuses a retreat to better-sqlite3 12 (the report is not the defect)', () => {
123116
// The failure this guards is a well-meant one: reading the unmet-peer
124117
// report as a defect and "fixing" it here. Measured, that clears the line
125118
// only by installing a second native better-sqlite3 that nothing loads —
126-
// see this file's header for the three readings. If better-auth ever
127-
// WIDENS its peer upstream, the right change is to retire the scaffold
119+
// see this file's header for the three readings. When better-auth dropped
120+
// the peer upstream (1.7.12), the right change was to retire the scaffold
128121
// entry, not to move this declaration.
129122
const declared = manifest.optionalDependencies?.['better-sqlite3'] ?? '';
130123
expect(soleMajor(declared)).not.toBe('12');

0 commit comments

Comments
 (0)