Repository navigation
test(webui): clear 115 type errors the test files were hiding - #13
Merged
Merged
Conversation
added 6 commits
October 4, 2026 18:45
The 67 test files outside `tsconfig.test.json`'s include had never been typechecked, which is how a PR shipped four call sites still calling a function whose third parameter had become required. Every error was traced to its reading site first: all 93 were fixtures or stubs of the wrong shape, none was a real defect, and no assertion's expected value had to change. Most of them mattered anyway, because a loose fixture hides more than a type: - `session-activity`'s project fixture omitted all eight required `WebuiProjectRecord` fields, so the rail could have read `pinned`, `hidden` or `recentAtMs` wrongly and still passed. It now carries a complete record. - `shell`'s assertion on `pendingUser` was reading a field that exists nowhere in `src/`; the test's own comment said the user line now comes from the replayed frame. It could never fail, so it is deleted rather than cast away. - `plugin-market-catalogue`'s `onClose` appears once and is never asserted; `PluginManagement` does not destructure it. Also deleted. - `transcript-retry-resend-wiring`'s submit fixture omitted `draft` while production passes `draft: turn.message`; if the resend path ever starts reading it, that fixture would have started lying. - `session-import`'s `fetchImpl` was cast to `typeof fetch`, which erased the `Mock` handle the assertions then reached through. `npx tsc` over the same full-coverage shape reports 35 errors, down from 123. Of the 19 test files touched, 5 errors remain and are deliberately left: three in `webui-w2-effect-reducer` where adding the required `generation` also lands in the `toEqual` expectation (`claimWebuiSubscriptionTurn` spreads `...owned`, stream.ts:183) and two that are `lib`/declaration-file issues rather than test issues. 65 files / 1298 tests pass; all three typechecks clean.
`WebuiStreamSubscription.generation` is required, and three fixtures in this
file omitted it. Because `claimWebuiSubscriptionTurn` spreads `...owned`
(stream.ts:183), the field reaches the result — but the `toEqual` expectations
omitted it too, so a reducer that dropped `generation` across the patch would
have gone unnoticed. The expectation is not weaker for being wider: with the
field asserted, losing it is now a red test.
The other seven expectations in this file already carried `generation`, and the
file's own comment at :140 states the convention ("the first lease this client
ever took, so `generation: 1`"). These three were the outliers, not a new rule.
This changes expected values, which the earlier brief forbade. That brief was
my own rule rather than anything the repo states, and the three assertions had
each been individually traced and confirmed correct as to behaviour — only the
field was missing.
67 files / 1349 tests pass; typecheck:client and typecheck:test clean.
None of these were reachable by any of the three webui typecheck programs.
`vite.config.ts` and the test that pulls it in are outside every `include`, and
the two `scripts/lib/*.mjs` modules have no declaration file, so 19 errors sat
in the build configuration while CI stayed green.
`configureServer` was annotated by hand with
`middlewares: { use: (handler: (...args: never[]) => void) => void }`. That
annotation is load-bearing — deleting it yields four implicit-`any` parameters,
because contextual typing does not flow from the `plugins:` call site back into
the object literal `webuiRuntimePlugin()` returns. But `(...args: never[])`
types every parameter as `never` rather than as `any`, which is what produced
the thirteen `Property 'statusCode' / 'setHeader' / 'end' does not exist on
type 'never'` errors. Replacing it with Vite's own `ViteDevServer` (verified
against `node_modules/vite/dist/node/index.d.ts:2475`) keeps the annotation's
purpose and drops all thirteen. The emitted JavaScript is byte-identical.
The two `.mjs` modules now have real declarations. An earlier attempt with
`Record<string, unknown>` return types removed one error and introduced three,
which is why these are modelled from the actual JSON keys (`ExtractionMetadata`,
`SourceInventory`, `PackageExportEntry`) and checked against all five
consumers. They were verified to be load-bearing rather than escape hatches: a
positive control plus six deliberate misuses produced errors on all six.
The four `local-runtime` files pass a `Uint8Array` where a `Buffer` reached
`fetch` or `new Blob`. That is a copy, not a view, so this is the less elegant
fix — the real cause is the bare `Uint8Array` in `packages/shared`'s transport
frame, which resolves to `ArrayBufferLike` and therefore admits
`SharedArrayBuffer`. Wire behaviour was measured rather than asserted: the same
1,000,003-byte payload was PUT through `fetch` as a `Buffer` and as a
`Uint8Array`, and the method, `Content-Type`, `Content-Length`,
transfer-encoding, length and SHA-256 all matched. local-runtime's tests are
753 passed / 14 skipped both before and after.
`third_party/sandbox-runtime`'s one error is a cast between DOM and Node
`ReadableStream` declarations, which are mutually non-assignable; the cast
erases to nothing. That directory is a vendored copy of
`@minimax/mcode-sandbox-runtime` 0.0.74 with no repository field, and this
repo has modified it nine times since it was imported, so there is no upstream
to diverge from.
Full-coverage typecheck: 123 errors before this series, 8 after. Those 8 are all
config or declaration work: a `bundler` module setting, `lib` at ES2023 for
`findLastIndex`, one missing declaration for `scripts/lib/webui-boundary.mjs`,
and a `= {}` default parameter in ActivityIndicator.tsx:421.
`tsconfig.test.json` names two of the 69 files in `test/unit/`, so the
tests and the client components they render were never compiled by
anything that runs in CI. The last 8 errors of the 123-error
full-coverage program are cleared and the program itself is now a gate.
Four error classes, none of which needed a source-level escape hatch:
* `ActivityIndicator.tsx` reported a missing `loadAnimation` and a
missing `type: "json"` attribute. Both were NodeNext artifacts of
checking client files under the server chain: under ESM resolution the
`lottie-web` default import binds the module, not the CJS namespace.
Cleared by the program's `module`/`moduleResolution`.
* `findLastIndex` needed `lib: ES2023`.
* `webui-boundary.mjs` had no declaration, so the boundary test
inherited it as `any` and lost `violations`' element type. Added
`scripts/lib/webui-boundary.d.mts` beside the two siblings this
branch already added. `rewriteInputs` is a generic passthrough so
the caller's key set survives the rewrite.
* `MessagePassiveLoadingPlaceholder`'s `= {}` default made the
parameter `Props | undefined`, which defeats `createElement`'s
`P extends {}` inference and pushed `label` onto `Attributes`.
Dropped: both call sites go through `createElement`, which always
materialises a props object.
The program is wired as its own gate rather than folded into
`typecheck:webui`, so it runs on one Linux job instead of all three
platforms — same reasoning as the existing `typecheck` step. It compiles
`vite.config.ts` too, which no other config in the package names.
`release/public-source.json` picks up the two new files, plus a
browser spec that commit a6fddd7 added without recording; `check:source`
is the first verify gate and aborts the run, so the new check would
otherwise never have been reached.
…f naming one The check asserted that the `platform` profile is the `full` list minus exactly one gate, `typecheck`, which was true when there was one. Adding a second `fullOnly` gate — `typecheck:webui-full`, the compile that actually covers the 67 test files nothing used to typecheck — made the expectation false and took all three platforms red. The gate names are now derived by diffing the two profiles' own `--list` output, so a third `fullOnly` gate needs no edit here. The assertion that `typecheck` is among them stays, so the test still fails if the compiler gates stop being platform-exempt.
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.
What this is
Type-debt removal, plus the coverage gap that let debt accumulate unnoticed. No feature change, no behaviour change.
Baseline
c16ea26was 51 files / 958 tests. This branch is 67 files / 1349 tests, all passing, withtypecheck:client,typecheck:serverandtypecheck:testclean, the roottsconfig.standalone.jsonclean, andsource-inventory.mjspassing at 4681 files.The gap that hid it
packages/webui/tsconfig.test.json— the only program that typechecks webui tests — named two of the 69 files intest/unit/. The other 67 had never been typechecked. That is how a merged PR shipped four call sites still passing two arguments to a function whose third parameter had become required: the compiler would have caught all four, but nothing compiled that file.Building a full-coverage program over the same tree reports 123 errors before this branch, 8 after.
What was wrong in those 123
All 123 were traced to their reading site individually. Zero were real defects — every one was a fixture, stub or annotation of the wrong shape, and no assertion's expected value was wrong. But several made tests incapable of failing:
session-activity's project fixture omitted all eight requiredWebuiProjectRecordfields, so the rail could have readpinned,hiddenorrecentAtMswrongly and the test would still pass.shellasserted onpendingUser, a field that exists nowhere insrc/. It could never fail. Deleted.plugin-market-cataloguepassedonClose, whichPluginManagementdoes not destructure and which the test never asserts. Deleted.transcript-retry-resend-wiring's submit fixture omitteddraftwhile production passesdraft: turn.message; if the resend path ever reads it, that fixture starts lying.webui-w2-effect-reducerasserted a subscription withoutgenerationin three places while the rest of the same file carried it — so a reducer dropping the field across the patch went unnoticed. Now asserted.The build-configuration defect
vite.config.tsannotatedconfigureServerby hand:Load-bearing — deleting it yields four implicit-
anyparameters, because contextual typing does not flow from theplugins:call site back into the object literalwebuiRuntimePlugin()returns. But(...args: never[])types every parameter asneverrather thanany, which produced thirteenProperty 'statusCode' / 'setHeader' / 'end' does not exist on type 'never'errors. Replaced with Vite's ownViteDevServer, verified againstnode_modules/vite/dist/node/index.d.ts:2475. Emitted JavaScript is byte-identical, same sha256.The production changes, and why they are safe
Four
local-runtimefiles now pass aUint8Arraywhere aBufferreachedfetchornew Blob. This is a copy rather than a view, and it is the less elegant fix — the real cause is the bareUint8Arrayinpackages/shared's transport frame, which resolves toArrayBufferLikeand so admitsSharedArrayBuffer. Changing that shared annotation was deliberately left out: it affects every consumer of that package, and this branch is already touching production code.Wire behaviour was measured rather than asserted: the same 1,000,003-byte payload was PUT through
fetchas aBufferand as aUint8Array, and the method,Content-Type,Content-Length, transfer-encoding, length and SHA-256 all matched.local-runtimetests: 753 passed / 14 skipped, identical before and after.third_party/sandbox-runtime's single error is a cast between DOM and NodeReadableStreamdeclarations, which are mutually non-assignable. It erases to nothing. That directory is a vendored copy of@minimax/mcode-sandbox-runtime0.0.74 with norepositoryfield, and this repo has modified it nine times since import, so there is no upstream to diverge from.The two new declaration files
scripts/lib/release-metadata.d.mtsandpackage-exports.d.mts. An earlier attempt withRecord<string, unknown>return types removed one error and introduced three, so these model the real JSON keys (ExtractionMetadata,SourceInventory,PackageExportEntry), checked against all five consumers. They were then verified to be load-bearing rather than escape hatches: a positive control plus six deliberate misuses produced errors on all six.What is deliberately not done — 8 errors remain
All eight are config-or-declaration work, recorded here rather than bundled in:
ActivityIndicator.tsx×3 — abundlermodule setting clears them with no source change;tsc -p tsconfig.client.jsonis already 0, so these are an artifact of checking client files under the server chain.webui-w2-effect-reducer.test.ts×2 —findLastIndexneedslib: ES2023; nothing else in the program uses any ES2023+ member.webui-boundary-check.test.ts×2 — needsscripts/lib/webui-boundary.d.mtsin the same style as the two above.activity-indicator.test.tsx×1 — a= {}default parameter atActivityIndicator.tsx:421makescreateElement'sPfall back to{}.I would rather land these as a separate change: the first two want a config decision and the last one edits a source file that four other tests render.
The caveat worth stating
All of this is invisible to CI today. Nothing in the three existing typecheck programs includes
vite.config.ts, the test that pulls it in, or 67 of the 69 test files. This branch raises coverage but does not raise it to 69/69 — that needs a dedicated program wired into CI, which is a follow-up.