Install
GenClass works in any web app that runs in a browser: React, Next.js, Vue, Svelte, Angular, Solid or plain HTML. The fastest way in is one command, run in your app's folder:
npx genclass
It signs you in, adds GenClass to the app, connects the GitHub App and your coding agent, and shows what it found in your terminal: the genclass command. The rest of this page is the runtime by hand.
npm install @genclass/runtime
The package is ESM only. Or let the runtime's own CLI detect your stack, show the diff and write it for you:
npx @genclass/runtime init --mode heal # recommended: reports, and fixes what it is sure of
npx @genclass/runtime init # observe: reports only
npx @genclass/runtime remove # undo exactly what init added
No build step? Add one script tag first in <head>. Keep the version pinned: the unversioned URL may serve an older release from the CDN cache.
<script src="https://cdn.jsdelivr.net/npm/@genclass/[email protected]"
data-mode="heal" data-devtools="local"></script>
data-mode="observe" reports only.
Starter apps for React + Vite, Next.js, Vue and SvelteKit, each with the one line and a racing mock API, are in templates/ on GitHub.
Audit a page
To check any page for visual bugs without adding GenClass to it, run the scan command with its address, from a terminal or from your coding agent:
npx @genclass/runtime@beta scan https://your-site.com # alias: audit; --json for agents
It runs the visual checks on that page in a headless browser on your machine and prints a report, as JSON with --json. Its fallback is the hosted page check, which runs the same checks in a Cloudflare browser. In a coding agent, the MCP server's tool genclass_scan runs the same.
Initialize
The one line, first in your entry file. /auto/heal starts the runtime in heal mode, which we recommend; /auto alone is observe, which reports only. Either discovers React, Redux and Zustand (devtools) state on its own and loads the model at idle:
import "@genclass/runtime/auto/heal"; // or "@genclass/runtime/auto" for observe
Prefer options in code? Call init() as early as possible, before your app makes requests:
import { GenClass } from "@genclass/runtime";
const rt = GenClass.init({ mode: "heal" }); // GenClass.init() alone is observe
The model downloads at idle, is cached, and runs in a Web Worker on WebGPU or WASM.
The first five minutes
What the console shows, in order. The notice and the model-ready line are what the runtime prints; the stale-response report is real output copied from the package README (a typeahead in Node, q8 model on WASM, observe mode; the line format has not changed since 0.1.0-beta.0).
[GenClass] Sends anonymous diagnostics (decisions and counts, no page text) to improve the model. Opt out: GenClass.init({ telemetry: false }) or ?genclass=no-telemetry.
[GenClass] Model ready (genclass-runtime-r17, wasm, q8, 0.43s). Mode: observe.
[GenClass] Flagged a stale response: search.results was written twice by other operations since its operation (#6)
started (version 1 → 3), last 0.69s ago by GET /api/search?q=reac (#8), which started 0.09s after #6, from a later
user action (#7). (stale, 0.96)
- The notice appears on public hosts only, not on
localhost.
- Then possibly nothing for a while, and that is normal. Observe mode reports only salient situations: a response landing over newer data, a repeat, a failure streak, a broken relation, an error. The first response of each kind after a page load is never a delivery decision, because the runtime first has to see what that request writes.
- Some flags will be wrong: expect 1 to 4 per 100 decisions where nothing was wrong, more in some apps (model 0.1.0; not re-measured for 0.2.0). Identical lines within a minute are folded into one with a count.
- Each flag opens a collapsed group with the evidence.
rt.explain(id) returns the exact situation text the model read.
- On localhost, Alt+Shift+G opens the devtools overlay;
init loads it in development only.
- With
/auto/heal the model-ready line says Mode: heal., and a flag becomes a Prevented line that says what was done: the example under Explain and undo.
Modes
| Mode | What it does |
observe | Default. Reports likely problems with the evidence and never holds, delays or changes anything. |
guard | Opt-in. Acts at the network boundary when the model's gate clears: discard, defer, coalesce, delay. At the shipped setting it acts on 7.5% of the real cases where acting would help (model 0.2.0, balanced). |
heal | Recommended, and the broadest mode. Everything guard does, plus retry, serve_cached, block, hedge, rollback, resync and custom actions. On held-out simulated apps it intervenes wrongly in 0.59% of cases at the default setting (model 0.2.0), over its 0.5% target, and in 0.00% on held-out real apps; precision among its actions was not measured for 0.2.0. |
GenClass.init({ mode: "heal" }); // or "guard"
Recommended path: ship heal with a
sink, so every action reaches you. It acts only when the model is sure, each action is one console line with its evidence, discards and rollbacks can be undone, and a breaker steps the mode down after repeated undos. If policy wants a read-only period first, ship observe and switch with one word.
Aggressiveness
Once GenClass can act, the profile sets how sure it must be first. balanced is the default. On held-out simulated apps (model 0.2.0) guard intervenes wrongly in 0.005% of cases at cautious, 0.13% at balanced and 0.54% at eager; heal in 0.26%, 0.59% and 1.84%. The targets are 0.1% and 0.5%, so pick cautious if a wrong intervention costs more than a missed one. On held-out real apps every profile measured 0.00%. The benchmarks put each recall next to its false-intervention rate.
GenClass.init({ mode: "heal", aggressiveness: "cautious" }); // "cautious" | "balanced" | "eager"
Where the reports go
By default a report is a console line in the end user's browser. In production that is not your console. The sinks option hands every detection, intervention, undo, breaker trip or summary to your own code as a structured, redacted record; the anonymous diagnostics (below) go to the GenClass maintainers. A free dashboard at genclass.dev also shows your app's detections; runtime 0.2.2 and earlier do not send to it.
Keep your error monitor and session replay. They see exceptions, failed requests, slow spans and what the user saw, but they cannot tell that a response was stale or that a POST was a duplicate, because nothing threw. GenClass decides that on the device, and a sink hands each report to your own logging or an endpoint you own.
GenClass.init({
sinks: [
{ send: (r) => myLogger.info("genclass", r), kinds: ["detection", "intervention"] },
{ send: (r) => navigator.sendBeacon("/genclass", JSON.stringify(r)), sampleRate: 0.2 },
],
requests: { ignore: ["/genclass"] }, // a sink that ships over fetch sends to an ignored endpoint
});
The generic sink above ships in 0.2.2. From runtime 0.3.0 (first as 0.3.0-beta.0) two ready-made sinks need no code of yours: console-json prints one JSON line per record for a log shipper, and webhook:<url> sends batched POSTs to an endpoint you own (data-sink="webhook:/genclass" on the script tag, or npx @genclass/runtime init --sink webhook:/genclass). The runtime has no Sentry, PostHog or Datadog adapter; forwarding from the genclass.dev collector to Sentry, PostHog, Datadog, Slack or a signed webhook, with no code in your app, is free on every plan. rt.audit() exports every decision and action as JSON. All sink options.
Explain and undo
Every detection and intervention is logged in plain English. rt.explain(id) returns the exact situation the model read, its facts and every answer. Discards and rollbacks return an ActionRecord with undo(), and the devtools overlay lists everything live. In guard or heal mode the stale-response report above becomes:
[GenClass] Prevented a stale response: search.results was written twice by other operations since its operation (#6)
started (version 1 → 3), last 0.69s ago by GET /api/search?q=reac (#8), which started 0.09s after #6, from a later
user action (#7). Delivered the response to GET /api/search?q=rea (#6) and dropped the state changes it makes over
newer data (search.results). (stale, 0.97; discard 0.98)
Kill switch
Append ?genclass=off to any URL and nothing is installed. ?genclass=observe|guard|heal changes the mode for that page within the limits you set. A breaker downgrades the mode automatically after repeated undos.
What it costs
Measured on a production Next.js ordering site (mobile viewport, 4× CPU throttling, runtime 0.1.0-beta.3; the full table):
- JavaScript: about +98 KB on first load (gzip). No measurable change to the load event, LCP or long tasks.
- First-visit download: about 13 MB at idle on WASM (the 10.2 MB q8 model plus ONNX Runtime), 18 MB on WebGPU, then cached and checked with sha256. Later visits: about 1 KB.
- Memory: 190 to 280 MB in a Web Worker while the model is loaded.
- Device floor: none by default; the default
loadIf only goes lazy on Save-Data connections. For phones and small devices:
GenClass.init({
model: {
loadIf: { minDeviceMemoryGB: 4, saveData: "skip" }, // or: (env) => !env.mobile && (env.deviceMemoryGB ?? 8) >= 4
preload: "lazy", // download on the first salient situation, not at idle on every page
unloadAfterIdleMs: 60_000, // free the worker's memory after a minute without a decision
},
});
model: false installs the runtime with no download and no worker, and then nothing is detected.
Strict performance budget? The lite install loads GenClass after the page has loaded, on 1 page load in 20: in one Angular app the load event did not move (94.0 ms against 94.2 ms without GenClass; runtime 0.3.0-beta.6, 4× CPU throttling). It reports less: nothing before it starts and none of your app's state, so no stale responses.
Self-host the model
Strict Content-Security-Policy or no third-party CDNs? Serve the model from your own origin (about 24 MB for the WASM build, 64 MB for every variant). Blocked and not self-hosted, GenClass observes without a model, prints one warning and detects nothing.
npx @genclass/runtime fetch-model public/genclass-model
npx @genclass/runtime init --model-url /genclass-model/
When to skip it
On a mostly static site there are no requests that write state, so there is nothing to decide and the model download is the only effect. GenClass also has little to report where every write is already idempotent on the server and reads are deduplicated in the client, as TanStack Query and SWR do.
On pages used mostly from 2 GB phones, set a device floor (above) or leave GenClass off those pages. If a strict Content-Security-Policy blocks cdn.jsdelivr.net and you cannot self-host the model, it detects nothing. If you need bugs fixed today rather than reported, read the benchmarks first: at the shipped setting guard acts on under 8% of the cases where acting would help (model 0.2.0, balanced), and six demo apps under chaos show the same bug rate with GenClass on or off.
Privacy and telemetry
Decisions are made on the device. From runtime 0.2.1, anonymous diagnostics are on by default on public hosts. From 0.2.2 they are off on local and private hosts (localhost, *.localhost, *.local, *.test, 127.0.0.1, ::1, private IP ranges); 0.2.1 sent from those hosts too, by mistake. They go to the GenClass maintainers' collector to improve the model. By default they contain no text from the page: a random per-page session id, versions, mode, device class, your hostname and id-normalised path, and per decision the trigger, diagnosis and confidence, action, gate, outcome, latency and counts. The situation text the model read is sent only if you opt in with telemetry: { include: { situation: true } }. Turn it off with GenClass.init({ telemetry: false }), npx @genclass/runtime init --no-telemetry, ?genclass=no-telemetry, localStorage genclass.telemetry=off, or Global Privacy Control. Apps that ship GenClass may need to disclose this to their users. See telemetry and privacy. For every request GenClass makes, how to stop each one, and the model from your own origin, follow open source, end to end.
Coding agents and the MCP server
@genclass/mcp is a free MCP server that gives Claude Code, Cursor, VS Code, Codex, Gemini CLI and other agents what GenClass recorded in your app's development build: every click, request, the order responses were delivered, what each wrote into state, and errors with their cause. From that the agent gets ranked findings (a response that overwrote newer data, one order submitted twice, the model's detections), genclass_explore (the same steps with responses delivered in other orders), genclass_replay (the exact order that produced a bug, before and after a fix) and a generated Playwright regression test. Everything stays on your machine. Install for your client · how it works and what was measured.
claude mcp add --scope project genclass -- npx -y @genclass/[email protected] # or the plugin: /plugin marketplace add genclass-dev/genclass-plugin, then /plugin install genclass@genclass
// in the app's development build only
import { connectAgent } from "@genclass/runtime/agent";
if (import.meta.env.DEV) connectAgent(GenClass.runtime);
The genclass skill is the procedure the agent follows with those tools: npx -y @genclass/[email protected] init writes it next to the server, and one line installs it in any agent. The skill page has every client.
For a team, GenClass runs this loop as a service: the MCP server, collector and dashboard hosted by GenClass or inside your network, on agreed terms (Enterprise).
Dashboard and plans
The runtime needs no account, and neither does the dashboard: genclass.dev/start gives an app a public token and a private dashboard link, with no sign-in. The dashboard guide shows how to get the dashboard, connect an app and find your way around it. The genclass.dev dashboard shows each app's detections, actions, routes and model load. It is free for every app, with 7 days of history, export of every event and forwarding to the tools you use, and so are your own classifier, which GenClass trains for you when you ask, and the @genclass/mcp server for coding agents. Export and forwarding need the app in a free account, which also keeps several apps in one place: sign in with Google or GitHub, and add a passkey for extra security if you like. Pages send to the dashboard with the app's token while diagnostics are on; runtime 0.2.2 and earlier do not send to it. See pricing.
Visual bugs
Measured checks find bugs that render without an error: text the same colour as its background, images, fonts and stylesheets that failed to load, pages that scroll sideways on a phone, undefined or [object Object] on the page, and controls under an invisible layer. They ship in runtime 0.3.0-beta.0; earlier versions do not have them. Measured in CI on October 13, 2026: 35 of 35 planted faults found on fixture pages, 0 false reports in 128 page views of unmodified apps, and on public home pages (bench run 4, 31 of 40 sites), 8 findings in 96 loads, 5 of them confirmed real and 3 unconfirmed. The page check runs the same checks on one public page, except click-blocked, which needs a visitor's clicks. Paste the address into the box on the page check, or open genclass.dev/ followed by the address. Visual bugs says what they find, how they measure it and what they do not catch yet.
Reference
- API reference
- MCP server for coding agents, and how record, replay and explore work
- The genclass skill, one command in any agent, and open source, end to end: every part, what leaves your network and how to stop each one, the model from your origin
- All options: routes, protected endpoints, breaker, shadow mode, veto, action limits, sinks
- Compatibility: 7 frameworks, 15 data layers, every mode
- Architecture, interception surface and telemetry
- Runtime guide
- How it works and benchmarks
- Found a false detection or a problem? GitHub issues are not enabled yet; write to [email protected] with the
rt.explain(id) output. Security reports: security policy.