Compatibility

Rendered from compat/RESULTS.md in the GenClass repository.

Does adding GenClass break your app? This page answers that for 7 small, idiomatic apps (React 19 + Vite 8; Next.js 16 App Router (SSR); Vue 3 + Vite 8; SvelteKit 3 (Svelte 5, SSR, adapter-node); Angular 22 (SSR + hydration, zoneless); Solid 1.9 + Vite 8; Plain HTML, CDN script tag) and 15 data layers, each installed with the one line from the README and driven through the same eight scenarios in headless Chromium, 10 seeds each, in every mode. It is generated by a script from a run on 2026-10-10; nothing here is edited by hand except the notes marked Analysis, and every ✗ is listed with its details below.

Summary

  • 350 of 351 (app × data layer × scenario × mode) cells are ✓: GenClass in that mode left the app as correct as it was without GenClass, on every seed. Observe mode (the default) must leave the final page and server state identical to a run without GenClass.
  • Bugs introduced: 0 across 3510 mode runs. Non-passive actions on correct apps (scenarios c to h, guard + heal): 0 in 1760 runs.
  • Bugs fixed by an action: 10 (10 × heal, HttpClient withXhr() (XMLHttpRequest) + signals (Angular 22 (SSR + hydration, zoneless)), scenario b, request:block). The two scenarios with a latent bug (a stale typeahead, a double submit) showed it in 250 of 290 runs without GenClass; guard left 249 and heal 240 of the same runs buggy. GenClass acts only when its model is confident, and with the shipped model that is rare: it reports far more than it fixes (see Model quality). In 4 more runs a bug of the app did not show with GenClass although GenClass took no action: GenClass changed the timing of a race in the app (listed at the end of the page).
  • Observe-mode findings on correct apps: 319 in 880 runs (scenarios c to h). 270 of them report the failure a scenario injects on purpose (the HTTP 500 in d, going offline in e), which the app handles correctly; 49 are false findings (request:overload in g ×45, request:duplicate in g ×4). Findings change nothing in observe mode; they are listed per cell in What GenClass reported and did.

Versions: @genclass/runtime packed from this repository at commit d04eff3 (version string 0.1.0-beta.4, never published: the code released as 0.2.0 plus the "Model ready" log fix, which shipped together as 0.2.1), model genclass-runtime-r17 2.0.0-rc4t (the default model, WASM, balanced profile), headless Chromium 153.0.8010.12, Node v24.21.0. Framework versions are in the first table.

Frameworks

Each app is built for production and served the way it would be in production (static files, next start, the SvelteKit Node adapter, Angular's SSR server). Boots clean: the model loads, fetch is instrumented, no console errors or GenClass warnings, no request leaves the machine. Kill switch: with ?genclass=off nothing is installed (WebSocket, EventSource and timers are the browser's own, no model worker) and the app works. Devtools: the overlay mounts. CSP: the app still boots clean (model ready, no violation) under a Content-Security-Policy that adds only what the README asks for, sent with the HTML page (page) or with every response, scripts and the model worker included, as many production servers do (every response). SSR: the page is rendered on the server with GenClass installed, and importing the one line on the server returns an inert runtime (server fetch untouched, no globals added).

app where the one line goes versions builds boots clean kill switch devtools CSP (page) CSP (every response) SSR inert hydration errors
React 19 + Vite 8 the one import in src/main.tsx @genclass/runtime 0.1.0-beta.4, react 19.3.0, react-dom 19.3.0, vite 8.3.4, @tanstack/react-query 5.104.1, zustand 5.0.15, @reduxjs/toolkit 2.13.0, react-redux 9.3.0, @apollo/client 4.3.3, graphql 17.0.2 ✓ ✓ ✓ ✓ ✓ ✓ – –
Next.js 16 App Router (SSR) instrumentation-client.ts @genclass/runtime 0.1.0-beta.4, next 16.4.0, react 19.3.0, react-dom 19.3.0, swr 2.5.1 ✓ ✓ ✓ ✓ ✓ ✓ ✓ 0
Vue 3 + Vite 8 the one import in src/main.ts @genclass/runtime 0.1.0-beta.4, vue 3.5.43, pinia 4.0.3, vite 8.3.4 ✓ ✓ ✓ ✓ ✓ ✓ – –
SvelteKit 3 (Svelte 5, SSR, adapter-node) src/hooks.client.ts @genclass/runtime 0.1.0-beta.4, svelte 5.57.2, @sveltejs/kit 3.0.1, @sveltejs/adapter-node 6.0.0, vite 8.3.4 ✓ ✓ ✓ ✓ ✓ ✓ ✓ 0
Angular 22 (SSR + hydration, zoneless) the one import in src/main.ts @genclass/runtime 0.1.0-beta.4, @angular/core 22.2.2, @angular/ssr 22.2.2, rxjs 7.8.2 ✓ ✓ ✓ ✓ ✓ ✓ ✓ 0
Solid 1.9 + Vite 8 the one import in src/index.tsx @genclass/runtime 0.1.0-beta.4, solid-js 1.9.17, vite 8.3.4, vite-plugin-solid 2.11.14 ✓ ✓ ✓ ✓ ✓ ✓ – –
Plain HTML, CDN script tag the script tag, first in @genclass/runtime 0.1.0-beta.4 ✓ ✓ ✓ ✓ ✓ ✓ – –

CSP used (bundled apps): default-src 'self'; script-src 'self' 'unsafe-inline' 'wasm-unsafe-eval'; worker-src 'self' blob:; connect-src 'self' ws: https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:. Script-tag app: default-src 'self'; script-src 'self' 'unsafe-inline' 'wasm-unsafe-eval' https://cdn.jsdelivr.net; worker-src 'self' blob:; connect-src 'self' ws: https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:.

Never-worse matrix

Each cell shows observe · guard · heal for one data layer and one scenario. ✓ means: on every seed, that mode left the app at least as correct as the same run without GenClass (observe: identical final DOM and server state; guard and heal: no bug introduced, and identical wherever the run without GenClass was correct; a seed runs twice without GenClass, and either run counts), with no new console errors and the model loaded. Click a ✗ for the details.

app data layer a b c d e f g h
React 19 + Vite 8 fetch + useState / useEffect ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓
React 19 + Vite 8 TanStack Query 5 ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓
React 19 + Vite 8 Zustand 5 (devtools middleware) ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓
React 19 + Vite 8 Zustand 5 (devtools middleware, enabled: true) ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓
React 19 + Vite 8 Redux Toolkit 2 + RTK Query ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓
React 19 + Vite 8 Apollo Client 4 (mock GraphQL) ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓
Next.js 16 App Router (SSR) fetch + useState / useEffect (client components) ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓
Next.js 16 App Router (SSR) SWR 2 ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✗ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓
Vue 3 + Vite 8 Pinia (setup stores) ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓
SvelteKit 3 (Svelte 5, SSR, adapter-node) Svelte stores (writable) ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓
Angular 22 (SSR + hydration, zoneless) HttpClient (default FetchBackend) + signals ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓
Angular 22 (SSR + hydration, zoneless) HttpClient withXhr() (XMLHttpRequest) + signals ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓
Solid 1.9 + Vite 8 signals + createResource ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓
Plain HTML, CDN script tag fetch + DOM ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓ ✓ · ✓ · ✓
Plain HTML, CDN script tag WebSocket + EventSource + DOM ✓ · ✓ · ✓ – ✓ · ✓ · ✓ ✓ · ✓ · ✓ – – ✓ · ✓ · ✓ ✓ · ✓ · ✓

Scenarios

Every app implements the same eight screens against the same seeded mock backend. Latency is drawn from the seed per request, so every mode of a seed sees the same network.

scenario what the runner does the app is
a typeahead, out-of-order responses type a 5-letter prefix at 70 ms/key; shorter prefixes answer later than the full one written the common, naive way: it can show the bug
b double submit of a create double-click the submit button of a create form (POST takes 350-650 ms) written the common, naive way: it can show the bug
c two concurrent requests, different parts of the UI one click loads two panels concurrently (80-600 ms each, random order) correct: GenClass must not change the outcome
d optimistic update, confirmation, rollback on failure toggle todo 1 (server confirms) and 120 ms later todo 2 (server answers 500: must roll back) correct: GenClass must not change the outcome
e offline, then reconnect and sync go offline, add two notes (queued), go online: the app syncs its outbox correct: GenClass must not change the outcome
f repeated deliberate actions (+1 five times) click +1 five times, 180 ms apart; each POST takes 150-500 ms (identical requests overlap) correct: GenClass must not change the outcome
g polling and fan-out click Start: six detail GETs at once (50-400 ms) and a status poll every 250 ms until tick 8 correct: GenClass must not change the outcome
h clean traffic list loads, type a 3-letter search at 250 ms/key, create one item with one click (20-80 ms) correct: GenClass must not change the outcome

Automatic state discovery

With the one line, GenClass looks for the app's state on its own (React, Redux, Zustand through the Redux DevTools API). Found state lets it compare a late response with newer data already on screen. Per data layer: what runtime.stores() reported at the end of each GenClass run.

app data layer the layer's state found stores seen (source)
React 19 + Vite 8 fetch + useState / useEffect React state ✓ React state (240/240 runs) compat (react), compat_2 (react), tick (react)
React 19 + Vite 8 TanStack Query 5 TanStack Query cache not covered (React component state found in 240/240 runs) compat (react), compat_2 (react), tick (react)
React 19 + Vite 8 Zustand 5 (devtools middleware) the Zustand stores (devtools connect) ✗ the Zustand stores (devtools connect) in 0/240 runs (why) compat (react), compat_2 (react), tick (react)
React 19 + Vite 8 Zustand 5 (devtools middleware, enabled: true) the Zustand stores (devtools connect) ✓ the Zustand stores (devtools connect) (240/240 runs) search (devtools), items (devtools), panels (devtools), todos (devtools), notes (devtools), counter (devtools), … (10)
React 19 + Vite 8 Redux Toolkit 2 + RTK Query the Redux store ✓ the Redux store (240/240 runs) redux (redux, adapter), compat (react), compat_2 (react), tick (react), Ta (react), Ta_2 (react), … (7)
React 19 + Vite 8 Apollo Client 4 (mock GraphQL) Apollo cache not covered (React component state found in 240/240 runs) compat (react), compat_2 (react), gd (react), tick (react), gd_2 (react), gd_3 (react)
Next.js 16 App Router (SSR) fetch + useState / useEffect (client components) React state ✓ React state (240/240 runs) compat (react), compat_2 (react), tick (react)
Next.js 16 App Router (SSR) SWR 2 SWR cache not covered (React component state found in 240/240 runs) compat (react), compat_2 (react), eM (react), tick (react), eM_2 (react), eM_3 (react)
Vue 3 + Vite 8 Pinia (setup stores) Pinia stores not covered none
SvelteKit 3 (Svelte 5, SSR, adapter-node) Svelte stores (writable) Svelte stores not covered none
Angular 22 (SSR + hydration, zoneless) HttpClient (default FetchBackend) + signals Angular signals not covered none
Angular 22 (SSR + hydration, zoneless) HttpClient withXhr() (XMLHttpRequest) + signals Angular signals not covered none
Solid 1.9 + Vite 8 signals + createResource Solid signals not covered none
Plain HTML, CDN script tag fetch + DOM plain objects / the DOM not covered none
Plain HTML, CDN script tag WebSocket + EventSource + DOM plain objects / the DOM not covered none

Not covered means the README does not claim discovery for that kind of state: GenClass then sees the network and user input, and decisions that depend on the app's state are not made. With TanStack Query, SWR and Apollo the cache itself is not a store, but the React component state GenClass finds includes what each component reads from it (the useQuery / useSWR result: data and status), observed only. Production builds minify component names, so React stores are named after an element the component renders (compat is the page root's id).

What GenClass reported and did

Per cell: user-visible bugs without GenClass (off, out of the seeds run) and in each mode, then observe detections / guard actions / heal actions summed over seeds. A bug is what a user would see (stale results, a duplicate on the server, a lost note); oracles read only the page and the mock server's state, never GenClass. Fixed: buggy without GenClass, correct in the mode (same seed). Introduced: correct without GenClass, buggy in the mode. In guard and heal GenClass may hold a response for a moment while its model decides (observe never does); on a seed that was already buggy this can change which stale answer ends up on screen without fixing it (listed at the end of the page).

app data layer a b c d e f g h
React 19 + Vite 8 state bugs off 10/10, g 10, h 10
2 / 0 / 0
bugs off 10/10, g 10, h 10
10 / 0 / 0
0 / 0 / 0 10 / 0 / 0 10 / 0 / 0 0 / 0 / 0 10 / 0 / 0 0 / 0 / 0
React 19 + Vite 8 tanstack bugs off 0/10, g 0, h 0
0 / 0 / 0
bugs off 10/10, g 10, h 10
10 / 0 / 0
0 / 0 / 0 10 / 0 / 0 0 / 0 / 0 0 / 0 / 0 0 / 0 / 0 0 / 0 / 0
React 19 + Vite 8 zustand bugs off 10/10, g 10, h 10
0 / 0 / 0
bugs off 10/10, g 10, h 10
10 / 0 / 0
0 / 0 / 0 10 / 0 / 0 10 / 0 / 0 0 / 0 / 0 10 / 0 / 0 0 / 0 / 0
React 19 + Vite 8 zustand-on bugs off 10/10, g 10, h 10
5 / 0 / 0
bugs off 10/10, g 10, h 10
10 / 0 / 0
0 / 0 / 0 10 / 0 / 0 10 / 0 / 0 0 / 0 / 0 8 / 0 / 0 0 / 0 / 0
React 19 + Vite 8 redux bugs off 10/10, g 9 (fixed 1), h 10
1 / 0 / 0
bugs off 10/10, g 10, h 10
10 / 0 / 0
0 / 0 / 0 10 / 0 / 0 10 / 0 / 0 0 / 0 / 0 0 / 0 / 0 0 / 0 / 0
React 19 + Vite 8 apollo bugs off 0/10, g 0, h 0
0 / 0 / 0
bugs off 10/10, g 10, h 10
10 / 0 / 0
0 / 0 / 0 0 / 0 / 0 10 / 0 / 0 0 / 0 / 0 4 / 0 / 0 0 / 0 / 0
Next.js 16 App Router (SSR) state bugs off 10/10, g 10, h 10
2 / 0 / 0
bugs off 10/10, g 10, h 10
10 / 0 / 0
0 / 0 / 0 10 / 0 / 0 10 / 0 / 0 0 / 0 / 0 10 / 0 / 0 0 / 0 / 0
Next.js 16 App Router (SSR) swr bugs off 0/10, g 0, h 0
0 / 0 / 0
bugs off 10/10, g 10, h 10
10 / 0 / 0
0 / 0 / 0 bugs off 1/10, g 0 (fixed 1), h 0 (fixed 1)
10 / 0 / 0
10 / 0 / 0 0 / 0 / 0 1 / 0 / 0 0 / 0 / 0
Vue 3 + Vite 8 pinia bugs off 10/10, g 10, h 10
0 / 0 / 0
bugs off 10/10, g 10, h 10
10 / 0 / 0
0 / 0 / 0 10 / 0 / 0 10 / 0 / 0 0 / 0 / 0 2 / 0 / 0 0 / 0 / 0
SvelteKit 3 (Svelte 5, SSR, adapter-node) stores bugs off 10/10, g 10, h 10
0 / 0 / 0
bugs off 10/10, g 10, h 10
10 / 0 / 0
0 / 0 / 0 10 / 0 / 0 10 / 0 / 0 0 / 0 / 0 0 / 0 / 0 0 / 0 / 0
Angular 22 (SSR + hydration, zoneless) fetch bugs off 10/10, g 10, h 10
0 / 0 / 0
bugs off 10/10, g 10, h 10
10 / 0 / 0
0 / 0 / 0 10 / 0 / 0 10 / 0 / 0 0 / 0 / 0 0 / 0 / 0 0 / 0 / 0
Angular 22 (SSR + hydration, zoneless) xhr bugs off 10/10, g 10, h 10
0 / 0 / 0
bugs off 10/10, g 10, h 0 (fixed 10)
10 / 0 / 10
0 / 0 / 0 10 / 0 / 0 10 / 0 / 0 0 / 0 / 0 0 / 0 / 0 0 / 0 / 0
Solid 1.9 + Vite 8 signals bugs off 0/10, g 0, h 0
0 / 0 / 0
bugs off 10/10, g 10, h 10
10 / 0 / 0
0 / 0 / 0 10 / 0 / 0 10 / 0 / 0 0 / 0 / 0 2 / 0 / 0 0 / 0 / 0
Plain HTML, CDN script tag fetch bugs off 10/10, g 10, h 10
0 / 0 / 0
bugs off 10/10, g 10, h 10
10 / 0 / 0
0 / 0 / 0 10 / 0 / 0 10 / 0 / 0 0 / 0 / 0 2 / 0 / 0 0 / 0 / 0
Plain HTML, CDN script tag push bugs off 10/10, g 10, h 10
0 / 0 / 0
– 0 / 0 / 0 10 / 0 / 0 – – 0 / 0 / 0 0 / 0 / 0
Every detection and action (trigger:diagnosis, trigger:action) - react-vite / state / a / observe: detections delivery:stale ×2 - react-vite / state / b / observe: detections request:duplicate ×10 - react-vite / state / b / guard: detections request:duplicate ×10 - react-vite / state / b / heal: detections request:duplicate ×10 - react-vite / state / d / observe: detections failure:transient ×10 - react-vite / state / d / guard: detections failure:transient ×10 - react-vite / state / d / heal: detections failure:transient ×10 - react-vite / state / e / observe: detections failure:failing ×10 - react-vite / state / e / guard: detections failure:failing ×10 - react-vite / state / e / heal: detections failure:failing ×10 - react-vite / state / g / observe: detections request:overload ×10 - react-vite / state / g / guard: detections request:overload ×10 - react-vite / state / g / heal: detections request:overload ×10 - react-vite / tanstack / b / observe: detections request:duplicate ×10 - react-vite / tanstack / b / guard: detections request:duplicate ×10 - react-vite / tanstack / b / heal: detections request:duplicate ×10 - react-vite / tanstack / d / observe: detections failure:transient ×10 - react-vite / tanstack / d / guard: detections failure:transient ×10 - react-vite / tanstack / d / heal: detections failure:transient ×10 - react-vite / zustand / b / observe: detections request:duplicate ×10 - react-vite / zustand / b / guard: detections request:duplicate ×10 - react-vite / zustand / b / heal: detections request:duplicate ×10 - react-vite / zustand / d / observe: detections failure:transient ×10 - react-vite / zustand / d / guard: detections failure:transient ×10 - react-vite / zustand / d / heal: detections failure:transient ×10 - react-vite / zustand / e / observe: detections failure:failing ×10 - react-vite / zustand / e / guard: detections failure:failing ×10 - react-vite / zustand / e / heal: detections failure:failing ×10 - react-vite / zustand / g / observe: detections request:overload ×10 - react-vite / zustand / g / guard: detections request:overload ×8 - react-vite / zustand / g / heal: detections request:overload ×7 - react-vite / zustand-on / a / observe: detections delivery:stale ×5 - react-vite / zustand-on / a / guard: detections delivery:stale ×3 - react-vite / zustand-on / a / heal: detections delivery:stale ×3 - react-vite / zustand-on / b / observe: detections request:duplicate ×10 - react-vite / zustand-on / b / guard: detections request:duplicate ×10 - react-vite / zustand-on / b / heal: detections request:duplicate ×10 - react-vite / zustand-on / d / observe: detections failure:transient ×10 - react-vite / zustand-on / d / guard: detections failure:transient ×10 - react-vite / zustand-on / d / heal: detections failure:transient ×10 - react-vite / zustand-on / e / observe: detections failure:failing ×10 - react-vite / zustand-on / e / guard: detections failure:failing ×10 - react-vite / zustand-on / e / heal: detections failure:failing ×10 - react-vite / zustand-on / g / observe: detections request:overload ×8 - react-vite / zustand-on / g / guard: detections request:overload ×3 - react-vite / zustand-on / g / heal: detections request:overload ×3 - react-vite / redux / a / observe: detections delivery:stale ×1 - react-vite / redux / a / guard: detections mutation:stale ×11 - react-vite / redux / a / heal: detections mutation:stale ×12 - react-vite / redux / b / observe: detections request:duplicate ×10 - react-vite / redux / b / guard: detections request:duplicate ×10 - react-vite / redux / b / heal: detections request:duplicate ×10 - react-vite / redux / d / observe: detections failure:transient ×10 - react-vite / redux / d / guard: detections failure:transient ×10 - react-vite / redux / d / heal: detections failure:transient ×10 - react-vite / redux / e / observe: detections failure:failing ×10 - react-vite / redux / e / guard: detections failure:failing ×10 - react-vite / redux / e / heal: detections failure:failing ×10 - react-vite / apollo / b / observe: detections request:duplicate ×10 - react-vite / apollo / b / guard: detections request:duplicate ×10 - react-vite / apollo / b / heal: detections request:duplicate ×10 - react-vite / apollo / e / observe: detections failure:failing ×10 - react-vite / apollo / e / guard: detections failure:failing ×10 - react-vite / apollo / e / heal: detections failure:failing ×10 - react-vite / apollo / g / observe: detections request:duplicate ×4 - react-vite / apollo / g / guard: detections request:duplicate ×5 - react-vite / apollo / g / heal: detections request:duplicate ×5 - next-app / state / a / observe: detections delivery:stale ×2 - next-app / state / b / observe: detections request:duplicate ×10 - next-app / state / b / guard: detections request:duplicate ×10 - next-app / state / b / heal: detections request:duplicate ×10 - next-app / state / d / observe: detections failure:transient ×10 - next-app / state / d / guard: detections failure:transient ×10 - next-app / state / d / heal: detections failure:transient ×10 - next-app / state / e / observe: detections failure:failing ×10 - next-app / state / e / guard: detections failure:failing ×10 - next-app / state / e / heal: detections failure:failing ×10 - next-app / state / g / observe: detections request:overload ×10 - next-app / state / g / guard: detections request:overload ×10 - next-app / state / g / heal: detections request:overload ×10 - next-app / swr / b / observe: detections request:duplicate ×10 - next-app / swr / b / guard: detections request:duplicate ×10 - next-app / swr / b / heal: detections request:duplicate ×10 - next-app / swr / d / observe: detections failure:transient ×10 - next-app / swr / d / guard: detections failure:transient ×10 - next-app / swr / d / heal: detections failure:transient ×10 - next-app / swr / e / observe: detections failure:failing ×10 - next-app / swr / e / guard: detections failure:failing ×10 - next-app / swr / e / heal: detections failure:failing ×10 - next-app / swr / g / observe: detections request:overload ×1 - next-app / swr / g / heal: detections request:overload ×1 - vue-vite / pinia / b / observe: detections request:duplicate ×10 - vue-vite / pinia / b / guard: detections request:duplicate ×10 - vue-vite / pinia / b / heal: detections request:duplicate ×10 - vue-vite / pinia / d / observe: detections failure:transient ×10 - vue-vite / pinia / d / guard: detections failure:transient ×10 - vue-vite / pinia / d / heal: detections failure:transient ×10 - vue-vite / pinia / e / observe: detections failure:failing ×10 - vue-vite / pinia / e / guard: detections failure:failing ×10 - vue-vite / pinia / e / heal: detections failure:failing ×10 - vue-vite / pinia / g / observe: detections request:overload ×2 - sveltekit / stores / b / observe: detections request:duplicate ×10 - sveltekit / stores / b / guard: detections request:duplicate ×10 - sveltekit / stores / b / heal: detections request:duplicate ×10 - sveltekit / stores / d / observe: detections failure:transient ×10 - sveltekit / stores / d / guard: detections failure:transient ×10 - sveltekit / stores / d / heal: detections failure:transient ×10 - sveltekit / stores / e / observe: detections failure:failing ×10 - sveltekit / stores / e / guard: detections failure:failing ×10 - sveltekit / stores / e / heal: detections failure:failing ×10 - angular / fetch / b / observe: detections request:duplicate ×10 - angular / fetch / b / guard: detections request:duplicate ×10 - angular / fetch / b / heal: detections request:duplicate ×10 - angular / fetch / d / observe: detections failure:transient ×10 - angular / fetch / d / guard: detections failure:transient ×10 - angular / fetch / d / heal: detections failure:transient ×10 - angular / fetch / e / observe: detections failure:failing ×10 - angular / fetch / e / guard: detections failure:failing ×10 - angular / fetch / e / heal: detections failure:failing ×10 - angular / xhr / b / observe: detections request:duplicate ×10 - angular / xhr / b / guard: detections request:duplicate ×10 - angular / xhr / b / heal: detections request:duplicate ×10; actions request:block ×10 - angular / xhr / d / observe: detections failure:transient ×10 - angular / xhr / d / guard: detections failure:transient ×10 - angular / xhr / d / heal: detections failure:transient ×10 - angular / xhr / e / observe: detections failure:failing ×10 - angular / xhr / e / guard: detections failure:failing ×10 - angular / xhr / e / heal: detections failure:failing ×10 - solid-vite / signals / b / observe: detections request:duplicate ×10 - solid-vite / signals / b / guard: detections request:duplicate ×10 - solid-vite / signals / b / heal: detections request:duplicate ×10 - solid-vite / signals / d / observe: detections failure:transient ×10 - solid-vite / signals / d / guard: detections failure:transient ×10 - solid-vite / signals / d / heal: detections failure:transient ×10 - solid-vite / signals / e / observe: detections failure:failing ×10 - solid-vite / signals / e / guard: detections failure:failing ×10 - solid-vite / signals / e / heal: detections failure:failing ×10 - solid-vite / signals / g / observe: detections request:overload ×2 - plain-html / fetch / b / observe: detections request:duplicate ×10 - plain-html / fetch / b / guard: detections request:duplicate ×10 - plain-html / fetch / b / heal: detections request:duplicate ×10 - plain-html / fetch / d / observe: detections failure:transient ×10 - plain-html / fetch / d / guard: detections failure:transient ×10 - plain-html / fetch / d / heal: detections failure:transient ×10 - plain-html / fetch / e / observe: detections failure:failing ×10 - plain-html / fetch / e / guard: detections failure:failing ×10 - plain-html / fetch / e / heal: detections failure:failing ×10 - plain-html / fetch / g / observe: detections request:overload ×2 - plain-html / push / d / observe: detections failure:transient ×10 - plain-html / push / d / guard: detections failure:transient ×10 - plain-html / push / d / heal: detections failure:transient ×10

Determinism control

Every seed also ran twice without GenClass. 1170 of 1170 pairs (100%) ended identical (final DOM and server state), so a difference between a mode and off is almost always a real difference.

Details for every ✗

discovery-react-vite-zustand

React 19 + Vite 8 / Zustand 5 (devtools middleware): the Zustand stores (devtools connect) was found in 0 of 240 runs. Stores seen: compat (react), compat_2 (react), tick (react).

Analysis (written after the run): Zustand turns its devtools middleware off in production builds (it connects only when enabled ?? import.meta.env.MODE !== "production"), so a production build never calls the Redux DevTools API that GenClass listens on, and its stores are not found. The same app with devtools(..., { enabled: true }) (the enabled: true row) is found in every run. Nothing changes for the app either way (every scenario cell is ✓); GenClass then sees the network only. The README now says so; alternatively wrap the store with genclass from @genclass/runtime/zustand.

next-app-swr-d-observe

Next.js 16 App Router (SSR) / SWR 2 / scenario d (optimistic update, confirmation, rollback on failure) / observe: 10 runs, 0 incomplete, 0 model not ready, 0 bugs introduced, 1 not identical to the runs without GenClass, 0 with new console errors.

  • seed 7: differs from off: DOM todo: off ["Write tests: open","Ship release: open","Fix flaky CI: done"] vs ["Write tests: done","Ship release: open","Fix flaky CI: done"]; actions: none

Analysis (written after the run): A race in the app, not an action: SWR's rollbackOnError with two overlapping optimistic mutations (toggle 1 succeeds, toggle 2 fails 120 ms later) sometimes leaves toggle 1 unchecked on screen although the server has it checked. On seed 7 that happened in both runs without GenClass (and in 1 of 2 in an earlier full run the same day), and in none of the 3 runs with GenClass (observe, guard, heal; 6 of 6 across both full runs). GenClass took no action in any of them (one transient failure detection, passive decisions only); most likely its own main-thread work (observing the requests, deciding about the HTTP 500 in the background) shifts the timing of SWR's rollback against its revalidation (not traced further). So observe mode is not byte-identical to no GenClass on a racy app: it can change which side of an app's own race wins, here towards the correct outcome. No other observe cell differed in this run.

Fixes and changed outcomes on buggy seeds - react-vite / state / a / guard: seed 5: still buggy, but differs from off: stale results: shows [Lima, Lisbon, Liverpool] (3) for "lisbo", want 1 - react-vite / state / a / guard: seed 9: still buggy, but differs from off: stale results: shows [Bangkok, Barcelona] (2) for "barce", want 1 - react-vite / state / a / guard: seed 10: still buggy, but differs from off: stale results: shows [Seattle, Seoul, Seville] (3) for "seatt", want 1 - react-vite / state / a / heal: seed 5: still buggy, but differs from off: stale results: shows [Lima, Lisbon, Liverpool] (3) for "lisbo", want 1 - react-vite / state / a / heal: seed 9: still buggy, but differs from off: stale results: shows [Bangkok, Barcelona] (2) for "barce", want 1 - react-vite / state / a / heal: seed 10: still buggy, but differs from off: stale results: shows [Seattle, Seoul, Seville] (3) for "seatt", want 1 - react-vite / zustand / a / guard: seed 3: still buggy, but differs from off: stale results: shows [Manchester, Manila] (2) for "manch", want 1 - react-vite / zustand / a / guard: seed 5: still buggy, but differs from off: stale results: shows [Lima, Lisbon, Liverpool] (3) for "lisbo", want 1 - react-vite / zustand / a / guard: seed 9: still buggy, but differs from off: stale results: shows [Bangkok, Barcelona] (2) for "barce", want 1 - react-vite / zustand / a / guard: seed 10: still buggy, but differs from off: stale results: shows [Seattle, Seoul, Seville] (3) for "seatt", want 1 - react-vite / zustand / a / heal: seed 5: still buggy, but differs from off: stale results: shows [Lima, Lisbon, Liverpool] (3) for "lisbo", want 1 - react-vite / zustand / a / heal: seed 9: still buggy, but differs from off: stale results: shows [Bangkok, Barcelona] (2) for "barce", want 1 - react-vite / zustand / a / heal: seed 10: still buggy, but differs from off: stale results: shows [Seattle, Seoul, Seville] (3) for "seatt", want 1 - react-vite / zustand-on / a / guard: seed 5: still buggy, but differs from off: stale results: shows [Lima, Lisbon, Liverpool] (3) for "lisbo", want 1 - react-vite / zustand-on / a / guard: seed 9: still buggy, but differs from off: stale results: shows [Bangkok, Barcelona] (2) for "barce", want 1 - react-vite / zustand-on / a / guard: seed 10: still buggy, but differs from off: stale results: shows [Seattle, Seoul, Seville] (3) for "seatt", want 1 - react-vite / zustand-on / a / heal: seed 5: still buggy, but differs from off: stale results: shows [Lima, Lisbon, Liverpool] (3) for "lisbo", want 1 - react-vite / zustand-on / a / heal: seed 9: still buggy, but differs from off: stale results: shows [Bangkok, Barcelona] (2) for "barce", want 1 - react-vite / zustand-on / a / heal: seed 10: still buggy, but differs from off: stale results: shows [Seattle, Seoul, Seville] (3) for "seatt", want 1 - react-vite / redux / a / guard: seed 3: still buggy, but differs from off: stale results: shows [Manchester, Manila] (2) for "manch", want 1 - react-vite / redux / a / guard: seed 5: still buggy, but differs from off: stale results: shows [Lima, Lisbon, Liverpool] (3) for "lisbo", want 1 - react-vite / redux / a / guard: seed 9: fixed (no action recorded); off: stale results: shows [Bangkok, Barcelona, Beijing, ...] (8) for "barce", want 1 - react-vite / redux / a / guard: seed 10: still buggy, but differs from off: stale results: shows [Seattle, Seoul, Seville] (3) for "seatt", want 1 - react-vite / redux / a / heal: seed 5: still buggy, but differs from off: stale results: shows [Lima, Lisbon, Liverpool] (3) for "lisbo", want 1 - react-vite / redux / a / heal: seed 9: still buggy, but differs from off: stale results: shows [Bangkok, Barcelona] (2) for "barce", want 1 - react-vite / redux / a / heal: seed 10: still buggy, but differs from off: stale results: shows [Seattle, Seoul, Seville] (3) for "seatt", want 1 - next-app / state / a / guard: seed 2: still buggy, but differs from off: stale results: shows [Beijing, Belgrade, Berlin, ...] (4) for "berli", want 1 - next-app / state / a / guard: seed 5: still buggy, but differs from off: stale results: shows [Lima, Lisbon, Liverpool] (3) for "lisbo", want 1 - next-app / state / a / guard: seed 9: still buggy, but differs from off: stale results: shows [Bangkok, Barcelona] (2) for "barce", want 1 - next-app / state / a / guard: seed 10: still buggy, but differs from off: stale results: shows [Seattle, Seoul, Seville] (3) for "seatt", want 1 - next-app / state / a / heal: seed 5: still buggy, but differs from off: stale results: shows [Lima, Lisbon, Liverpool] (3) for "lisbo", want 1 - next-app / state / a / heal: seed 9: still buggy, but differs from off: stale results: shows [Bangkok, Barcelona] (2) for "barce", want 1 - next-app / state / a / heal: seed 10: still buggy, but differs from off: stale results: shows [Seattle, Seoul, Seville] (3) for "seatt", want 1 - next-app / swr / b / guard: seed 1: still buggy, but differs from off: server has 2 "Report 1" - next-app / swr / b / guard: seed 2: still buggy, but differs from off: server has 2 "Report 2" - next-app / swr / b / guard: seed 7: still buggy, but differs from off: server has 2 "Report 7" - next-app / swr / b / heal: seed 1: still buggy, but differs from off: server has 2 "Report 1" - next-app / swr / b / heal: seed 2: still buggy, but differs from off: server has 2 "Report 2" - next-app / swr / b / heal: seed 7: still buggy, but differs from off: server has 2 "Report 7" - next-app / swr / d / observe: seed 7: fixed (no action recorded); off: shows [Write tests: open; Ship release: open; Fix flaky CI: done], server [Write tests: done; Ship release: open; Fix flaky CI: done] - next-app / swr / d / guard: seed 7: fixed (no action recorded); off: shows [Write tests: open; Ship release: open; Fix flaky CI: done], server [Write tests: done; Ship release: open; Fix flaky CI: done] - next-app / swr / d / heal: seed 7: fixed (no action recorded); off: shows [Write tests: open; Ship release: open; Fix flaky CI: done], server [Write tests: done; Ship release: open; Fix flaky CI: done] - angular / xhr / b / heal: seed 1: fixed (request:block); off: server has 2 "Report 1" - angular / xhr / b / heal: seed 2: fixed (request:block); off: server has 2 "Report 2" - angular / xhr / b / heal: seed 3: fixed (request:block); off: server has 2 "Report 3" - angular / xhr / b / heal: seed 4: fixed (request:block); off: server has 2 "Report 4" - angular / xhr / b / heal: seed 5: fixed (request:block); off: server has 2 "Report 5" - angular / xhr / b / heal: seed 6: fixed (request:block); off: server has 2 "Report 6"

Method

  • Apps. One small app per framework under compat/apps/, written idiomatically for each data layer (no GenClass API anywhere: only the one line). They install @genclass/runtime from a tarball packed from this repository (npm pack), so the matrix tests the code as it would be published.
  • Backend. One seeded mock server for every app (compat/harness/backend.mjs): REST, a GraphQL endpoint, a WebSocket and EventSource streams on the app's own origin. It injects latency and failures per scenario; nothing in it knows about GenClass.
  • A trial. A fresh browser context loads the page (?genclass=off for the run without GenClass; otherwise the mode through window.GENCLASS_CONFIG, telemetry off), waits until the model is ready, drives the scenario with real (trusted) keyboard and mouse input, waits until nothing is in flight and the page has been quiet for 0.9 s, then reads the page and the server's state.
  • Pairing. Every mode run is compared with the run without GenClass on the same seed. 10 seeds per cell, plus a second run without GenClass per seed as a determinism control.
  • Isolation. Every request to another host is blocked and counted; the model files and ONNX Runtime are served from a local copy of the files the runtime would fetch from jsDelivr, and the script-tag app gets the packed tarball in place of the CDN.
  • Not tuned. The runtime and the model are not changed or tuned for these apps, and the apps are not used for training.
  • Limits. Small apps, one machine, headless Chromium only (no Firefox or Safari), WASM inference (no GPU), production builds only. Each cell has few seeds; read single differences as signals to investigate, not rates.

Reproduce

On Linux with Node ≥ 22.22.3 (Angular 22's minimum) and about 4 GB free:

git clone https://github.com/genclass-dev/GenClass-lib && cd GenClass-lib
npm ci && (cd compat && npm install && npx playwright install --with-deps chromium)
node packages/runtime/bin/genclass-runtime.mjs fetch-model .cache-model/runtime-model-0.2.0 --variant q8 --ort wasm
COMPAT_MODEL_DIR=$PWD/.cache-model/runtime-model-0.2.0 npm --prefix compat run compat -- --seeds 10

This rebuilds the runtime, packs it, installs it into every app, builds and serves each app, runs every trial and writes compat/results/<date>.json and this page. --apps, --layers, --scenarios, --modes and --workers narrow a run. This page was generated from 2026-10-10.json (5850 trials, 0 did not complete; 16 parallel browsers).