Skip to content

Commit faed589

Browse files
baozhoutaoclaude
andauthored
fix(devx): break the one workspace manifest cycle that makes pnpm --filter 'PKG^...' build unorderable (#14197)
* fix(devx): break the workspace manifest cycle that makes `pnpm --filter '<pkg>^...' build` unorderable `@objectstack/driver-turso` carried `@objectstack/verify` as a devDependency for one test file. That closed a heterogeneous three-class cycle: runtime --peerDependencies(optional)--> driver-turso driver-turso --devDependencies-------------> verify verify --dependencies---------------> runtime pnpm walks all four declaration classes when computing a `PKG^...` selection, so the cycle left those selections with no topological order. pnpm does not refuse a cyclic selection: it schedules the members concurrently, and verify's DTS leg reads a sibling's dist while that sibling is still emitting it. The run dies with TS2307/TS7016 naming a module the author never imported. Measured over all 78 workspace manifests, this was the ONLY single edge whose removal makes the whole graph acyclic. The test moved to packages/qa/dogfood, which is already where this repo keeps @objectstack/verify-based cross-package conformance for driver-sql and driver-sqlite-wasm. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV * keep the relocated Turso parity suite a SOURCE pin, and shrink the two stale registries Moving date-bucket-parity into packages/qa/dogfood changed how TursoDriver resolves: `./turso-driver.js` (source) became a bare `@objectstack/driver-turso` specifier, which resolves through the package's exports map to dist. Two gates caught it, and both refuse the widen-the-registry repair: check:test-source-alias -> anchored resolve.alias in vitest.config.ts check:type-source-resolution -> a bare-name `paths` rule in tsconfig.json Both registries are shrink-only and both carried a now-stale `@objectstack/verify` under @objectstack/driver-turso; the driver's only importer of it was the test that moved. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent c41b42e commit faed589

9 files changed

Lines changed: 136 additions & 8 deletions

File tree

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
---
2+
"@objectstack/driver-turso": patch
3+
---
4+
5+
chore(driver-turso): drop the `@objectstack/verify` devDependency — the one edge that made this workspace's manifest graph cyclic (#13513)
6+
7+
No runtime, API or type change: `dist` is byte-identical, and a consumer never
8+
installs a devDependency. What changes is the workspace's own build graph.
9+
10+
**The defect.** `@objectstack/driver-turso` carried `@objectstack/verify` as a
11+
devDependency so that one test file — `src/date-bucket-parity.test.ts` — could
12+
import `checkDateBucketParity`. That closed a heterogeneous cycle across three
13+
different declaration classes:
14+
15+
```
16+
@objectstack/runtime --peerDependencies(optional)--> @objectstack/driver-turso
17+
@objectstack/driver-turso --devDependencies-----------> @objectstack/verify
18+
@objectstack/verify --dependencies--------------> @objectstack/runtime
19+
```
20+
21+
pnpm walks all four declaration classes when it computes a `PKG^...` / `PKG...`
22+
selection, so the cycle left those selections with no topological order. pnpm
23+
does not refuse a cyclic selection — it schedules the members **concurrently**,
24+
so `@objectstack/verify`'s DTS leg reads a sibling's `dist` while that sibling
25+
is still emitting it. The run then dies with `TS2307`/`TS7016` naming a module
26+
the author never imported, in a package the author never touched, which reads
27+
exactly like "your branch broke an import". Seven seats paid for that
28+
misattribution on unmodified trees. `pnpm install` had been printing
29+
`WARN There are cyclic workspace dependencies` on every install throughout.
30+
31+
**Why this edge.** Measured over all 78 workspace manifests: this was the
32+
**only single edge** whose removal makes the whole graph acyclic. Cutting the
33+
`runtime → driver-turso` peer edge instead is not sufficient on its own — a
34+
second optional-peer edge (`service-datasource → driver-turso`) closes the same
35+
loop through `plugin-auth → rest → service-datasource`.
36+
37+
**Where the test went.** `date-bucket-parity.test.ts` moved to
38+
`packages/qa/dogfood/test/date-bucket-parity-turso.test.ts`, which is where this
39+
repo already keeps `@objectstack/verify`-based cross-package conformance —
40+
`date-bucket-parity-conformance.test.ts` next door runs the same
41+
`checkDateBucketParity` over `driver-sql` and `driver-sqlite-wasm`, both of them
42+
`@objectstack/dogfood` devDependencies for exactly this reason. All five cases
43+
moved intact and all five still run by name, negative control included.

packages/drivers/driver-turso/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@
4545
}
4646
},
4747
"devDependencies": {
48-
"@objectstack/verify": "workspace:*",
4948
"@types/node": "^26.2.0",
5049
"better-sqlite3": "^13.0.3",
5150
"typescript": "^6.0.3",

packages/qa/dogfood/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"@objectstack/core": "workspace:*",
3939
"@objectstack/driver-sql": "workspace:*",
4040
"@objectstack/driver-sqlite-wasm": "workspace:*",
41+
"@objectstack/driver-turso": "workspace:*",
4142
"@types/node": "^26.2.0",
4243
"typescript": "^6.0.3",
4344
"vitest": "^4.1.10"

packages/drivers/driver-turso/src/date-bucket-parity.test.ts renamed to packages/qa/dogfood/test/date-bucket-parity-turso.test.ts

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,48 @@
2323
*
2424
* What each mode is worth is stated explicitly below, because the honest answer
2525
* differs per mode and a vacuous pass must not read as coverage.
26+
*
27+
* ## Why this suite lives in `packages/qa/dogfood` and not in the driver (#13513)
28+
*
29+
* It used to be `packages/drivers/driver-turso/src/date-bucket-parity.test.ts`,
30+
* and the `@objectstack/verify` devDependency it needed was the ONE edge that
31+
* made this workspace's manifest graph cyclic:
32+
*
33+
* runtime --peerDependencies(optional)--> driver-turso
34+
* driver-turso --devDependencies--------> verify
35+
* verify --dependencies-----------------> runtime
36+
*
37+
* pnpm walks all four declaration classes when it computes a `PKG^...` /
38+
* `PKG...` closure, so that cycle left `pnpm --filter '<pkg>^...' build` with no
39+
* topological order to build in. pnpm does not refuse a cyclic selection — it
40+
* schedules the members CONCURRENTLY, so `verify`'s DTS leg reads a `dist` that
41+
* a sibling is still emitting, and the run dies with `TS2307`/`TS7016` naming a
42+
* module the author never touched. Seven seats paid for that misattribution on
43+
* unmodified trees before it was traced. Measured on 78 workspace manifests,
44+
* this was the ONLY single edge whose removal makes the whole graph acyclic.
45+
*
46+
* Nothing about this suite wanted to live in the driver: the repo's convention
47+
* for `@objectstack/verify`-based cross-package conformance is already this
48+
* package — `date-bucket-parity-conformance.test.ts` next door runs the very
49+
* same `checkDateBucketParity` over `driver-sql` and `driver-sqlite-wasm`, both
50+
* of which are `@objectstack/dogfood` devDependencies for exactly this reason.
51+
* TursoDriver was the outlier, and it is the outlier that closed the loop.
52+
*
53+
* ⭐ The move is semantics-preserving on the one axis that could have changed
54+
* silently. In its old home this suite imported `./turso-driver.js` — the
55+
* driver's SOURCE — so its verdict was about the checkout. A bare
56+
* `@objectstack/driver-turso` specifier would instead resolve through the
57+
* package's `exports` map to the BUILT `dist`, turning a source pin into a
58+
* verdict about the last `pnpm build`. Two declarations keep it a source pin,
59+
* and each is enforced by its own gate: an anchored `resolve.alias` entry in
60+
* this package's `vitest.config.ts` (`check:test-source-alias`) and a `paths`
61+
* rule in its `tsconfig.json` (`check:type-source-resolution`). Both carry the
62+
* reasoning at the site.
2663
*/
2764

2865
import { describe, it, expect } from 'vitest';
2966
import { checkDateBucketParity } from '@objectstack/verify';
30-
import { TursoDriver } from './turso-driver.js';
67+
import { TursoDriver } from '@objectstack/driver-turso';
3168

3269
describe('TursoDriver date-bucket parity (framework#3773)', () => {
3370
describe('local mode — the real check', () => {

packages/qa/dogfood/tsconfig.json

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,29 @@
1616
// package-internal on purpose (the guard's data, not public API), so
1717
// there is no entry point to import them from. Every other ledger guard
1818
// in the repo compiles them as relative sources for the same reason.
19-
"rootDir": "../../.."
19+
"rootDir": "../../..",
20+
// [#13513] `test/date-bucket-parity-turso.test.ts` imports `TursoDriver` as
21+
// a VALUE and drives it. Without this rule tsc resolves the specifier
22+
// through the package's `exports` map — `dist/index.d.ts`, A BUILD ARTIFACT
23+
// — so this suite's type verdict would be about the last `pnpm build`
24+
// rather than about the driver source in the checkout, which is exactly
25+
// what `check:type-source-resolution` refuses.
26+
//
27+
// The suite used to live inside `packages/drivers/driver-turso` and resolve
28+
// the class through a relative source import; this rule is what keeps that
29+
// property across the move. The move itself is #13513's cycle cut — the
30+
// driver's `@objectstack/verify` devDependency was the one edge that made
31+
// this workspace's manifest graph cyclic — not a change of intent about
32+
// what the suite measures.
33+
//
34+
// ONE bare-name rule, no star: this package's `exports` map carries only
35+
// `"."`, and a `paths` target matching nothing on disk is worse than absent
36+
// — tsc falls back to node resolution, i.e. to `dist`, silently. `rootDir`
37+
// already spans the repo root, so pulling the driver's source into this
38+
// program needs no widening.
39+
"paths": {
40+
"@objectstack/driver-turso": ["../../drivers/driver-turso/src/index.ts"]
41+
}
2042
},
2143
"include": ["test/**/*"],
2244
"exclude": ["node_modules"]

packages/qa/dogfood/vitest.config.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,25 @@ export default defineConfig({
6969
// Aliasing is graph-wide, so packages still loaded from dist (objectql,
7070
// plugin-security, …) resolve their own `@objectstack/metadata-core`
7171
// imports to this same single source instance rather than a second copy.
72+
// [#13513] `date-bucket-parity-turso.test.ts` drives `TursoDriver` itself —
73+
// it asserts the driver's SQL date bucketing against the in-memory
74+
// reference, plus the on-disk storage form SqlDriver writes. That verdict
75+
// has to be about the driver source in this checkout, not about the last
76+
// `pnpm build`, and it was: the suite used to live inside
77+
// `packages/drivers/driver-turso` and imported `./turso-driver.js`
78+
// relatively. It moved here because the `@objectstack/verify` devDependency
79+
// it needs was the one edge that made this workspace's manifest graph
80+
// cyclic; this alias is what keeps the move semantics-preserving rather than
81+
// quietly converting a source pin into a dist pin.
7282
alias: [
7383
{
7484
find: /^@objectstack\/metadata-core$/,
7585
replacement: path.resolve(__dirname, '../../metadata-core/src/index.ts'),
7686
},
87+
{
88+
find: /^@objectstack\/driver-turso$/,
89+
replacement: path.resolve(__dirname, '../../drivers/driver-turso/src/index.ts'),
90+
},
7791
],
7892
},
7993
test: {

pnpm-lock.yaml

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

scripts/check-test-source-alias.mjs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -383,8 +383,14 @@ const KNOWN_UNALIASED_TEST_IMPORTS = {
383383
'@objectstack/driver-sqlite-wasm': [
384384
'@objectstack/core', '@objectstack/driver-sql', '@objectstack/formula', '@objectstack/spec',
385385
],
386+
// [#13513] `@objectstack/verify` shrank out of this entry when
387+
// `src/date-bucket-parity.test.ts` moved to `packages/qa/dogfood`. That test
388+
// was the driver's only importer of it, and its devDependency was the one
389+
// edge that made this workspace's manifest graph cyclic
390+
// (runtime -peer(optional)-> driver-turso -dev-> verify -dep-> runtime), which
391+
// left every `pnpm --filter 'PKG^...' build` with no topological order.
386392
'@objectstack/driver-turso': [
387-
'@objectstack/core', '@objectstack/driver-sql', '@objectstack/spec', '@objectstack/verify',
393+
'@objectstack/core', '@objectstack/driver-sql', '@objectstack/spec',
388394
],
389395
'@objectstack/example-crm': ['@objectstack/driver-sql', '@objectstack/objectql', '@objectstack/spec'],
390396
'@objectstack/example-embed-objectql': [

scripts/check-type-source-resolution.mjs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,8 +280,14 @@ const KNOWN_DIST_RESOLVED_TYPE_IMPORTS = {
280280
'@objectstack/driver-sqlite-wasm': [
281281
'@objectstack/core', '@objectstack/driver-sql', '@objectstack/formula', '@objectstack/spec',
282282
],
283+
// [#13513] `@objectstack/verify` shrank out of this entry when
284+
// `src/date-bucket-parity.test.ts` moved to `packages/qa/dogfood`. That test
285+
// was the driver's only importer of it, and its devDependency was the one
286+
// edge that made this workspace's manifest graph cyclic
287+
// (runtime -peer(optional)-> driver-turso -dev-> verify -dep-> runtime), which
288+
// left every `pnpm --filter 'PKG^...' build` with no topological order.
283289
'@objectstack/driver-turso': [
284-
'@objectstack/core', '@objectstack/driver-sql', '@objectstack/spec', '@objectstack/verify',
290+
'@objectstack/core', '@objectstack/driver-sql', '@objectstack/spec',
285291
],
286292
'@objectstack/embedder-openai': ['@objectstack/spec'],
287293
'@objectstack/example-crm': ['@objectstack/driver-sql', '@objectstack/objectql', '@objectstack/spec'],

0 commit comments

Comments
 (0)