test: prove the dist→src rewrite is in effect, and make the dist job total - #778
Merged
sroussey merged 2 commits intoAug 14, 2026
Conversation
`resolveId` — the one function the workspace-source plugin exists for — had no test, and nothing asserted the plugin reaches the generated projects. `workspaceSource.test.ts` covered only the pure helpers, and both CI modes pass under either resolution: `test-vitest-unit` and `test-vitest-dist` run the same files and the same assertions, differing only in which files load, so no suite distinguished "resolved to src" from "resolved to dist". The failure that leaves is silent. Hoisting `plugins: [workspaceSourcePlugin(__dirname)]` from the per-project object to the root `defineConfig` — a natural "one instance instead of N" cleanup, and the exact mistake the comment there warns about — makes every project resolve `@workglow/*` through `exports` to dist again. All tests pass, merge-vitest-coverage succeeds, and the only symptom is entry-point behavior reading as uncovered. The same silence covers a regression inside `resolveId`. Extract the hook body into `resolveWorkspaceSourceId(packages, context, source, importer, options)`, which takes the plugin context as a parameter so a recording stub can stand in for Vite; `workspaceSourcePlugin` becomes a one-line adapter. `WorkspaceResolveContext` declares `resolve` with method syntax on purpose — parameter bivariance is what lets Vite's real `PluginContext` satisfy it — and is generic in the result so the adapter's return type stays `ResolvedId`, which is what Vite's `resolveId` hook is declared to return. Both together are what keep the call site cast-free (checked against the installed vite typings, where the non-generic form needs one). New tests: the rewrite, a non-workspace specifier short-circuiting before `this.resolve`, an external resolution left alone, dist output with no source twin left on the built file, the unresolved-specifier diagnostic (a branch with zero execution anywhere before this), and `skipSelf: true` plus verbatim forwarding of the hook's own options. Plus a "plugin attachment" block that re-imports the real `vitest.config.ts` and asserts every project carries `workglow:workspace-source` under the default target and none does under `dist`. Both directions stub `WORKGLOW_TEST_TARGET` explicitly and unstub afterwards: `test-vitest-dist` runs this file with that variable ambient, so a test reading the ambient value would pass in one CI job and fail in the other.
`test-vitest-dist` is the one blocking job that resolves `@workglow/*` through `exports`, but it runs the UNIT tier only — while the source rewrite applies to EVERY vitest job. The integration/rag/provider suites previously loaded the bundles and now load src, so a bundle reachable only from an `.integration.test.ts` file lost its blocking check. Two entries lost every check: `@workglow/openrouter/ai-runtime` and `@workglow/huggingface-inference/ai-runtime`, imported only from provider-api integration files, whose section the nightly Bun parity run also excludes. Concretely: a `bun build` change dropping `registerOpenRouterInline` from `providers/openrouter/dist/ai-runtime.js` leaves the file in place, satisfies the dist-must-exist requirement, passes all of CI, and breaks consumers only after publish. `PublishedEntryImports.test.ts` makes the check total instead of tier-shaped: it enumerates every workspace manifest's `exports`, resolves each subpath under the Node conditions only (`node`/`import`/`default`, walked in declaration order the way Node does, so `types`/`browser`/`bun` are stepped over rather than entered), and dynamically imports each resulting specifier, asserting the module is non-empty. Under `WORKGLOW_TEST_TARGET=dist` that one unit-tier file loads every published bundle; under the default target it costs nothing, since it loads the same source the rest of the suite already does. Adding `workglow` to `packages/test`'s devDependencies is the larger half: it brings the meta-package's own entries and, transitively, the provider bundles those re-export. The enumeration is local rather than shared with `scripts/lib/sourceStubs`: `stubSpecsFor` returns dist targets rather than import specifiers, and `packages/test` is a `composite` project rooted at `./src`, so importing from `scripts/` would put those files in its program and break `build-types`. Anti-vacuity assertions (over 60 entries across over 20 packages, every target `./dist/**.js`) keep a mis-typed walk from passing as a short list, and both exemption maps are staleness-checked against the enumeration. Two exemptions, each with its reason: `@workglow/cli` (uncheckable — an example app `packages/test` does not depend on, so under isolated linking the specifier does not resolve from here at all) and `workglow/auto-bootstrap` (imported, but exempt from the non-empty assertion: it registers providers as a side effect and exports nothing by design). New packages default to checked.
Coverage Report
File CoverageNo changed files found. |
sroussey
merged commit Aug 14, 2026
05f3b38
into
claude/coverage-dist-bundle-fix-ew0vj8
12 checks passed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Targets
claude/coverage-dist-bundle-fix-ew0vj8(#741), notmain.Two review findings, both the same shape: a guard that does not guard what it claims.
1. Nothing verified the dist→src rewrite is actually in effect
resolveId— the one function the whole plugin exists for — had no test, and nothing asserted the plugin reaches the generated projects.workspaceSource.test.tscovered only the pure helpers. Both CI modes pass under either resolution:test-vitest-unitandtest-vitest-distrun the same files with the same assertions, differing only in which files load, so nothing distinguished "resolved to src" from "resolved to dist".The failure that leaves is silent. Hoisting
plugins: [workspaceSourcePlugin(__dirname)]from the per-project object to the rootdefineConfig— a natural "one instance instead of N" cleanup, and the exact mistake the comment there warns about — makes every project resolve throughexportsto dist again. All tests pass,merge-vitest-coveragesucceeds, and the only symptom is entry-point behavior reading as uncovered.Extraction rather than casts. The hook body moves to
resolveWorkspaceSourceId(packages, context, source, importer, options), which takes the plugin context as a parameter so a recording stub can stand in for Vite;workspaceSourcePluginbecomes a one-line adapter. Two details keep the call site cast-free, and I checked both against the installed vite typings rather than assuming:WorkspaceResolveContext.resolveuses method syntax, so parameter bivariance lets Vite's realPluginContextsatisfy the interface structurally.ResolvedId. Non-generic withexternal?: boolean | string, the adapter fails to typecheck (Type 'string' is not assignable to type '"absolute" | "relative" | boolean | undefined') and would need the cast the extraction exists to avoid.New
describe("resolveId")with a recording stub covers: the dist→src rewrite; a non-workspace specifier short-circuiting without callingresolve; an external resolution left as-is; dist output with no source twin left on the built file; the workspace diagnostic thrown when resolution yields nothing (a branch with zero execution anywhere before this); andskipSelf: trueplus verbatim forwarding of the hook's ownkind/isEntryoptions.New
describe("plugin attachment")re-imports the realvitest.config.ts(withvi.resetModules(), since the config reads the variable at module scope) and asserts every project carriesworkglow:workspace-sourceunder the default target and none does underdist. Both directions stubWORKGLOW_TEST_TARGETexplicitly andvi.unstubAllEnvs()after —test-vitest-distruns this file with that variable ambient, so a test reading the ambient value would pass in one CI job and fail in the other.2. The blocking bundle check was tier-shaped; two entries had no check at all
test-vitest-distrunstest:vitest:unitonly, while the source rewrite applies to every vitest job. The integration/rag/provider suites used to load the bundles and now load src, so a bundle reachable only from an.integration.test.tsfile lost its blocking check. Two lost every check —@workglow/openrouter/ai-runtimeand@workglow/huggingface-inference/ai-runtime, imported only from provider-api integration files whose section the nightly Bun parity run also excludes. Abun buildchange droppingregisterOpenRouterInlinefromproviders/openrouter/dist/ai-runtime.jswould leave the file in place, satisfy the dist-must-exist requirement, pass all of CI, and break consumers only after publish.New
packages/test/src/test/util/PublishedEntryImports.test.tsmakes the check total instead of tier-shaped: it enumerates every workspace manifest'sexports, resolves each subpath under the Node conditions only (walked in declaration order the way Node does, sotypes/browser/bunare stepped over rather than entered), andit.eachdynamically imports each resulting specifier, asserting the module is non-empty. UnderWORKGLOW_TEST_TARGET=distthat one unit-tier file loads every published bundle; under the default target it costs nothing, since it loads the same source the rest of the suite already does.Adding
"workglow": "workspace:*"topackages/test's devDependencies is the larger half — it brings the meta-package's own 28 entries and, transitively, the provider bundles those re-export. Total swept today: 98 entries across 38 packages, 97 imported (@workglow/cliexempt).The review suggested generating this from
stubSpecsFor(manifest). That does not work: it returns dist targets, not import specifiers, andpackages/testis acompositeproject rooted at./src, so importing fromscripts/lib/would put those files in its program and breakbuild-types. The enumeration is therefore local.Anti-vacuity assertions (over 60 entries across over 20 packages, every target matching
^\./dist/.+\.js$) keep a mis-typed walk from passing as a short list, and both exemption maps are staleness-checked against the enumeration. Two exemptions, each with its reason:@workglow/clipackages/testdoes not depend on — under isolated linking the specifier does not resolve from here at allworkglow/auto-bootstrapNew packages default to checked.
3. Docs
The
.claude/CLAUDE.mdparagraph this branch adds now says why the unit-tier dist job suffices — namingPublishedEntryImports.test.tsand the tier-shaped gap it closes — and documents theresolveId/attachment tests alongside it.Verification
WORKGLOW_TEST_TARGET=dist bun run test:vitest:unitagainst a realbun run build: 534 files passed / 3 skipped, 6152 tests passed / 71 skipped, exit 0. No OOM; the sweep adds ~11s. That run is what proves all 97 entries load as bundles, not just as source.providers/openrouter/dist/ai-runtime.jswith an export-less module (file still present) fails@workglow/openrouter/ai-runtime loadsandworkglow/openrouter/runtime loads, and nothing else —AssertionError: expected 0 to be greater than 0. Before this PR no CI job imported either.bun scripts/test.ts scripts vitest— 4 files, 38 passed.pluginsto the rootdefineConfigfails "attaches the source rewrite to every project" and nothing else.scripts/lib/workspaceSource.tsagainst the installed vite typings (scripts/is in no CI tsconfig, andvitedoes not resolve from the root, so the branch's existingimport type { Plugin } from "vite"is effectivelyanythere — pinned with a temporarypathsmapping instead of trusting that).bun run build— 84/84 tasks, so addingworkglowtopackages/testintroduces no turbo cycle andbuild-typesstill passes.🤖 Generated with Claude Code