Skip to content

fix(e2e)!: load TypeScript with e2e's own oxc loader, so pnpm 11+ installs pass - #790

Merged
okwasniewski merged 6 commits into
mainfrom
oskar/oxc-loader
Oct 4, 2026
Merged

okwasniewski merged 6 commits into
mainfrom
oskar/oxc-loader

Conversation

@okwasniewski

@okwasniewski okwasniewski commented Oct 3, 2026 •

Copy link
Copy Markdown
Member

Every new pnpm 11+ user's first pnpm install after pnpm dlx e2e init fails today:

Error: ERR_PNPM_IGNORED_BUILDS
  × installing dependencies
  ╰─▶ Ignored build scripts: esbuild@0.28.2

e2e loaded TypeScript through tsx, tsx depends on esbuild, and esbuild has a postinstall. pnpm 11+ fails on any build script the project has not decided on. The root fix is that nothing e2e installs runs an install script. Replaces the closed #712, which edited pnpm-workspace.yaml instead.

What changed

  • tsx is gone from dependencies. e2e loads TypeScript itself with synchronous module.registerHooks hooks (packages/e2e/src/config/esm-hooks.ts), compiles with oxc-transform (config/typescript.ts), and reads tsconfig with get-tsconfig (config/tsconfig.ts). None of the new dependencies has an install script; platform binaries come as optionalDependencies.
  • One policy (config/compiled-files.ts): one extension table (lang, format, the extension an import writes) drives what is compiled, its module format, the ./x.js swap, and implied extensions. .ts .mts .tsx .jsx run as ES modules, .cts as CommonJS. A project file compiles with the nearest tsconfig.json; TypeScript in an installed package compiles with defaults.
  • Resolution: an import written in a compiled file follows TypeScript (./x.js and ./x to x.ts, a directory's package.json main then index, tsconfig paths/baseUrl for project files); a # import or package export whose target is missing (./x.js, or ./x with no extension) moves to the TypeScript file behind it, also when a lax resolver earlier in the chain returned the missing file. An import written in JavaScript gets plain Node resolution. require() specifiers are read as platform paths (C:\app\helper on Windows; config/specifiers.ts).
  • load reads source through nextLoad, so other hooks in the chain still see the file.
  • The tsconfig.json is the nearest one, or the referenced project that includes the file when it is solution-style (files: [] + references, as Vite writes). One that cannot be read (an extends package not installed) is skipped with one warning per run (the runner hands the files it warned about to its workers, which warn only about others they reach). tsconfig and file lookups live in a view per module graph (e2e-graph): fresh for each e2e mcp session's graph load, read once otherwise (collecting 50 files parses the extends chain once). Outside a graph, file lookups go to the disk each time, as Node's own resolution does.
  • Compiled CommonJS starts with require = globalThis[Symbol.for("e2e.hooked-require.v1")](__filename);, a require the loader registers: the require Node hands a CommonJS module an ES module imported skips resolve hooks before modules: fix sync hook short-circuit in require() in imported CJS nodejs/node#62920 (24.18.0, 26.2.0; not in 22), and on every version once a module.register loader is in the chain (Yarn PnP, tsx in NODE_OPTIONS). Its resolve runs the loader's resolve hook, since require.resolve skips hooks before module: run require.resolve through module.registerHooks() nodejs/node#62028 (24.15.0; not in 22). The statement joins the end of the last directive (found from oxc-parser's parse of the output), so the prologue and every mapped position stay put; with no directive it takes its own first line and the map gains one unmapped line.
  • oxc helpers (using on Node 22, legacy decorators) come from e2e's own @oxc-project/runtime: the from "..." / require("...") specifiers oxc reports in helpersUsed are rewritten (oxc 0.152 has no option for it), so a project's own @oxc-project/runtime import is untouched.
  • oxc appends export {}; to CommonJS whose only imports were type-only, before any trailing comment; the loader finds it in oxc-parser's parse of the output and blanks it. Not reported upstream yet (crates/oxc_transformer/src/typescript/annotations.rs).
  • Syntax oxc passes through and Node cannot run is refused before compiling, with file:line:col and the fix: auto-accessors (no oxc release lowers them), decorators without experimentalDecorators, and import declarations and value export declarations in .cts (export interface/type/declare pass, as tsc allows). The check parses only files whose text could hold them.
  • oxc and oxc-parser load on first compile, so --version and --help run without the native binding; without it, a run fails with TYPESCRIPT_COMPILER_UNAVAILABLE (exit 3) naming the @oxc-transform/@oxc-parser bindings for the platform and [BUG] Platform-specific optional dependencies not being included in package-lock.json when reinstalling with node_modules present npm/cli#4828; any other load failure gives its reason.
  • Another loader earlier in the chain: a CommonJS load result with no source (Yarn PnP) is read from disk, and an inline source map it left is composed with oxc's (@jridgewell/remapping), so file:line still names the file on disk.
  • Workers from source --import config/register.ts, which registers the hooks; it and esm-hooks.ts load under Node's own type stripping, so they stay erasable TypeScript.
  • Removed what existed only for tsx: tsxUsesSyncHooks and its version table, the tsx namespace and tsx-namespace URL tagging, the NODE_OPTIONS inspection, and the diagnose hint for tsx's ?namespace= CommonJS dead end. Config and tests now share the runner's module instances; the globalThis slots stay for a project that loads its own copy of e2e.
  • A test's source location is read off the stack text, now with at async ... frames (a test file that awaits a helper declaring tests), and only file: URLs are decoded (collect/registry.ts); main throws on a % in a file name and loses the location at a #. util.getCallSites drops async frames and Error.prepareStackTrace gets no call sites while source maps are on, so the text stays the source.
  • A type imported without import type that stays in the output (a decorated member's type under emitDecoratorMetadata, a re-exported type) fails at link time as before, but the error now names the importing line and import type.
  • Breaking: Node floor is ^22.22.3 || >=24.8.0 for every published package (was >=22.12.0), as an explicit release-range model in internal/node-version.ts. registerHooks does not exist before 22.15, and before 22.22.3 / 24.8.0 a CommonJS file an ES module imported cannot require() an ES module the hooks load (esm: populate separate cache for require(esm) in imported CJS nodejs/node#59679). The CLI refuses older versions naming the ones to upgrade to, and the Bun and Deno runtimes (Bun has no registerHooks; Deno has it but cannot load oxc's addon from a hook); registerLoader refuses them too (NODE_UNSUPPORTED). No async module.register fallback.
  • Guards: pnpm check:install-scripts (in pnpm check) walks the installed tree of every published package and names the chain to any preinstall/install/postinstall or binding.gyp (on main: e2e > tsx@4.23.14 > esbuild@0.28.2). A new install CI job runs scripts/install-smoke.ts with pnpm 11 and 12: packs e2e, @e2e-dev/web, @e2e-dev/mobile, installs them into a fresh project, installs again from the lockfile, runs a TypeScript test, and fails on a non-zero exit, ERR_PNPM_IGNORED_BUILDS, or a written pnpm-workspace.yaml (on main: pnpm install exits 1).
  • CI pnpm test runs on 22, 24, 26 (check-latest) and the floors 22.22.3 and 24.8.0. Vitest externalizes the workspace packages' dist/ so integration tests load them natively, through e2e's hooks.
flowchart LR
  A["import / require"] --> W{written in a compiled file?}
  W -- no --> N[Node resolution]
  W -- "yes: ./x.js, ./x, ./dir, tsconfig alias" --> P[TypeScript file, from the graph's view]
  W -- "yes: # import / package export" --> X[Node resolution, retried on the x.js to x.ts swap]
  P --> N
  N --> F["format from the extension table; e2e-graph query"]
  X --> F
  F --> L{load hook}
  L -- "TypeScript, JSX" --> O["nextLoad source, oxc-transform, inline source map"]
  L -- "JSON, no type attribute" --> J["nextLoad as json, export default JSON.parse"]
  L -- else --> D[nextLoad]
Loading

Parity with tsx

Each row has a test: tests/integration/typescript-loader.test.ts runs the built CLI on a fixture with all of them, in an ESM package and in a package without "type": "module", with --workers 2; tests/unit/esm-hooks.test.ts and tests/unit/typescript.test.ts cover the rules one by one.

Case main (tsx) this branch
config + tests, "type": "module" yes yes
config + tests, no "type" (Next.js-style CJS scope) yes yes
.mts yes yes
.cts with import type, import x = require(), export = yes yes
.cts with ESM import statements yes (esbuild converts) no, see below
.tsx and .jsx (tsconfig jsx/jsxFactory) yes yes
enums, const enum, namespaces, parameter properties yes yes
experimentalDecorators (+ emitDecoratorMetadata) yes yes
standard (TC39) decorators yes (esbuild lowers) refused with file:line:col, see below
auto-accessors (accessor x = 1) yes (esbuild lowers) refused with file:line:col, see below
.cts with export interface/type/declare and module.exports yes yes
package exports/imports target without extension (./src/*) yes yes
tsconfig extends package not installed yes (ignored silently) yes, one warning
Yarn PnP (module.register loader in the chain), .cts yes yes (not on Node 24.8.0 to 24.11.0, see below)
import type yes yes
tsconfig paths + baseUrl yes (cwd tsconfig, JS files too) yes (nearest tsconfig, from TypeScript files)
./x and ./x.js importing x.ts, directory index yes (from JS files too) yes, from TypeScript files; JavaScript gets Node resolution
workspace package exporting .ts source yes yes
# subpath imports, including one mapped to ./x.js with x.ts on disk yes yes
.cts requiring a sibling .ts (extensionless, .cjs, tsconfig alias) and a decorator helper yes yes
JSON with and without with { type: 'json' } yes yes
top-level await yes yes
.cjs helper require()ing project TypeScript Node type stripping yes, through the hooks
.cts with a hashbang, a header comment, and 'use strict'; stack line of a throw yes yes
.ts written with module.exports, required by a .cjs file Node type stripping no, see below
failure file:line:col and test source in report and failure text fails.e2e.ts:7:19, test at 5:1 identical
test file named 50% off #1.e2e.ts COLLECTION_ERROR: URI malformed (50%off), empty source file (#) collects, 7:19 and 5:1
config re-load freshness (e2e-graph), tsconfig alias in the graph yes yes, plus tsconfig edits apply
workers yes yes
e2e mcp, e2e explore yes yes (mcp-server.test.ts, explore.test.ts)
Windows paths pathToFileURL/fileURLToPath only, no platform branches same

Behavior changes, all in docs/reference/config.mdx and the changeset:

  • oxc does not turn ESM import statements into require calls, so a .cts file uses import x = require(). TypeScript's verbatimModuleSyntax and Node's own type stripping draw the same line.
  • oxc compiles only legacy decorators. Standard decorators without experimentalDecorators, and auto-accessors, are refused with their file:line:col and the fix.
  • The CLI runs on Node.js only: bun --bun e2e and deno run npm:e2e exit 2 naming the fix. Bun and Deno still install it, and bunx e2e runs it on Node.js.
  • Yarn PnP with a .cts file on Node.js 24.8.0 to 24.11.0: Node rejects the sourceless CommonJS result PnP's loader returns before e2e's hook sees it (fixed in 24.11.1). Documented in docs/reference/cli.mdx.
  • Every .ts is an ES module, including one a .cjs file requires; CommonJS TypeScript goes in .cts.
  • An import written in a JavaScript file resolves as Node resolves it (tsx also applied TypeScript's extension rules and tsconfig paths there).
  • TypeScript in an installed package compiles without the project's tsconfig.json.

Verified

Ran it locally: yes

Fresh user, pnpm 12.6.0. Every published package packed (pnpm pack) and served from a local verdaccio per build (main on 4410, this branch on 4411; e2e and @e2e-dev/* never proxy to npmjs, everything else does), fresh /tmp dirs, fresh store, minimumReleaseAge 0 so a seconds-old local publish is not what pnpm writes a workspace file for, example test against a static site on 4420.

step main this branch
pnpm dlx e2e init --yes exit 0 exit 0
pnpm install exit 1, ERR_PNPM_IGNORED_BUILDS (esbuild@0.28.2) exit 0
pnpm install --frozen-lockfile exit 1, ERR_PNPM_IGNORED_BUILDS exit 0
pnpm-workspace.yaml written by pnpm: allowBuilds: esbuild: set this to true or false absent
esbuild/tsx in pnpm-lock.yaml 4 entries 0
pnpm exec e2e run (example test) 1 passed (pnpm linked the packages before failing) 1 passed

pnpm 12.9.0 on this branch: the same five steps, all exit 0, no pnpm-workspace.yaml.

npm 11.17 (npx e2e init --yes, npm install, npm ci, npx e2e run) and bun 1.4.2 (bunx e2e init --yes, bun install, bun install --frozen-lockfile, bunx e2e run) on this branch: every step exit 0, example test passes.

Node floor. The same three files on each version (hooks.mjs registers pass-through registerHooks, main.mjs imports req.cjs, which requires esm.mjs):

Node result
22.12.0 SyntaxError: The requested module 'node:module' does not provide an export named 'registerHooks'
22.22.2, 24.7.0 (also 22.15, 22.18, 24.0, 24.5) TypeError: Cannot read properties of undefined (reading 'exports') at node:internal/modules/esm/translators (cjsCache.get(job.url).exports)
22.22.3, 24.8.0, 26.10.0 answer 42

The e2e unit and integration suites pass on 22.22.3, 24.8.0, 24.17.0 (the compiled-CommonJS require path), and 24.19.0; the loader's own tests also on 24.11.0 and 26.3.0; the parity fixture through the built CLI (--workers 2, ESM and CJS scope) also on 26.10.0. On 24.7.0 the CLI stops with e2e requires Node.js 22.22.3 or newer on Node.js 22, or 24.8.0 or newer; this is Node.js 24.7.0. ....

Install smoke. node scripts/install-smoke.ts 11 and ... 12 on this branch: install, frozen install, and the TypeScript test all exit 0. On main, pnpm install exits 1.

Package managers. Every published package packed and served from a local verdaccio on 4411 (e2e and @e2e-dev/* never proxy to npmjs), main is e2e@0.16.0 from npmjs. A fresh /tmp project per row: init --yes through the manager's own runner for npm and pnpm, a copy of that npm-initialized project for the rest; then a TS-heavy fixture (tsconfig paths, a .cts that requires ./plain, a legacy decorator and an enum, JSX, a #shared/* import map naming ./shared/src/* with no extension); install, frozen install, the example test against a static site on 4420, and the fixture. Node 24.19.

manager main: install / frozen main: example / fixture branch: install / frozen branch: example / fixture
npm 11.17 ok / ok pass / fail (Cannot find module './plain' from the .cts) ok / ok pass / pass
npm --omit=optional ok / ok pass / fail (same) ok / ok TYPESCRIPT_COMPILER_UNAVAILABLE naming the darwin-arm64 oxc bindings and npm/cli#4828, exit 3; e2e --version works
npm, lockfile written for linux-x64 ok / ok pass / fail (same) ok / ok pass / pass
pnpm 10.34 ok / ok pass / fail (same) ok / ok pass / pass
pnpm 11.28 ERR_PNPM_IGNORED_BUILDS / same pass / fail (same) ok / ok, no workspace file pass / pass
pnpm 12.9 ERR_PNPM_IGNORED_BUILDS / same pass / fail (same) ok / ok, no workspace file pass / pass
pnpm 12 workspace monorepo (tests import a workspace package, exports: {"./*": "./src/*"}) ERR_PNPM_IGNORED_BUILDS / same pass / pass ok / ok pass / pass
Yarn 1.22 ok / ok pass / fail (same) ok / ok pass / pass
Yarn 4.18, nodeLinker: node-modules ok / ok pass / fail (same) ok / ok pass / pass
Yarn 4.18, Plug'n'Play ok / ok pass / fail (same) ok / ok pass / pass
Bun 1.4.2 install, bunx e2e run (Node.js) ok / ok pass / fail (same) ok / ok pass / pass
bun --bun e2e run (Bun runtime) Cannot find package 'tsx:' exit 2: e2e runs on Node.js: Bun 1.4.2 reports Node.js 26.3.0 but has no module.registerHooks ...
Deno 2.9.4 install, deno run -A npm:e2e run ok / ok Loading unprepared module ...?tsx-namespace=e2e ok / ok exit 2: e2e runs on Node.js, not Deno 2.9.4: run the CLI with Node.js (npx e2e).

Suites. testbed: 71 passed, 1 skipped. Web benchmark tests/: 91 passed. Web benchmark tests-agent/ with CI=1 --strict-cache and no model key: identical per-test verdicts and counters on main and this branch (26 replayed, 2 handed off, 6 missed, 2 model calls refused for the missing key), so the branch moves no replay key. Mobile benchmark left to CI. Every edge case from review has a repro in /tmp/edge-repros and a committed regression test.

Timings. Config load and collect in a fresh process per round (loadConfigModule + resolveConfig + collect from dist), alternating rounds against main, medians.

project build config load ms collect ms CPU ms tests
apps/testbed main 150 12 231 71
apps/testbed branch 136 17 189 71
apps/web-benchmark main 150 21 242 91
apps/web-benchmark branch 140 25 200 91

Config load is 10 to 14 ms faster and CPU about 18% lower (no tsx registration, no esbuild service). Collect is 4 to 5 ms slower: the syntax check parses files whose text could hold a refused construct, and oxc-parser loads on first use.

@github-actions

github-actions Bot commented Oct 3, 2026 •

Copy link
Copy Markdown
Contributor

🟢 e2e web: 91 passed

All 91 tests in 41 files
Test Time
🟢 tests/aria-hidden-flow.e2e.ts · 2 passed 485ms
🟢 aria hidden flow › picks the Pro plan by its visible text and confirms it 349ms
🟢 aria hidden flow › another plan shows the error and keeps the picker open 136ms
🟢 tests/async-states.e2e.ts · 2 passed 7.0s
🟢 async states › reads the code off the transient toast and verifies it once the button enables 2.2s
🟢 async states › a wrong code is rejected after the toast has gone 4.8s
🟢 tests/canvas-only.e2e.ts · 2 passed 204ms
🟢 canvas only › taps square, circle, then diamond at their pixel coordinates 112ms
🟢 canvas only › a shape out of order paints the error and resets progress 92ms
🟢 tests/canvas-whiteboard.e2e.ts · 2 passed 341ms
🟢 canvas whiteboard › drags both shapes into their slots and saves 190ms
🟢 canvas whiteboard › bounces a shape off the wrong slot and refuses to save early 151ms
🟢 tests/cart-totals.e2e.ts · 2 passed 297ms
🟢 cart totals › increasing a quantity updates the line total but the order summary stays stale (planted bug) 169ms
🟢 cart totals › a quantity never drops below one 128ms
🟢 tests/checkout-review.e2e.ts · 1 passed 131ms
🟢 shows a raw greeting token and an undefined delivery date (planted bug), yet places the order 131ms
🟢 tests/context-menu-trap.e2e.ts · 2 passed 482ms
🟢 context menu trap › right-click opens nothing; the row menu renames the file 249ms
🟢 context menu trap › the decoy menu items refuse and keep the file 233ms
🟢 tests/control-inventory.e2e.ts · 8 passed 3.1s
🟢 control inventory › check sets the checkbox and the radio without flipping a set one 209ms
🟢 control inventory › check picks a radio the pick replaces with its summary 122ms
🟢 control inventory › a long press and a double click are told apart from a tap 945ms
🟢 control inventory › the file menu opens on right-click only and Rename renames the file 184ms
🟢 control inventory › setInputFiles attaches files named from the project root 109ms
🟢 control inventory › the details view is left through the browser history 135ms
🟢 control inventory › scrollIntoView brings the footnote into view 221ms
🟢 control inventory › every exercise done shows the success message 1.2s
🟢 tests/css-content-ui.e2e.ts · 2 passed 281ms
🟢 css content ui › the tile labelled Ship it advances and confirming ships 159ms
🟢 css content ui › a wrong tile shows the CSS-rendered error and keeps the picker 122ms
🟢 tests/date-picker.e2e.ts · 2 passed 526ms
🟢 date picker › navigates three months ahead and books the required date 311ms
🟢 date picker › confirming without the exact date shows the error 215ms
🟢 tests/debounced-search.e2e.ts · 2 passed 2.8s
🟢 debounced search › a decoy one character off is refused 1.4s
🟢 debounced search › picking the exact item adds it to the cart 1.4s
🟢 tests/deferred-dialog.e2e.ts · 1 passed 1.1s
🟢 one submit opens one confirm, even when double-clicked 1.1s
🟢 tests/div-soup.e2e.ts · 2 passed 600ms
🟢 div soup › a wrong code is rejected 265ms
🟢 div soup › the right code grants access after the delete tile fixes a typo 335ms
🟢 tests/drag-and-drop.e2e.ts · 2 passed 594ms
🟢 drag and drop › an out-of-order item resets the dropzone 311ms
🟢 drag and drop › dropping Banana then Cherry passes 283ms
🟢 tests/filter-deep-link.e2e.ts · 2 passed 228ms
🟢 filter deep link › filtering writes the query to the URL 122ms
🟢 filter deep link › opening a shared link restores the filter 106ms
🟢 tests/gift-card-purchase.e2e.ts · 3 passed 502ms
🟢 gift card purchase › refuses to buy without an amount 138ms
🟢 gift card purchase › refuses a recipient without an at sign 180ms
🟢 gift card purchase › sends the gift card 184ms
🟢 tests/hover-menu.e2e.ts · 2 passed 411ms
🟢 hover menu › a decoy leaf is refused 182ms
🟢 hover menu › hovering Account, then Billing, reaches Redeem voucher 229ms
🟢 tests/iframe-form.e2e.ts · 2 passed 388ms
🟢 iframe form › a wrong code is refused by the host page 186ms
🟢 iframe form › the code read from one frame applies through the other 202ms
🟢 tests/image-only-ui.e2e.ts · 3 passed 439ms
🟢 image only ui › the icons have no names, so the bare svg locator is ambiguous 97ms
🟢 image only ui › a wrong icon shows the error image and keeps the icons 145ms
🟢 image only ui › tapping the star, then the heart, verifies the icons 197ms
🟢 tests/infinite-scroll.e2e.ts · 1 passed 3.9s
🟢 scrolls the feed until the Golden Ticket loads, then claims it 3.9s
🟢 tests/kanban-board.e2e.ts · 3 passed 596ms
🟢 kanban board › pointer drags move both cards into the goal layout 313ms
🟢 kanban board › a swipe along a path drags a card as well 133ms
🟢 kanban board › a plain tap moves nothing and the untouched board is rejected 150ms
🟢 tests/login-form.e2e.ts · 4 passed 617ms
🟢 login form › rejects a malformed email 149ms
🟢 login form › rejects a short password 132ms
🟢 login form › rejects an unknown account 150ms
🟢 login form › signs in and logs out 186ms
🟢 tests/lying-labels.e2e.ts · 4 passed 494ms
🟢 lying labels › the button whose accessible name says Pay now is the wrong one 113ms
🟢 lying labels › a text match for Pay now catches the decoy phrase too 86ms
🟢 lying labels › the field whose accessible name says Amount is the wrong one 146ms
🟢 lying labels › visible text drives the payment through 149ms
🟢 tests/native-dialogs.e2e.ts · 3 passed 400ms
🟢 native dialogs › answers the confirm, prompt, and alert chain 135ms
🟢 native dialogs › dismissing the confirm cancels the order 135ms
🟢 native dialogs › a wrong gift code is rejected 130ms
🟢 tests/newsletter-signup.e2e.ts · 1 passed 2.2s
🟢 subscribe renders no feedback and logs a TypeError (planted bug) 2.2s
🟢 tests/onboarding-wizard.e2e.ts · 5 passed 4.0s
🟢 onboarding wizard › back keeps what was typed and the brief comes back with it 208ms
🟢 onboarding wizard › the address step takes the brief through its bare-div dropdown 276ms
🟢 onboarding wizard › the profile step steers the calendar three decades back 1.0s
🟢 onboarding wizard › the preferences step flips the toggles the brief names and leaves the trap off 1.2s
🟢 onboarding wizard › the review step repeats the brief and finishes with the PIN 1.3s
🟢 tests/order-history.e2e.ts · 2 passed 1.4s
🟢 order history › overview and settings render their panels 161ms
🟢 order history › the orders tab never leaves its loading state (planted bug) 1.2s
🟢 tests/otp-auto-advance.e2e.ts · 3 passed 1.3s
🟢 otp auto-advance › focus advances with each digit and the full code verifies 437ms
🟢 otp auto-advance › backspace on an empty box clears and refocuses the previous one 116ms
🟢 otp auto-advance › a wrong code clears every box and starts over 748ms
🟢 tests/overlay-trap.e2e.ts · 1 passed 697ms
🟢 continues only after the banner and the overlay are gone 697ms
🟢 tests/playbook-cleanup.e2e.ts · 2 passed 404ms
🟢 playbook cleanup › deleting the draft removes it from an exact-name search 204ms
🟢 playbook cleanup › cancelling the dialog keeps the draft searchable 200ms
🟢 tests/price-sorting.e2e.ts · 3 passed 336ms
🟢 price sorting › opens in the featured order with sale prices struck through 105ms
🟢 price sorting › low to high sorts by the original price, not the paid one (planted bug) 108ms
🟢 price sorting › high to low is the same wrong order reversed (planted bug) 123ms
🟢 tests/product-reviews.e2e.ts · 1 passed 1.1s
🟢 the running-shoe page lists coffee-grinder reviews (planted bug) 1.1s
🟢 tests/promo-storefront.e2e.ts · 1 passed 1.4s
🟢 buys a deal once the promo chrome is out of the way 1.4s
🟢 tests/rich-text-editor.e2e.ts · 2 passed 428ms
🟢 rich text editor › bolding exactly the required word passes the check 214ms
🟢 rich text editor › bold that spills into the previous word fails the check 214ms
🟢 tests/saved-addresses.e2e.ts · 3 passed 463ms
🟢 saved addresses › adds an address and clears the form 153ms
🟢 saved addresses › ignores an add with an empty field 148ms
🟢 saved addresses › delete shows its toast but keeps the address, on retry too (planted bug) 162ms
🟢 tests/shadow-dom-form.e2e.ts · 2 passed 481ms
🟢 shadow DOM form › rejects a wrong access code 252ms
🟢 shadow DOM form › grants access with the code from the hint inside the root 229ms
🟢 tests/smoke.e2e.ts · 1 passed 345ms
🟢 home lists the scenarios and opens one 345ms
🟢 tests/stale-dom.e2e.ts · 1 passed 2.1s
🟢 taps the moving target three times across rebuilds of the list 2.1s
🟢 tests/sticky-chrome.e2e.ts · 1 passed 148ms
🟢 accepts the terms once the button clears the sticky footer 148ms
🟢 tests/virtualized-table.e2e.ts · 1 passed 1m 5s
🟢 scrolls the windowed table to the Golden Row and claims it 1m 5s
🟢 tests/wishlist.e2e.ts · 2 passed 1.4s
🟢 wishlist › saving toggles the button and the tab badge 182ms
🟢 wishlist › the wishlist tab renders the empty state despite the badge count (planted bug) 1.2s

e2e 0.16.0 · 1m 49s · web · run artifacts

@github-actions

github-actions Bot commented Oct 3, 2026 •

Copy link
Copy Markdown
Contributor

🟢 e2e web agent: 33 passed

34 agent steps · 26 replayed from cache · 20 model calls · 76.4k tokens (92% cached) · $0.0036

All 33 tests in 5 files
Test Agent Time
🟢 tests-agent/control-inventory.e2e.ts · 7 passed 7 steps 6.9s
🟢 control inventory › check sets the checkbox and the radio 1 step 455ms
🟢 control inventory › check picks a radio the pick replaces with its summary 1 step 236ms
🟢 control inventory › a long press and a double click are chosen over a plain tap 1 step 866ms
🟢 control inventory › a right-click opens the file menu and Rename renames the file 1 step 2.4s
🟢 control inventory › upload attaches the named project files 1 step 2.4s
🟢 control inventory › back returns from the details view to the inventory 1 step 237ms
🟢 control inventory › scroll_to brings the footnote into view 1 step 321ms
🟢 tests-agent/login-form.e2e.ts · 1 passed 1 step 310ms
🟢 act signs in with the benchmark account 1 step 310ms
🟢 tests-agent/planted-bugs.e2e.ts · 3 passed 4 steps · 8 calls 11.8s
🟢 cart totals: a judgment catches the stale order summary 2 steps · 4 calls 5.5s
🟢 checkout review: a judgment catches the broken copy 1 step · 1 call 1.3s
🟢 wishlist: a judgment catches the lost saved items 1 step · 3 calls 5.0s
🟢 tests-agent/scenarios.e2e.ts · 21 passed 21 steps · 12 calls 2m 21s
🟢 act completes shadow-dom-form 1 step · 3 calls 4.1s
🟢 act completes lying-labels 1 step · 3 calls 4.4s
🟢 act completes iframe-form 1 step · 3 calls 4.3s
🟢 act completes infinite-scroll 1 step 5.0s
🟢 act completes virtualized-table 1 step 1m 39s
🟢 act completes overlay-trap 1 step 806ms
🟢 act completes sticky-chrome 1 step 808ms
🟢 act completes async-states 1 step · 3 calls 6.1s
🟢 act completes debounced-search 1 step 2.2s
🟢 act completes hover-menu 1 step 414ms
🟢 act completes drag-and-drop 1 step 383ms
🟢 act completes kanban-board 1 step 554ms
🟢 act completes context-menu-trap 1 step 2.5s
🟢 act completes stale-dom 1 step 383ms
🟢 act completes otp-auto-advance 1 step 642ms
🟢 act completes date-picker 1 step 771ms
🟢 act completes rich-text-editor 1 step 2.7s
🟢 act completes playbook-cleanup 1 step 491ms
🟢 act completes promo-storefront 1 step 2.2s
🟢 act completes gift-card-purchase 1 step 2.4s
🟢 act filters the list and the URL carries the filter 1 step 180ms
🟢 tests-agent/unique-cache.e2e.ts · 1 passed 1 step 239ms
🟢 act adds an address with a run-unique label 1 step 239ms

e2e 0.16.0 · 2m 40s · web · run artifacts

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 44 files

Heads up: you’re close to your included review allowance. Set a flex budget so reviews don’t pause.

Re-trigger cubic

Comment thread packages/e2e/src/run/worker/handle.ts Outdated
Comment thread packages/e2e/src/config/esm-hooks.ts Outdated
Comment thread scripts/check-install-scripts.ts
Comment thread scripts/install-scripts.ts Outdated
Comment thread scripts/install-scripts.ts
Comment thread docs/mobile-ci.mdx Outdated
@github-actions

github-actions Bot commented Oct 3, 2026 •

Copy link
Copy Markdown
Contributor

🟢 e2e ios: 1 flaky, 51 passed, 1 skipped

⚠️ 1 flaky test passed on a retry

⚠️ control inventory › toBeFocused follows the focus
tests/controls.e2e.ts:101

ASSERTION_FAILED at step 5 of 5: expect.toBeFocused getByRole("textbox", name: "Autofocus field"), after 5.4s

  • Expected: focused
  • Observed: default states (1 match)
  • Failed the same way on 2 attempts: ASSERTION_FAILED at step 5. The retry that passed is the exception.
  • Screen: dev.e2e.benchmark / Control Inventory

Evidence: screenshot, log

All 53 tests in 7 files
Test Time
⚠️ tests/controls.e2e.ts · 1 flaky, 22 passed, 1 skipped 4m 4s
🟢 control inventory › a label query resolves a labeled text field 11.9s
🟢 control inventory › a labeled field answers name, display value, value reads, fill, and clear 20.5s
🟢 control inventory › a placeholder query and attribute reads find the field 6.3s
🟢 control inventory › a secure field takes a fill and denies value reads 12.2s
🟢 control inventory › press sends a character and Enter through the soft keyboard 10.8s
🟢 control inventory › a field autofocuses on mount and focus() moves the focus 13.9s
⚠️ control inventory › toBeFocused follows the focus (2 failed attempts first) 16.5s
🟢 control inventory › a switch checks, unchecks, and reports its state 17.9s
🟢 control inventory › checkbox and radio roles answer getByRole, and a checkbox toggles by tap 12.5s
🟢 control inventory › checkbox and radio roles check, uncheck, and report their state 15.4s
🟢 control inventory › a radio group reports the checked option 10.5s
🟢 control inventory › buttons report disabled, enabled, and a state that arrives later 9.7s
🟢 control inventory › a display: none element is absent rather than hidden, its visible twin attached 10.4s
🟢 control inventory › a header answers its accessible name 6.5s
🟢 control inventory › a tab row answers count, all, texts, filters, positions, and switching 9.7s
🟢 control inventory › a list answers counts, list-form text, reads, and boxes in order 11.3s
⏭️ control inventory › a row contains its texts (skipped: React Native flattens a plain View out of the accessibility tree on both platforms: XCTest and the Android helper report a View with a testID as a leaf beside its children, so filter({ has }) and a query scoped to it match nothing; only Sc…)
🟢 control inventory › a long press lands as one 10.2s
🟢 control inventory › pointer verbs land at the point asked for 13.1s
🟢 control inventory › an ambiguous locator fails at once 8.0s
🟢 control inventory › screenshot, restart, and clearState reopen the app on its home list 15.7s
🟢 refusals › verbs a device never declares are refused before the engine 3ms
🟢 refusals › keys the soft keyboard cannot send are refused by the engine 406ms
🟢 refusals › a missing locator fails as not found once its timeout passes 1.2s
🟢 tests/device-fixture.e2e.ts · 9 passed 1m 31s
🟢 device fixture › setAppearance flips the color scheme the app reads 12.9s
🟢 device fixture › setOrientation rotates the window the app measures 9.0s
🟢 device fixture › the clipboard reads back what was written 8.1s
🟢 device fixture › home leaves the app, openApp brings it back where it was 8.6s
🟢 device fixture › closeApp then openApp relaunches on the home list 10.5s
🟢 device fixture › foregroundApp after closeApp reports the closed session as APP_NOT_OPEN 6.8s
🟢 device fixture › device.back pops the scenario 8.7s
🟢 device fixture › setLocation and clearLocation change what the app reads 15.2s
🟢 device fixture › enrollBiometrics and setBiometrics answer a biometric prompt 10.9s
🟢 tests/device.e2e.ts · 2 passed 58.7s
🟢 sequential onboarding echoes the exact values 43.8s
🟢 onboarding rejects a malformed email before advancing 14.9s
🟢 tests/flows-lists.e2e.ts · 7 passed 4m 18s
🟢 infinite scroll reaches the target item 31.8s
🟢 product catalog adds exactly two of the right variant 12.8s
🟢 gestures: long-press, then swipe left 11.2s
🟢 sticky chrome: accept below the fold, then continue 12.8s
🟢 async states: load, pull to refresh, claim through the toast 14.3s
🟢 huge virtualized list reaches row 512 2m 39s
🟢 web view coupon form applies the code shown on the page 16.6s
🟢 tests/flows.e2e.ts · 7 passed 1m 31s
🟢 text inputs unlock the submit button 20.8s
🟢 filled text inputs report their values 12.6s
🟢 modal flow confirms through the native alert 11.8s
🟢 bottom tabs act inside the last tab 9.4s
🟢 error recovery retries, then confirms the delete 12.0s
🟢 choice controls place the exact order 10.9s
🟢 debounced search selects the target once results arrive 13.6s
🟢 tests/login-form.e2e.ts · 3 passed 57.5s
🟢 login form › rejects a malformed email 15.2s
🟢 login form › rejects an unknown account 15.2s
🟢 login form › signs in and logs out 27.1s
🟢 tests/smoke.e2e.ts · 1 passed 15.2s
🟢 home lists the scenarios and opens one 15.2s

e2e 0.16.0 · 7m 23s · ios-simulator · run artifacts

@github-actions

github-actions Bot commented Oct 3, 2026 •

Copy link
Copy Markdown
Contributor

🟢 e2e android: 1 flaky, 53 passed, 1 skipped

⚠️ 1 flaky test passed on a retry

⚠️ sequential onboarding echoes the exact values
tests/device.e2e.ts:52

ENGINE_FAILURE at step 7 of 7: locator.fill getByTestId("first-name-input"), after 4.7s

perform fill failed: Android fill verification failed Hint: Retry with --debug and inspect diagnostics log for details.

  • Screen: dev.e2e.benchmark

Evidence: screenshot, log

All 55 tests in 7 files
Test Time
⚠️ tests/device.e2e.ts · 1 flaky, 3 passed 38.4s
🟢 permission prompt › allowing the system dialog enables the microphone 6.7s
🟢 permission prompt › a denied prompt recovers once the permission is granted outside 12.3s
⚠️ sequential onboarding echoes the exact values (1 failed attempt first) 12.7s
🟢 onboarding rejects a malformed email before advancing 6.7s
⏭️ tests/controls.e2e.ts · 24 passed, 1 skipped 2m 55s
🟢 control inventory › a label query resolves a labeled text field 20.7s
🟢 control inventory › a labeled field answers name, display value, value reads, fill, and clear 13.0s
🟢 control inventory › a placeholder query and attribute reads find the field 5.2s
🟢 control inventory › a secure field takes a fill and denies value reads 6.5s
🟢 control inventory › press sends a character and Enter through the soft keyboard 8.6s
🟢 control inventory › a field autofocuses on mount and focus() moves the focus 11.2s
🟢 control inventory › toBeFocused follows the focus 10.1s
🟢 control inventory › a switch checks, unchecks, and reports its state 9.5s
🟢 control inventory › checkbox and radio roles answer getByRole, and a checkbox toggles by tap 7.0s
🟢 control inventory › checkbox and radio roles check, uncheck, and report their state 11.9s
🟢 control inventory › a tab role answers getByRole 3.6s
🟢 control inventory › a radio group reports the checked option 8.9s
🟢 control inventory › buttons report disabled, enabled, and a state that arrives later 7.6s
🟢 control inventory › a display: none element is absent rather than hidden, its visible twin attached 6.2s
🟢 control inventory › a header answers its accessible name 3.7s
🟢 control inventory › a tab row answers count, all, texts, filters, positions, and switching 5.5s
🟢 control inventory › a list answers counts, list-form text, reads, and boxes in order 5.0s
⏭️ control inventory › a row contains its texts (skipped: React Native flattens a plain View out of the accessibility tree on both platforms: XCTest and the Android helper report a View with a testID as a leaf beside its children, so filter({ has }) and a query scoped to it match nothing; only Sc…)
🟢 control inventory › a long press lands as one 7.0s
🟢 control inventory › a double tap lands inside the 300 ms window 7.7s
🟢 control inventory › an ambiguous locator fails at once 4.9s
🟢 control inventory › screenshot, restart, and clearState reopen the app on its home list 8.4s
🟢 refusals › verbs a device never declares are refused before the engine 2ms
🟢 refusals › keys the soft keyboard cannot send are refused by the engine 1.3s
🟢 refusals › a missing locator fails as not found once its timeout passes 1.1s
🟢 tests/device-fixture.e2e.ts · 8 passed 1m 2s
🟢 device fixture › setOrientation rotates the window the app measures 4.9s
🟢 device fixture › home leaves the app, openApp brings it back where it was 5.5s
🟢 device fixture › foregroundApp sees the home screen after home() 3.4s
🟢 device fixture › closeApp then openApp relaunches on the home list 6.0s
🟢 device fixture › foregroundApp after closeApp reports the closed session as APP_NOT_OPEN 3.9s
🟢 device fixture › device.back pops the scenario 4.4s
🟢 device fixture › setNetwork and setAirplaneMode change what the app sees 15.1s
🟢 device fixture › setLocation and clearLocation change what the app reads 18.8s
🟢 tests/flows-lists.e2e.ts · 7 passed 2m 13s
🟢 infinite scroll reaches the target item 23.6s
🟢 product catalog adds exactly two of the right variant 9.3s
🟢 gestures: long-press, then swipe left 7.4s
🟢 gestures: double-tap completes the flow 9.4s
🟢 sticky chrome: accept below the fold, then continue 11.2s
🟢 async states: load, pull to refresh, claim through the toast 11.4s
🟢 huge virtualized list reaches row 512 1m 1s
🟢 tests/flows.e2e.ts · 7 passed 50.4s
🟢 text inputs unlock the submit button 6.1s
🟢 filled text inputs report their values 5.5s
🟢 modal flow confirms through the native alert 7.8s
🟢 bottom tabs act inside the last tab 6.3s
🟢 error recovery retries, then confirms the delete 8.1s
🟢 choice controls place the exact order 8.3s
🟢 debounced search selects the target once results arrive 8.3s
🟢 tests/login-form.e2e.ts · 3 passed 29.4s
🟢 login form › rejects a malformed email 8.4s
🟢 login form › rejects an unknown account 8.5s
🟢 login form › signs in and logs out 12.5s
🟢 tests/smoke.e2e.ts · 1 passed 5.1s
🟢 home lists the scenarios and opens one 5.1s

e2e 0.16.0 · 8m 28s · android-emulator · run artifacts

@github-actions

github-actions Bot commented Oct 3, 2026 •

Copy link
Copy Markdown
Contributor

🟢 e2e android agent: 20 passed

24 agent steps · 132 model calls · 839.5k tokens (83% cached) · $0.0549

All 20 tests in 1 file
Test Agent Time
🟢 tests-agent/scenarios.e2e.ts · 20 passed 24 steps · 132 calls 10m 44s
🟢 act completes Login Form 1 step · 3 calls 29.9s
🟢 act completes Infinite Scroll List 1 step · 3 calls 31.3s
🟢 act completes Modal Flow 1 step · 4 calls 10.6s
🟢 act completes Bottom Tabs 1 step · 3 calls 7.9s
🟢 act completes Text Input Variations 1 step · 3 calls 10.0s
🟢 act completes Broken Accessibility 2 steps · 6 calls 28.1s
🟢 act completes Vision Only 2 steps · 6 calls 28.2s
🟢 act completes Gestures 1 step · 10 calls 35.4s
🟢 act completes Async States 1 step · 22 calls 1m 20s
🟢 act completes Debounced Search 1 step · 4 calls 9.7s
🟢 act completes Huge Virtualized List 1 step · 3 calls 1m 13s
🟢 act completes Flattened Registration Form 2 steps · 8 calls 46.7s
🟢 act completes Flattened Login 2 steps · 6 calls 27.4s
🟢 act completes Sticky Chrome Target 1 step · 4 calls 14.8s
🟢 act completes WebView Accessibility 1 step · 5 calls 21.9s
🟢 act completes Permission Prompt 1 step · 3 calls 8.3s
🟢 act completes Product Catalog 1 step · 5 calls 12.7s
🟢 act completes Error Recovery 1 step · 7 calls 13.0s
🟢 act completes Choice Controls 1 step · 7 calls 41.0s
🟢 act completes Sequential Onboarding 1 step · 20 calls 1m 53s

e2e 0.16.0 · 10m 44s · android-emulator · run artifacts

@github-actions

github-actions Bot commented Oct 3, 2026 •

Copy link
Copy Markdown
Contributor

🟢 e2e ios agent: 1 flaky, 20 passed, 1 skipped

25 agent steps · 139 model calls · 1.0M tokens (79% cached) · $0.0787

⚠️ 1 flaky test passed on a retry

⚠️ act completes Gestures
tests-agent/scenarios.e2e.ts:257

ACTION_FAILED at step 4 of 4: agent.act "Complete this scenario as the screen instructs: long-press the first target, swipe the card left, then double-tap the last target", after 1m 14s and 14 model calls

agent.act failed: Long-press advanced to step 2, and a leftward swipe advanced to step 3. I double-tapped the final target, but the screen remained on “Step 3 of 3 — Double-tap the button below,” with no visible completion outcome.

  • Turn 11: tap({"target":"n4615"}) → [tap] Tapped #n4615.
  • Turn 12: tap_at({"x":175,"y":465}) → [tap_at] Tapped the point (199, 530); no listed control is there (under it: #n4645 other "Double-tap me" testid="double-tap-target").
  • Turn 13: tap_at({"x":175,"y":465}) → [tap_at] Tapped the point (199, 530); no listed control is there (under it: #n4675 other "Double-tap me" testid="double-tap-target").
  • Screen: dev.e2e.benchmark / Benchmark Examples

Evidence: screenshot, log

All 22 tests in 1 file
Test Agent Time
⚠️ tests-agent/scenarios.e2e.ts · 1 flaky, 20 passed, 1 skipped 25 steps · 139 calls 13m 27s
🟢 act completes Login Form 1 step · 13 calls 1m 17s
🟢 act completes Infinite Scroll List 1 step · 3 calls 54.1s
🟢 act completes Modal Flow 1 step · 4 calls 17.0s
🟢 act completes Bottom Tabs 1 step · 3 calls 12.6s
🟢 act completes Text Input Variations 1 step · 3 calls 26.4s
🟢 act completes Broken Accessibility 2 steps · 7 calls 37.5s
🟢 act completes Vision Only 2 steps · 6 calls 30.5s
⚠️ act completes Gestures (1 failed attempt first) 1 step · 7 calls 38.3s
🟢 act completes Async States 1 step · 7 calls 31.3s
🟢 act completes Debounced Search 1 step · 4 calls 20.6s
🟢 act completes Flattened Registration Form 2 steps · 21 calls 2m 13s
🟢 act completes Flattened Login 2 steps · 13 calls 1m 19s
🟢 act completes Sticky Chrome Target 1 step · 7 calls 34.7s
🟢 act completes WebView Accessibility 1 step · 5 calls 27.8s
🟢 act completes Permission Prompt 1 step · 3 calls 19.2s
🟢 act completes Photo Picker 1 step · 4 calls 18.8s
🟢 act completes Product Catalog 1 step · 5 calls 18.3s
🟢 act completes Error Recovery 1 step · 6 calls 18.5s
🟢 act completes Choice Controls 1 step · 8 calls 43.3s
🟢 act completes Stripe PaymentSheet 1 step · 6 calls 46.3s
🟢 act completes Apple Pay 1 step · 4 calls 23.7s
⏭️ act completes Apple Pay Billing Address (skipped: the CI simulator's Apple Pay sheet skips the billing-address step, so the scenario cannot be completed there)

e2e 0.16.0 · 14m 52s · ios-simulator · run artifacts

@okwasniewski

Copy link
Copy Markdown
Member Author

[claude-opus-5-5] responding on behalf of okwasniewski

For the human review:

  • Node floor is ^22.22.3 || >=24.8.0, not the >=24.11.0 approved in the session: bisecting real binaries shows the fix (esm: populate separate cache for require(esm) in imported CJS nodejs/node#59679) landed in 24.8.0 (24.7.0 fails, 24.8.0 passes, and the whole e2e suite passes on 24.8.0 and 22.22.3; both are now in the CI matrix). Say if you want 24.11.0 anyway.
  • Three behavior changes to sign off on (changeset and docs/reference/config.mdx): ESM import statements in .cts are no longer turned into require, standard decorators need experimentalDecorators, and a CommonJS-written .ts that a .cjs file requires now needs the .cts extension.
  • Not verified: Windows (the loader has no platform branches; docs require WSL) and pnpm 11 specifically (verified pnpm 12.6.0 and 12.9.0).
  • Left as is: packages/e2e/tests/live/oauth.ts still documents npx tsx for hand-run checks; tsx stays in the repo's dev tree only through vite's optional peer, so allowBuilds: esbuild in pnpm-workspace.yaml stays.

@okwasniewski okwasniewski left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On 44a714f:

The new STACK_FRAME regex in collect/registry.ts:501 misses V8's anonymous async frame. When a shared helper in another file awaits something and then calls test(), V8 shows the test module as at async file:///.../a.test.ts:3:1, with no function name and no parens. The capture becomes async file:///.... It doesn't start with file:, so it isn't converted and never matches moduleFile. The test's source location then points at the helper instead of the test file. The old regex took only the trailing location, so main handles this frame. I reproduced the stack shape on Node 26. Allowing an optional async prefix fixes it: /^at (?:async )?(?:[^(]*? \()?(.+?):(\d+):(\d+)\)?$/. A captureSource case with a cross-file async helper would cover it.

reviewed 44a714f by Hermes on behalf of @okwasniewski

Comment thread packages/e2e/src/collect/registry.ts Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 21 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread packages/e2e/src/config/esm-hooks.ts Outdated
@okwasniewski

Copy link
Copy Markdown
Member Author

[claude-opus-5-5] responding on behalf of okwasniewski

Review round addressed (head 3ff7ada). For the human:

  • util.getCallSites was tried for the test location and backed out: it drops at async frames, and Error.prepareStackTrace gets no call sites while source maps are on. The stack text stays the source, with the async regex from your thread and an integration case for it.
  • The require banner for compiled .cts stays a text insertion: oxc 0.152 has no option that emits it (define also strips unused declarations; inject emits an ESM import). It is gated to Node releases before modules: fix sync hook short-circuit in require() in imported CJS nodejs/node#62920 (24.18.0, 26.2.0), joins the last directive, and only shifts the map by one unmapped line when there is no directive.
  • oxc's export {}; in CommonJS output is not reported upstream yet. Code pointer is in withoutModuleMarker.
  • web agent replays 0 of 34 steps on this head, same as main since fix(web): a check whose control the click replaces passes and replays #788 (feat(web): locale and timezoneId options set the language and time zone every attempt runs in #786 and feat(agent): identify e2e on every model request #784 replayed 25). Local main and branch match step for step, so this PR moves no replay key. The recordings need a re-record on main.
  • lifecycle.test.ts > splits a running trace around a video started mid-attempt failed twice on test (22.22.3) here. It also fails on main (runs 37136353682, 37135435714, 37134069364) and passes locally on 22.22.3. Passed on the second rerun.
  • scripts/check-install-scripts.ts stays next to the new install smoke job (pnpm 11 and 12) because it names the dependency chain; delete it if you only want the smoke job.

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 22 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread packages/e2e/src/config/specifiers.ts Outdated
Comment thread packages/e2e/src/run/worker/entry.ts Outdated
Comment thread packages/e2e/src/config/load.ts
Comment thread packages/e2e/src/config/typescript.ts
Comment thread .changeset/oxc-typescript-loader.md Outdated
Comment thread packages/e2e/tests/unit/esm-hooks.test.ts
@okwasniewski

Copy link
Copy Markdown
Member Author

Edge-case round done (b263fb8, d8c8129, 9b01e2e, 81e37dd, on top of the rebased a0f8df3). Each finding has a repro in /tmp/edge-repros/<name> and a committed regression test.

finding outcome
.cts with no source (Yarn PnP) fixed: read from disk. Node 24.8.0 to 24.11.0 rejects the sourceless result before any hook (fixed in 24.11.1), documented in cli.mdx
auto-accessors no oxc release lowers them: refused with file:line:col and the fix (getter/setter), documented
exports/imports target without extension fixed, also when a lax resolver earlier in the chain returns the missing file
.cts trailing comment after export {} marker fixed: marker found via oxc-parser, not the last line
tsconfig extends not installed one warning from the runner, compiles with defaults
decorator metadata naming an interface error names file:line and import type { X }
nested loader source maps composed with @jridgewell/remapping
Windows require() paths config/specifiers.ts, unit tests with path.win32. No Windows CI job added: the integration suite is POSIX-specific, so a job would cost a port of the suite first
nits file:line on evaluation errors, Bun/Deno refused in bin.ts, lazy oxc with TYPESCRIPT_COMPILER_UNAVAILABLE, require.resolve on the floors
cheap pre-existing gaps Vite references, directory main, .mts index

Package-manager matrix (verdaccio tarballs, main e2e@0.16.0 vs branch) is in the PR body: every manager installs and runs the TS fixture on the branch; main fails the fixture everywhere and install on pnpm 11/12.

Follow-ups, not in this PR:

  • jsconfig.json is not read.
  • @jsxImportSource pragma per file.
  • import.meta.url of a compiled file carries ?e2e=.
  • .cts/.cjs stay cached across an e2e-graph reload (Node's CommonJS cache).
  • .mts implied index follows Node, not TypeScript.
  • Nested tsconfig references (only one level).
  • Upstream: oxc export {} marker in CommonJS output (crates/oxc_transformer/src/typescript/annotations.rs).

@okwasniewski

Copy link
Copy Markdown
Member Author

Update, 3752276: cubic's 6 threads fixed or answered (FILE: URLs, foreign infrastructure errors, changeset wording, #dir test, UTF-16 offsets confirmed and locked by a test). Correction to the table above: a worker now also warns about an unreadable tsconfig.json the runner never reached (shown under the test that reached it); the runner passes the ones it warned about, so each warns once per run. All CI green, labeled.

@okwasniewski
okwasniewski merged commit 0d1ed15 into main Oct 4, 2026
23 checks passed
@okwasniewski
okwasniewski deleted the oskar/oxc-loader branch October 4, 2026 07:22
@github-actions github-actions Bot mentioned this pull request Oct 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant