Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions .github/workflows/playwright-reporter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,18 @@ on:
push:
paths:
- "packages/playwright/**"
- "packages/runtime/src/agent/**"
- "packages/runtime/src/cdn/**"
- "packages/runtime/src/**"
- "packages/runtime/package.json"
- "packages/runtime/tsup.config.ts"
- "packages/mcp/test/e2e/app/**"
- "package-lock.json"
- ".github/workflows/playwright-reporter.yml"
pull_request:
paths:
- "packages/playwright/**"
- "packages/runtime/src/agent/**"
- "packages/runtime/src/cdn/**"
- "packages/runtime/src/**"
- "packages/runtime/package.json"
- "packages/runtime/tsup.config.ts"
- "packages/mcp/test/e2e/app/**"
- "package-lock.json"
- ".github/workflows/playwright-reporter.yml"
Expand Down Expand Up @@ -110,6 +112,11 @@ jobs:
- name: Chromium
run: npx playwright install --with-deps chromium

# the unit test that resolves the installed runtime's dist/ by default needs the runtime built, as here
- name: Unit tests with the runtime built
working-directory: packages/playwright
run: npx vitest run test/install.test.ts

- name: The fixture end to end (buggy page flagged, fixed page and latency-only run clean, report rendered)
# shell: bash runs with pipefail, so a failing run.mjs fails the step even though tee succeeds
shell: bash
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Default mode is `observe` (report only); `guard` is opt-in; `heal` is experiment
| `demos/` | six demo apps with a chaos backend and Playwright trials; `bench/heal/` runs them per mode |
| `packages/mcp/` | `@genclass/mcp`: the MCP server for coding agents (bridge on 127.0.0.1:47357, store, explore verdicts, replays, regression-test generation, 8 tools); no runtime deps; `test/e2e/race.e2e.mjs` is the real-browser check (not in CI's main job). `plugin/` is the Claude Code plugin (also an Agent Plugins package); `server.json`, the plugin, `.claude-plugin/`, `.cursor-plugin/`, `gemini-extension.json`, `llms-install.md` and the READMEs pin its version (`npm run sync -w @genclass/mcp` after a version bump; `test/manifests.test.ts` fails otherwise) |
| `packages/sv/` | `@genclass/sv`: the Svelte CLI community add-on (`npx sv add @genclass`): adds the runtime, the `/auto` import first in `src/hooks.client`, the dev-only overlay and (default) the telemetry opt-out meta in `src/app.html`, all with the `genclass:init` marker so `remove` undoes it; bundles `@sveltejs/sv-utils`, `sv` is a peer; `test/e2e/` runs sv's add-on harness (pnpm, Chromium) |
| `packages/playwright/` | `@genclass/playwright`: the fixture (`src/fixture.ts`, `install.ts`, `page.ts`, `chaos.ts`, `races.ts`: puts the runtime's script-tag build on every `page` in observe mode with telemetry off, seeded chaos on fetch/XHR responses with `page.route`, witnesses through the agent entry with every hold off, one `genclass-races/1` file per test in `test-results/genclass/`) and the reporter + pure summariser that read those files and write `genclass-report.md` for the PR; peer deps only (`@playwright/test`, `@genclass/runtime`); `test/e2e/run.mjs` is the real-browser check (its own workflow) |
| `packages/playwright/` | `@genclass/playwright`: the fixture (`src/fixture.ts`, `install.ts`, `page.ts`, `chaos.ts`, `races.ts`: puts the runtime's script-tag build on every `page` in observe mode with telemetry off, seeded chaos that holds matched fetch/XHR requests with `page.route` and `route.fallback()`, witnesses through the agent entry with every hold off, one `genclass-races/1` file per test in `test-results/genclass/`) and the reporter + pure summariser that read those files and write `genclass-report.md` for the PR; peer deps only (`@playwright/test`, `@genclass/runtime`); `test/e2e/run.mjs` is the real-browser check (its own workflow) |
| `packages/nuxt/`, `packages/astro/` | `@genclass/nuxt` (a Nuxt module, `@nuxt/kit`, built with `@nuxt/module-builder`) and `@genclass/astro` (an Astro integration, `injectScript("page", ...)`): from options `{ mode, token?, telemetry?, devtools? }` they generate the runtime's own one line (`/auto` or `/auto/guard`) plus a `window.GENCLASS_CONFIG` module imported before it; browser only, nothing on the server; `@genclass/runtime` is a peer dependency. The generated code is pure (`src/source.ts`, `src/index.ts`) and the tests check it exactly, then build one fixture app. Workflow: `integrations.yml` (Node 24) |
| `docs/adoption/listings/` | how the framework packages and templates get listed (nuxt.com/modules entry and steps, astro.build/integrations, sveltesociety.dev, awesome-vue and Angular lists): research and draft entries; nothing is submitted from the repo |
| `bench/visual/` | the visual checks' bench (GitHub Actions only, `.github/workflows/visual-measure.yml`): false reports on the demos, templates, compat apps and about 40 public sites, recall on faults planted from the harness, cost; public sites are counted per check kind only, never named in the repo, its run logs or plain artifacts (per-site results only encrypted with the `VISUAL_BENCH_KEY` secret) |
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

46 changes: 28 additions & 18 deletions packages/playwright/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,36 @@
with `page.route`, never from a CDN. The model is skipped by default (`model: "skip"`: no 13 MB download) and the
file carries the runtime's witnesses, computed without the model through the agent entry connected with every
hold off (`overwrote-newer`, `identical-in-flight`); `model: "load"` adds the model's detections with their
confidence. At the end of each test it writes one `genclass-races/1` file to `test-results/genclass/` (or
`outDir`), named after the test, its id and its seed, with the seed and the chaos settings. `enabled: false`
makes it inert. Peer dependencies only (`@playwright/test`, `@genclass/runtime`, both optional: the reporter
needs neither).
- **Seeded chaos** (`chaos: { seed, delayMs, reorder, match }`): with `page.route` the real response is fetched at
once and its delivery to the page held for a latency derived from the seed and the request's key (method, path,
occurrence), so a run repeats exactly; `reorder: false` keeps the request order (latency only). Only fetch and
XHR requests inside `match` (default: same-origin `/api*` or JSON responses); never the runtime's own files,
documents, scripts or anything outside the filter. Off unless given. With `repeatEach`, the repeat index is
added to the seed, so two repeats give the report its two seeds.
confidence (the model's diagnosis probability, from the runtime's `decide` event). At the end of each test it
writes one `genclass-races/1` file to `genclass` in the project's outputDir (test-results/genclass, emptied by
Playwright at each run; or `outDir`), named after the test, its id, its seed and, for a retry, the attempt, with
the seed, the chaos settings and a `fixture` record (the model option, whether the agent entry connected, how many
requests chaos held, whether a service worker controlled the page, a mode switch it overrode). A test skipped in
its body writes nothing. The runtime is held to observe: a URL or localStorage switch that raised the mode is set
back. The agent link gets a stand-in fetch for its own address, so the fixture adds no console error, failed
request or CSP report to the page. `enabled: false` makes it inert. Peer dependencies only (`@playwright/test`,
`@genclass/runtime`, both optional: the reporter needs neither); `typesVersions` for `moduleResolution: node`;
`engines` `^20.19.0 || >=22.12.0` (CommonJS test projects load the ESM-only fixture with require).
- **Seeded chaos** (`chaos: { seed, delayMs, reorder, match }`): with `page.route` each matched request is held for a
latency derived from the seed and the request's key (method, path, occurrence, assigned when the page sends it),
then passed on with `route.fallback()`, so it still reaches the test's own routes (`context.route`,
`routeFromHAR`) and the browser's network stack, with redirects, cookies, CORS and streaming as they are, and a run
repeats exactly. `reorder: false` sends matched requests in their order, at least 30 ms apart (latency only). Only
fetch and XHR requests inside `match` (default: same-origin `/api*` or an Accept header asking for JSON, decided
from the request); never the runtime's own files, documents, scripts or anything outside the filter. Off unless
given. With `repeatEach`, the repeat index is added to the seed, so two repeats give the report its two seeds.
- **Witnesses in the report**: signatures with trigger `witness` get their own group (shown only when there are
any), no confidence on the line (a fact that happened, not a probability) and a footer sentence saying what they
are; they are never counted as duplicate submits or stale responses. A model state `off` (no model by choice)
no longer gets the "model was not ready" note. The model-only report is byte for byte what it was.
- **Measured** (`test/e2e`, run by the workflow in Chromium): on the MCP e2e search page with latency 0 to 600 ms
and reordering, the buggy page gets `witness:overwrote-newer` on `GET /api/search` in 5 of 5 seeds, the same
page with latency only in 0 of 5, and the fixed page in 0 of 5; no other signature anywhere. With `model: "load"`
(one run by hand, two seeds) the model loaded through the fixture's path and gave no likely detection on that
page (`stale` at 0.64 in one seed, below the 0.90 report gate; nothing in the other), while the witness was
recorded in both.
are; they are never counted as duplicate submits or stale responses. Files with a `fixture` record get notes
for runs the fixture could not check (no witnesses recorded, a service worker in control) and for chaos that held
no request; a `model: "skip"` file gets no model note. Files without a `fixture` record are reported as before.
- **Measured** (`test/e2e`, run by the workflow in Chromium; six runs of five seeds): on the MCP e2e search page
(its store on the fixture's runtime through the script-tag mapping) with latency 0 to 600 ms and reordering, the
buggy page gets `witness:overwrote-newer` on `GET /api/search` in 30 of 30 seeds, the same page with latency only
in 0 of 30, and the fixed page in 0 of 30; no other signature, console error or failed request anywhere. With
`model: "load"` (one run by hand, two seeds, before chaos moved to requests) the model loaded through the
fixture's path and gave no likely detection on that page (`stale` at 0.64 in one seed, below the 0.90 report
gate; nothing in the other), while the witness was recorded in both.
- First version of the reader side: `@genclass/playwright/reporter` (a Playwright reporter by shape: reads
`test-results/genclass/*.json` in the `genclass-races/1` format at the end of the run, writes and prints
`genclass-report.md`), `@genclass/playwright/summarize` (the pure files-to-markdown function, with
Expand Down
Loading
Loading