Skip to content

test(exports): widen both export guards one directory down, and fix what they find - #813

Merged
sroussey merged 2 commits into
claude/wonderful-turing-rjtcnx-ai-typesfrom
claude/optimistic-goldberg-d74u5n-export-guard-depth
Aug 16, 2026
Merged

test(exports): widen both export guards one directory down, and fix what they find#813
sroussey merged 2 commits into
claude/wonderful-turing-rjtcnx-ai-typesfrom
claude/optimistic-goldberg-d74u5n-export-guard-depth

Conversation

@sroussey

Copy link
Copy Markdown
Collaborator

Stacked on #717 (base: claude/wonderful-turing-rjtcnx-ai-types). The manifest work in #717 is not touched here — this is the same defect class surviving one directory below where #717's new guards look.

Both guards landed in #717 examine only the layer they were written for:

  • ExportBarrelParity keyed on the hard-coded src/ai/index{,.browser}.ts pair, so it never saw src/ai/runtime.browser.ts — the barrel a browser consumer gets under @workglow/<vendor>/ai-runtime.
  • ExportTypesPairing's duplicateBrowserEntryViolations collected pairs with a flat readdirSync(srcDir), so it saw only the entry layer the manifests name.

Two commits, deliberately not squashed

The A-then-B ordering is the evidence the widening bites. Commit A widens the guards only and is red, reporting exactly two defects and nothing else:

providers/ollama/src/ai/runtime.ts exports
  * from "./common/Ollama_StructuredGeneration", * from "./common/Ollama_TextGeneration"
  but providers/ollama/src/ai/runtime.browser.ts does not
providers/openrouter/src/ai/runtime.browser.ts is identical to
  providers/openrouter/src/ai/runtime.ts and names only relative specifiers
  ("./common/OpenRouter_Client", "./common/OpenRouter_EffortPolicy",
   "./registerOpenRouterInline", "./registerOpenRouterWorker"),
  so both entries resolve the same module graph and the declaration split is
  nominal — re-export the node entry from it instead of duplicating it

Commit B fixes those two sources and turns it green.

What the widening needed

The recursive scan takes ExportBarrelParity from 5 pairs to ~53, and three things had to come with it:

  • Specifier keys strip a trailing .browser. Without it every runtime pair reports as total drift (* from "./common/Ollama_Client" against * from "./common/Ollama_Client.browser") and the guard says nothing. The residual is documented in the source: this makes a star-export comparison across a .browser sibling nominal — and the recursive scan is exactly what closes it, since Ollama_Client.browser.ts vs Ollama_Client.ts is itself a compared pair now.
  • A pure sibling re-export is skipped. A browser file whose entire surface is * from "./<nodeStem>" IS the node surface, and it is the shape fix(providers): point each export condition's types at its own declaration #717 wants — the widened scan now reaches llamacpp-server and stable-diffusion-server, which would otherwise report the node file's every other statement as drift.
  • INTENTIONAL_NODE_ONLY is rekeyed from package dir to browser FILE path, since a package now contributes several pairs. Both consumers (drift loop, staleness loop) key on pair.browserPath.

The vacuous-pass assertion is removed rather than adjusted. expect(pairs.map(…).sort()).toEqual([...INTENTIONAL_NODE_ONLY.keys()]) is flatly wrong at 53 pairs against 5 pins, and any form of it still means a correct new provider fails until somebody registers it as needing no exemption. Its two real roles are stated separately instead: pairs.length > 40 (the scan found the tree) and pairs.some(p => p.browserPath.includes("/src/ai/")) (and it recursed into it). The staleness test is kept, retargeted to the path.

Scope stays at providers/ deliberately, and the doc comment says why: extending to packages/ pulls in packages/tasks/src/task/image/imageTextRender.browser.ts, a genuine implementation split whose browser file exports one factory against the node module's fifteen names — a fifteen-name exemption buying nothing.

For ExportTypesPairing, the relative/bare distinction is unchanged and is still the whole rule. Verified under the recursive scan: all nine packages/workglow bare-specifier shims still classify BARE and stay unreported. The > 9 floor is kept (they are still the negative case) and a some(p => p.browserPath.includes("/src/ai/")) assertion is added so a regression to the flat scan fails loudly.

The source fixes

ollama is a source-compatibility fix, not a cosmetic one. Before #717 the browser types condition pointed at the NODE declarations, so a browser consumer importing createOllamaTextGenerationStream from @workglow/ollama/ai-runtime compiled fine and got undefined at runtime. With #717's accurate types it becomes a hard TS2305 for anyone with customConditions: ["browser"].

Neither module has a .browser variant and neither imports a node builtin; both are already compiled into the browser bundle via Ollama_JobRunFns.browser. Verified against a rebuild: ai-runtime.browser.js gains no code — the only content delta is the two names in the entry's export list, plus a module reordering because they are now direct entry re-exports.

Every other provider's runtime.browser.ts was audited against its runtime.ts: deepseek, openai, openrouter and xai are in parity modulo the .browser specifier suffix. ollama was the only omission. (The ai-runtime.browser.ts entry layer was audited too; llamacpp-server and stable-diffusion-server differ only in being the intended sibling re-export.)

openrouter was a byte copy naming only relative specifiers, so both halves already resolved the same module graph and the split was nominal. It now re-exports its node peer, reusing the wording #717 established in providers/llamacpp-server/src/ai-runtime.browser.ts. Rebuilt ai-runtime.browser.js is byte-identical (modulo debugId).

Verification

check result
bun scripts/test.ts util vitest after A 2 failed | 814 passed — exactly the two above
bun scripts/test.ts util vitest after B 55 files, 816 passed, 10 skipped
ExportBarrelParity + ExportTypesPairing directly 62 passed
BunExportConditions.test.ts 3 passed — the pinned "bun" condition set is untouched (no package.json is modified by either commit)
bun run build:types 41 successful
bunx turbo run build-js build-types --filter=@workglow/ollama --filter=@workglow/openrouter 10 successful
bun run format clean

Notes for whoever merges

Rebase resolution rule. This branch was rebased onto origin/main before the fixes. main had added export { <vendor>EffortPolicy } from "./common/<Vendor>_EffortPolicy"; to both barrels of openai / xai / deepseek / openrouter while #717's own commit added export * lines to index.browser.ts in the same region. deepseek and xai auto-merged; openai and openrouter conflicted and were resolved as the UNION of both sides in BOTH files. Taking one side's EffortPolicy line in index.ts but not index.browser.ts — or dropping a branch-restored export * — silently recreates exactly the asymmetry ExportBarrelParity exists to catch. The resolution was verified with the guard itself (a dropped line surfaces as a new <vendor>EffortPolicy from "./common/<Vendor>_EffortPolicy" entry in the drift list); no such entry appeared.

Diff size. Because this branch is rebased onto main and the base branch is not, GitHub computes the diff from an old merge base and shows ~117 commits. The change actually under review is the two commits at the tip; git diff <PR-717-tip-as-rebased>..HEAD is 4 files. Rebasing #717 onto main collapses this to those two commits.


Generated by Claude Code

@sroussey
sroussey force-pushed the claude/wonderful-turing-rjtcnx-ai-types branch from 6efb8a2 to d02a046 Compare August 16, 2026 16:31
claude added 2 commits August 16, 2026 16:35
Both guards landed in #717 look only at the layer they were written for, and
the same defect class survives one level below.

`ExportBarrelParity` keyed on the hard-coded `src/ai/index{,.browser}.ts` pair,
so it never saw `src/ai/runtime.browser.ts` — the barrel a browser consumer
gets under `@workglow/<vendor>/ai-runtime`. It now walks `providers/<vendor>/src`
recursively and pairs every `<stem>.browser.ts` with the `<stem>.ts` beside it
(~53 pairs, against 5 before). Three things that widening needs:

- Specifier keys strip a trailing `.browser`, or every runtime pair reports as
  total drift (`* from "./common/Ollama_Client"` against
  `* from "./common/Ollama_Client.browser"`) and the guard says nothing. That
  makes a star-export comparison across a `.browser` sibling NOMINAL — and the
  recursive scan is what closes it, since `Ollama_Client.browser.ts` vs
  `Ollama_Client.ts` is itself a compared pair now.
- A browser file whose entire surface is `* from "./<nodeStem>"` is skipped: it
  IS the node surface, and it is the shape #717 wants (llamacpp-server,
  stable-diffusion-server), which the widened scan now reaches.
- `INTENTIONAL_NODE_ONLY` is rekeyed from the package dir to the browser FILE
  path, since a package now contributes several pairs.

The vacuous-pass assertion (`pairs` equals the pinned keys) is removed rather
than adjusted: 53 pairs against 5 pins makes equality flatly wrong, and any
form of it means a correct new provider fails until somebody registers it as
needing no exemption. Its two roles are stated separately instead — the scan
found the tree, and it recursed into it.

`ExportTypesPairing`'s `duplicateBrowserEntryViolations` scan is likewise
recursive over `src` rather than a flat `readdirSync`. The relative/bare
distinction is unchanged and is still the whole rule; all nine
`packages/workglow` bare-specifier shims still classify BARE and stay
unreported.

Scope stays at `providers/` deliberately: `packages/` pulls in
`packages/tasks/src/task/image/imageTextRender.browser.ts`, a genuine
implementation split (one factory against the node module's fifteen names)
needing a fifteen-name exemption for no benefit.

This commit is intentionally red. It reports exactly two real defects, fixed
in the commit that follows:

  providers/ollama/src/ai/runtime.ts exports
  * from "./common/Ollama_StructuredGeneration", * from "./common/Ollama_TextGeneration"
  but providers/ollama/src/ai/runtime.browser.ts does not

  providers/openrouter/src/ai/runtime.browser.ts is identical to
  providers/openrouter/src/ai/runtime.ts and names only relative specifiers,
  so both entries resolve the same module graph and the declaration split is
  nominal

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lgxtp7mQECdh7F2UT9CVwN
…ds found

Both are the defect class #717 fixed at the entry layer, surviving one
directory down in `src/ai/`.

**ollama** — `runtime.ts` exports `Ollama_TextGeneration` and
`Ollama_StructuredGeneration`; `runtime.browser.ts` did not. This is a
source-compatibility fix, not a cosmetic one: before #717 the browser `types`
condition pointed at the NODE declarations, so a browser consumer importing
`createOllamaTextGenerationStream` from `@workglow/ollama/ai-runtime` compiled
fine and got `undefined` at runtime. With #717's accurate types it becomes a
hard TS2305 for anyone with `customConditions: ["browser"]`. Restoring the two
`export *` lines is what makes the declaration honest.

Neither module has a `.browser` variant, and neither imports a node builtin —
both are already compiled into the browser bundle via
`Ollama_JobRunFns.browser`. Verified: the rebuilt `ai-runtime.browser.js`
gains no code, only the two names in the entry's export list (and a module
reordering, since they are now direct entry re-exports).

Every other provider's `runtime.browser.ts` was audited against its
`runtime.ts` — deepseek, openai, openrouter and xai are in parity modulo the
`.browser` specifier suffix. ollama was the only omission.

**openrouter** — `runtime.browser.ts` was a byte copy of `runtime.ts` naming
only RELATIVE specifiers, so both already resolved the same module graph and
the declaration split was nominal: two `.d.ts` files kept equal by hand. It now
re-exports its node peer, in the shape #717 established for
`providers/llamacpp-server`. Rebuilt `ai-runtime.browser.js` is byte-identical.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lgxtp7mQECdh7F2UT9CVwN
@sroussey
sroussey force-pushed the claude/optimistic-goldberg-d74u5n-export-guard-depth branch from cf54657 to 3e57e17 Compare August 16, 2026 16:37
@sroussey
sroussey merged commit f4ce54b into claude/wonderful-turing-rjtcnx-ai-types Aug 16, 2026
10 of 11 checks passed
@sroussey
sroussey deleted the claude/optimistic-goldberg-d74u5n-export-guard-depth branch August 24, 2026 18:48
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