Found by the dev seat implementing #7276 while re-deriving that card's mechanism. Unassigned, unlabelled — recording a measurement, not claiming a priority.
The defect
turbo.json's build task declares:
"outputs": ["dist/**", ".next/**", "build/**", "**/*.tsbuildinfo", "!**/node_modules/**"]
@object-ui/console (apps/console, not private) publishes its entry from the PACKAGE ROOT, not from dist/:
"exports": { ".": { "types": "./plugin.d.ts", "import": "./plugin.js", "default": "./plugin.js" } },
"main": "./plugin.js",
"files": ["dist", "plugin.ts", "plugin.js", "plugin.d.ts", "README.md"]
plugin.js and plugin.d.ts are produced by build:plugin (tsc -p tsconfig.plugin.json), are gitignored (apps/console/.gitignore:16), and match none of the outputs globs. So they are neither hashed as inputs nor stored as outputs: a cache hit for @object-ui/console:build restores dist/ and leaves the package's published entry absent.
Measured
On origin/main 52cac38, two worktrees of the same commit, turbo 2.10.9:
- Worktree A ran a cold
pnpm build — 43 tasks, 3m46.7s. apps/console/plugin.js present.
- Worktree B ran
pnpm build — 43 of 43 cache hits, 272ms, FULL TURBO.
- In worktree B:
apps/console/dist/ fully restored, apps/console/plugin.js does not exist.
pnpm check:node-esm-load in worktree B then reported exactly the line #7276 quoted from a real run:
✗ @object-ui/console: no-build-output — ./plugin.js does not exist after the build
Why this is worth a card
This is the mechanism behind the loud half of #7276, and it outlives that card's fix. #7276 makes the ESM-load gate refuse to grade artifacts another tree produced — that turns the misattributed red into an accurate one, but it does not make the cache entry complete. Anything else that consumes a cached @object-ui/console build still gets a package whose declared entry is missing, and the tell is a no-build-output style failure pointing at a package the author did not touch.
The narrow fix is to declare the two files as outputs — either widening the shared build task in turbo.json, or (smaller blast radius) giving apps/console a package-level turbo.build.outputs that adds plugin.js and plugin.d.ts. Whether any OTHER package publishes from its root should be checked in the same pass rather than assumed: the audit is exports/main targets that do not start with ./dist/.
Not fixed in #7276's PR: that card's declared file surface is the gate script and its test, and turbo.json was explicitly limited there to "only if the fix requires it". It did not.
Refs: #7276 (where this surfaced) · #1760 (the adjacent cache-masking case)
Generated by Claude Code
Found by the dev seat implementing #7276 while re-deriving that card's mechanism. Unassigned, unlabelled — recording a measurement, not claiming a priority.
The defect
turbo.json'sbuildtask declares:@object-ui/console(apps/console, not private) publishes its entry from the PACKAGE ROOT, not fromdist/:plugin.jsandplugin.d.tsare produced bybuild:plugin(tsc -p tsconfig.plugin.json), are gitignored (apps/console/.gitignore:16), and match none of theoutputsglobs. So they are neither hashed as inputs nor stored as outputs: a cache hit for@object-ui/console:buildrestoresdist/and leaves the package's published entry absent.Measured
On
origin/main52cac38, two worktrees of the same commit, turbo 2.10.9:pnpm build— 43 tasks, 3m46.7s.apps/console/plugin.jspresent.pnpm build— 43 of 43 cache hits, 272ms,FULL TURBO.apps/console/dist/fully restored,apps/console/plugin.jsdoes not exist.pnpm check:node-esm-loadin worktree B then reported exactly the line #7276 quoted from a real run:Why this is worth a card
This is the mechanism behind the loud half of #7276, and it outlives that card's fix. #7276 makes the ESM-load gate refuse to grade artifacts another tree produced — that turns the misattributed red into an accurate one, but it does not make the cache entry complete. Anything else that consumes a cached
@object-ui/consolebuild still gets a package whose declared entry is missing, and the tell is ano-build-outputstyle failure pointing at a package the author did not touch.The narrow fix is to declare the two files as outputs — either widening the shared
buildtask inturbo.json, or (smaller blast radius) givingapps/consolea package-levelturbo.build.outputsthat addsplugin.jsandplugin.d.ts. Whether any OTHER package publishes from its root should be checked in the same pass rather than assumed: the audit isexports/maintargets that do not start with./dist/.Not fixed in #7276's PR: that card's declared file surface is the gate script and its test, and
turbo.jsonwas explicitly limited there to "only if the fix requires it". It did not.Refs: #7276 (where this surfaced) · #1760 (the adjacent cache-masking case)
Generated by Claude Code