Skip to content

fix(test): explain an unresolvable workspace specifier - #752

Merged
sroussey merged 2 commits into
claude/coverage-dist-bundle-fix-ew0vj8from
claude/coverage-resolve-diagnostic
Aug 13, 2026
Merged

fix(test): explain an unresolvable workspace specifier#752
sroussey merged 2 commits into
claude/coverage-dist-bundle-fix-ew0vj8from
claude/coverage-resolve-diagnostic

Conversation

@sroussey

@sroussey sroussey commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator

Based on #741 (retarget to main once that merges). Rebased onto origin/main (67bed681). Independent of #748 and #749.

⚠️ build is red, inherited from main

main has not built since 5b94e05a@workglow/anthropic#build-types fails with Anthropic_StructuredGeneration.ts(57,41): error TS2739 … missing max_tokens, messages, model. This branch previously passed build only because its base predated the break; after rebasing onto current main it inherits the failure. Not caused by this PR and not fixed here — another agent owns that fix. Every job downstream of build is blocked by it.

What

The source-redirect plugin rewrites the result of resolution — that ordering is the whole trick, since it lets conditional exports pick the right target first. The consequence is that resolution still goes through exports, which point at ./dist/*, so dist/<entry>.js has to exist even though the plugin immediately rewrites it to src.

When it does not, Vite's resolution fails before the rewrite can happen, this.resolve yields nothing, the plugin returns null, and the run dies with a generic message that blames the manifest and names neither the plugin nor the remedy. Observed first-hand in this checkout:

Error: Cannot find package '@workglow/ai/worker' imported from
  …/providers/huggingface-transformers/src/ai/common/HFT_ModelSchema.ts

Nothing there tells you that a workspace plugin is involved, that @workglow/ai is the package to look at, or that bun run build / bun run use-source fixes it.

Why this fix

The plugin kept only package names, so at the moment resolution failed it could not say which package's dist to look at. It now keeps WorkspacePackage[], which makes an actionable message possible at all. After the fix, the same failure reads:

Error: [workglow:workspace-source] cannot resolve "@workglow/ai/worker" (imported from
  …/HFT_ModelSchema.ts). It is owned by the workspace package @workglow/ai. Resolution
  goes through that package's "exports", which point at ./dist/*, so the built entry has
  to exist even though this plugin then rewrites it to src. …/packages/ai/dist is missing
  or empty — @workglow/ai has never been built in this checkout. Run `bun run build`, or
  `bun run use-source` to write source stubs into dist.

Throwing, not warning. An unresolvable @workglow/* specifier already fails the run a moment later; this only replaces the message.

The remedy branches, because the two cases need opposite responses: a dist with no built entries means the package was never built, whereas a populated dist that still lacks this entry is the "added an exports subpath and did not rebuild" case — and there, "run build" on its own reads as wrong advice to someone staring at a directory full of bundles.

Beyond the original plan: the branch tests for built entries, not for the directory. The first draft used existsSync(dir/dist) and, when actually exercised, produced the wrong branch — bun run use-dist --no-build (and bun run clean) leave the directory in place but empty, so a never-built tree was told its dist was stale. Caught only by running it.

Tests

resolveId needs Vite's plugin context and cannot be unit tested, so the two pure helpers were separated out and tested directly (5 cases in scripts/workspaceSource.test.ts):

  • ownerOf(packages, "@workglow/util/schema") → the @workglow/util entry; the bare name too.
  • ownerOf(packages, "@workglow/utilities")undefined — a string prefix that is not a package boundary; attributing it would point the diagnostic at an unrelated directory. "vitest" likewise.
  • the message names specifier, owner, importer and the plugin.
  • the two remedy branches do not read the same (missing or empty / never been built vs carries built entries but none for this specifier / stale rather than absent).
  • the importer clause is omitted when there is no importer.

All five fail pre-fix (TypeError: ownerOf is not a function — neither helper exists).

Actually executed:

  • vitest --project scripts: 3 files, 19 passed.
  • Pre-fix: 5 failed.
  • End-to-end, which is the part that matters: bun run use-dist --no-build to empty every dist, then ran a suite and confirmed the new error text (quoted above) replaces the generic one. Then bun run use-source and re-confirmed green.
  • prettier --check clean on both scripts/ files. .claude/CLAUDE.md fails prettier --check on the unmodified base branch too — pre-existing, verified by stashing.

Risk / blast radius

The one behaviour change in this stack: a workspace specifier that fails to resolve is now a hard error at the plugin, where it was previously a soft null that let Vite continue. If some call site legitimately expects an optional @workglow/* specifier to fail resolution and be handled downstream, this converts that into a thrown error. No such call site was found.

Confined to scripts/lib/workspaceSource.ts. The success path is byte-identical — ownerOf replaces an inlined names.some(...) with the same boundary semantics.

Unverified

  • "Vite fails before the plugin can rewrite when dist is absent" is now observed, not merely reasoned — but only for one specifier shape (a subpath export under vite-node). Other resolution paths may still produce Vite's own message.
  • No exhaustive audit of optional-workspace-specifier call sites beyond a search; the behaviour change above rests on that.
  • The build failure inherited from main (see the note at the top).
  • Run under Node v22.22.2, not the Node 24 the repo asks for.

@sroussey
sroussey force-pushed the claude/coverage-dist-bundle-fix-ew0vj8 branch from ced2825 to 97a0b75 Compare August 13, 2026 03:53
The source-redirect plugin rewrites the RESULT of resolution, so resolution
itself still goes through the package's `exports`, which point at ./dist/*.
With no built entry there, Vite's resolution fails first, `this.resolve` yields
nothing, the plugin returns null, and the run dies with a generic
`Cannot find package '@workglow/ai/worker' imported from …` that blames the
manifest and names neither the plugin nor anything to do about it.

Keeps the workspace list as WorkspacePackage rather than bare names, so the
owning directory is available at the point resolution fails, and throws a
message naming the specifier, the owner, the importer and the remedy. Throwing
rather than warning is right: an unresolvable @workglow/* specifier already
fails the run, so this replaces a misleading message with an actionable one.

The remedy branches on whether the owner's dist holds any built entries, since
"never built" and "a new exports subpath was added without rebuilding" call for
different actions and the second reads as wrong advice to someone looking at a
populated dist. An empty dist directory — what `bun run clean` and
`use-dist --no-build` both leave behind — counts as never built.

ownerOf and unresolvedWorkspaceMessage are separated out as pure functions
because resolveId needs Vite's plugin context to drive and cannot be unit
tested; the message was additionally verified end to end by emptying dist and
running a suite.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H797qbH356jjznKgUax63o
@sroussey
sroussey force-pushed the claude/coverage-resolve-diagnostic branch from 007fc3b to 02e3cdb Compare August 13, 2026 03:55
…ew0vj8' into tmp-resolve-752

# Conflicts:
#	scripts/workspaceSource.test.ts
@sroussey
sroussey merged commit 71afe15 into claude/coverage-dist-bundle-fix-ew0vj8 Aug 13, 2026
3 checks passed
@github-actions

Copy link
Copy Markdown

Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 70.3% 29046 / 41312
🔵 Statements 69.16% 31053 / 44900
🔵 Functions 70.16% 5897 / 8405
🔵 Branches 60.22% 15869 / 26348
File CoverageNo changed files found.
Generated in workflow #3019 for commit 28f837f by the Vitest Coverage Report Action

@sroussey
sroussey deleted the claude/coverage-resolve-diagnostic branch August 13, 2026 05:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants