Observed while building the workspace closure for the option-B acceptance pin (#15004, PR #15039). Filed unassigned and label finding: the consequence class is severe and the observation is exact, but the CAUSE is not established and I could not reproduce it on demand. Triage owns the severity.
What was measured
pnpm --workspace-concurrency=2 --filter '@objectstack/cli^...' build on origin/main 33681eaef, in a fresh worktree with a fresh pnpm install. The build ran the package's own script — rm -rf dist && tsup && node ../../../scripts/check-dts-emitted.mjs — and its last three lines for this package were:
packages/services/service-cluster build: DTS dist/index.d.cts 35.03 KB
packages/services/service-cluster build: DTS dist/index.d.ts 35.03 KB
packages/services/service-cluster build: DTS dist/testing.d.ts 794.00 B
packages/services/service-cluster build: check-dts-emitted: @objectstack/service-cluster - 3/3 declared declaration file(s) present.
packages/services/service-cluster build: Done
The whole closure exited 0.
Some time later in the same session — after a repo-wide pnpm lint, several check:* gate runs and two vitest run invocations, with no further build and no rm — ls packages/services/service-cluster/dist/ held:
index.cjs index.cjs.map index.js index.js.map testing.js testing.js.map
Every .d.ts / .d.cts gone. dist/testing.cjs gone too. The .js / .cjs outputs and their maps survived.
Why that is worse than a build failure
@objectstack/service-cluster is a dependencies entry of @objectstack/runtime, and packages/runtime/src/runtime.ts imports it. Any tsc program that reaches runtime's source then reports:
../runtime/src/runtime.ts(9,8): error TS7016: Could not find a declaration file for module '@objectstack/service-cluster'.
'/…/packages/services/service-cluster/dist/index.js' implicitly has an 'any' type.
That is the LOUD half. The quiet half is the one this repo already has scar tissue about (#7668, #12078, scripts/check-test-source-alias.mjs's header): a package whose declarations are ABSENT makes an importing symbol any, and a suite that reads that symbol can run green against a type it is no longer checking, with nothing in the output saying so. Here it was loud only because noImplicitAny caught the direct import; a consumer reaching it through a re-export would not have been.
Recovered with pnpm --filter @objectstack/service-cluster build, after which the same typecheck reported the expected pre-existing numbers exactly.
What is NOT established, stated so nobody re-derives it as fact
Why it is worth a card even unreproduced
The failure mode is "a package's declarations are absent from a tree that built green". Every downstream type verdict taken in that window is a verdict about a .d.ts that is not there — and the direction that does NOT throw is a suite passing against any. If this is a real race rather than a one-off, it makes any local typecheck reading in this repo conditional on something nobody currently observes.
A cheap first step, if triage wants one: have check-dts-emitted (or a sibling) re-run over the whole closure once at the END of a --filter '<pkg>^...' build, rather than only per-package immediately after that package's tsup. That would convert a silent window into a named failure without needing the cause first.
Observed while building the workspace closure for the option-B acceptance pin (#15004, PR #15039). Filed unassigned and label
finding: the consequence class is severe and the observation is exact, but the CAUSE is not established and I could not reproduce it on demand. Triage owns the severity.What was measured
pnpm --workspace-concurrency=2 --filter '@objectstack/cli^...' buildonorigin/main33681eaef, in a fresh worktree with a freshpnpm install. The build ran the package's own script —rm -rf dist && tsup && node ../../../scripts/check-dts-emitted.mjs— and its last three lines for this package were:The whole closure exited 0.
Some time later in the same session — after a repo-wide
pnpm lint, severalcheck:*gate runs and twovitest runinvocations, with no further build and norm—ls packages/services/service-cluster/dist/held:Every
.d.ts/.d.ctsgone.dist/testing.cjsgone too. The.js/.cjsoutputs and their maps survived.Why that is worse than a build failure
@objectstack/service-clusteris adependenciesentry of@objectstack/runtime, andpackages/runtime/src/runtime.tsimports it. Any tsc program that reaches runtime's source then reports:That is the LOUD half. The quiet half is the one this repo already has scar tissue about (#7668, #12078,
scripts/check-test-source-alias.mjs's header): a package whose declarations are ABSENT makes an importing symbolany, and a suite that reads that symbol can run green against a type it is no longer checking, with nothing in the output saying so. Here it was loud only becausenoImplicitAnycaught the direct import; a consumer reaching it through a re-export would not have been.Recovered with
pnpm --filter @objectstack/service-cluster build, after which the same typecheck reported the expected pre-existing numbers exactly.What is NOT established, stated so nobody re-derives it as fact
rm, and no second build ran. I did not instrument the window, so I cannot name a writer.packages/services/service-cluster/tsup.config.tscarries a long [finding]metadata-coreandservice-clusterstill emit and PUBLISHdist/testing.cjs, which no exports condition points at any more — dead bytes shipped to npm #13013 note about exactly this hazard and setsclean: falsein BOTH halves precisely to stop one half racing the other's writes, with the singlerm -rf disthoisted into the package script. So the documented guard was in place.packages/**/distin the same tree found no other package withindex.jspresent andindex.d.tsabsent.packages/create-objectstackmatched that shape and is a false positive — itsexportsmap declares only./created-summary, so it has noindex.d.tsto emit.check-dts-emittedcould have caught it. It cannot, and that is not a defect in it: Every tsup-built package can report build success having emitted zero declarations — #11907's repair is wired into plugin-auth only, ~50 packages still exposed #12078 wired it to run immediately aftertsup, which is exactly when it ran and exactly when the files were there. Nothing re-checks afterwards.Why it is worth a card even unreproduced
The failure mode is "a package's declarations are absent from a tree that built green". Every downstream type verdict taken in that window is a verdict about a
.d.tsthat is not there — and the direction that does NOT throw is a suite passing againstany. If this is a real race rather than a one-off, it makes any localtypecheckreading in this repo conditional on something nobody currently observes.A cheap first step, if triage wants one: have
check-dts-emitted(or a sibling) re-run over the whole closure once at the END of a--filter '<pkg>^...' build, rather than only per-package immediately after that package'stsup. That would convert a silent window into a named failure without needing the cause first.