Skip to content

feat(agent): browser-extension verification (load, drive, compile, re-run) - #23

Merged
tcballard merged 6 commits into
mainfrom
claude/extension-verification
Jun 30, 2026
Merged

feat(agent): browser-extension verification (load, drive, compile, re-run)#23
tcballard merged 6 commits into
mainfrom
claude/extension-verification

Conversation

@tcballard

@tcballard tcballard commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

What & why

Proofkeeper couldn't test a browser extension: it drove a vanilla, non-persistent Chromium page, had no config for an extension, and the emitter always produced a test({ page }) spec on the default fixture — none of which loads an extension. Chromium loads an unpacked extension only in a persistent context with --load-extension, and an MV3 extension's ID is regenerated on every load.

This adds first-class extension verification through the same drive→compile→fidelity→run→write-back loop: load the unpacked extension, let the model exercise its popup/options pages (chrome-extension://<id>/...) and its effect on host pages, and emit a compiled spec that re-loads the extension and re-discovers the ID at run time — so the committed test and the fidelity gate verify the real extension, never a stale recorded ID.

How

  • Launch (src/cli.ts browserDrive): when an extension is configured, launch a persistent context with channel: "chromium" and --load-extension / --disable-extensions-except. channel: "chromium" selects Chromium's new headless — the only headless mode that loads extensions (old headless never does; pure headed needs a display). Verified empirically against this repo's pre-installed Chromium.
  • ID discovery (src/agent/extension.ts): loadExtension() reads the runtime ID from the MV3 service worker (MV2 background-page fallback, then waits for the worker). The driver surfaces the ID and the chrome-extension://<id>/ base to the model.
  • Emitter, the moat (src/compiler/emit.ts): a new mode when RecordedSession.extensionPath is set — emit a persistent-context spec that relaunches with the extension, rediscovers extId, and rewrites recorded chrome-extension gotos to use the runtime extId. Non-extension specs are byte-identical to before (regression-guarded).
  • Config/CLI: a --extension <dir> flag on qa, and a per-environment extensionPath, threaded through resolveTargetQaOptionsDriveOptions for both the qa and scoped paths.

Usage

proofkeeper qa --corpus path/to/rac/ --url http://localhost:3000/ --extension ./my-extension

Or per environment in the config: "environments": { "dev": { "url": "...", "extensionPath": "./my-extension" } }.

Tests & gates

  • Unit: extensionIdFromUrl / loadExtension (fake context); emitter extension mode (persistent context, channel: "chromium", runtime-ID rewrite, determinism, non-extension unchanged); config parse + resolveTarget.
  • Browser-gated integration (tests/extension.integration.test.ts, PROOFKEEPER_E2E): loads a real MV3 fixture extension (tests/fixtures/extension/), discovers the ID, drives the popup — passing against the pre-installed Chromium. Added to the CI e2e job.
  • Full suite 234 passed; typecheck + build clean.
  • Dogfood corpus: req-extension-verification + design-extension-verification linked to the autonomous-qa-enhancements roadmap, ## Verified By the new tests. rac validate 23/23, rac relationships --validate 0 issues, rac review 100/100.

Scope

MV3 Chromium extensions loaded unpacked. Packed .crx, the Chrome Web Store, non-Chromium browsers, and a manifest-key stable-ID mode are out of scope (noted in the design). Releasable → next CalVer. Independent of the OpenAI-adapter PR.

@tcballard tcballard changed the title feat: browser-extension verification (load, drive, compile, re-run) feat(agent): browser-extension verification (load, drive, compile, re-run) Jun 30, 2026
…ign [roadmap:autonomous-qa-enhancements]

Implements lore-proofkeeper/requirements/req-extension-verification.md and lore-proofkeeper/designs/design-extension-verification.md.

Records the capability to load an unpacked extension for the drive, rediscover
its runtime ID, and emit a compiled test that re-loads it.

Signed-off-by: Tom Ballard <tom@armytage.co>
… [roadmap:autonomous-qa-enhancements]

When a session carries an extensionPath, the emitter produces a spec that
launches a persistent context with the extension (channel: chromium new
headless), rediscovers the extension ID from the MV3 service worker at run
time, and rewrites chrome-extension://<recorded-id>/… gotos to the runtime ID.
Non-extension sessions emit byte-identically. RecordedSession carries the
extension dir.

Signed-off-by: Tom Ballard <tom@armytage.co>
…ontext [roadmap:autonomous-qa-enhancements]

Adds loadExtension(): discover the extension's runtime ID from the MV3 service
worker (MV2 background-page fallback, then wait). The driver surfaces the ID and
chrome-extension:// base to the model so it can drive the extension's pages.

Signed-off-by: Tom Ballard <tom@armytage.co>
…CLI [roadmap:autonomous-qa-enhancements]

browserDrive launches a persistent context with the extension when set; a new
--extension flag and a per-environment extensionPath thread through resolveTarget
→ QaOptions → DriveOptions for both the qa and scoped paths.

Signed-off-by: Tom Ballard <tom@armytage.co>
…al-browser drive [roadmap:autonomous-qa-enhancements]

Signed-off-by: Tom Ballard <tom@armytage.co>
…roadmap:autonomous-qa-enhancements]

Signed-off-by: Tom Ballard <tom@armytage.co>
@tcballard
tcballard force-pushed the claude/extension-verification branch from 7001b4f to 99c1a2b Compare June 30, 2026 17:09
@tcballard
tcballard merged commit 18c7a05 into main Jun 30, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant