Skip to content

Add page tools with React provider - #5

Merged
pgom merged 4 commits into
masterfrom
feature/page-tools-react
Nov 5, 2026
Merged

pgom merged 4 commits into
masterfrom
feature/page-tools-react

Conversation

@xavi-999

Copy link
Copy Markdown
Contributor

Description

Adds Page Tools: a framework-neutral layer that injects a provider script into the target's dev-server page, with zero edits to the target repo. That lets the agent use chrome-devtools-mcp's third-party developer tools (--categoryExperimentalThirdParty). The first provider is React's react-devtools-cdt-mcp, pinned at 0.1.0.

How injection works: page_tools.mjs serve hosts the provider script on 127.0.0.1 with CORS. A one-line sync-XHR initScript loader (page_tools.mjs loader), passed on every navigate_page/reload, evaluates it before React loads.

On React targets it powers:

  • INP blame in interaction: the components that rendered in the slow interaction, ranked by self time, with file:line.
  • re-renders preset: gates on total components rendered (Σ componentsChanged) for one fixed interaction. 3 baseline captures pick the gate shape: identical → deterministic minEffect: 0, otherwise → N-run.
  • Senior Audit: render-cost catalog entries gate through re-renders instead of staying advisory, and carry measured evidence.
  • Perf Map render overlay: build_perf_map.mjs --renders.

The four existing prod-build gates are unchanged. Page Tools runs as its own dev-server pass, and that pass's teardown always runs.

Design spec: docs/superpowers/specs/2026-10-26-page-tools-react-design.md (§8 records the deviations found during verification).

Related issues

N/A

Impacted areas

  • .mcp.json: --categoryExperimentalThirdParty. It only adds list_3p_developer_tools / execute_3p_developer_tool (29 → 31 tools); no existing tool or schema changes.
  • package.json: new exact runtime dependency react-devtools-cdt-mcp@0.1.0. CLAUDE.md's runtime-deps rule is amended, and the SessionStart hook guard now also checks for it.
  • New scripts/page_tools.mjs: serve / loader / locate plus pure, self-tested helpers. It is imported by doctor.mjs and build_perf_map.mjs, and CLI-guarded.
  • doctor.mjs: pageTools axis and config key, a 3p flag check, and a provider pin check.
  • check_invariants.mjs: provider pin, 3p flag, and pageTools in the mirrored-config-key check (checkDepPulseKeys → checkConfigKeys).
  • Docs: SKILL.md, new references/page-tools.md, tools.md, presets.md, measurement.md, senior-audit.md, architectural-perf-catalog.md, perf-map.md, framework catalogs, README, CONTRIBUTING.md, commands/browsergnome.md.

Steps to reproduce or test

Development

  • npm test: all self-tests (new page-tools:test, 41 checks) plus the invariants check pass.
  • CI smoke step: build_perf_map.mjs with the --renders fixture pair.
  • claude plugin validate . --strict passes with the CI-pinned CLI (2.1.226).
  • End-to-end runs through chrome-devtools-mcp@1.6.0 --categoryExperimentalThirdParty with the real injection, React 19:
Target Tools discovered Source → repo path Σ components, 3 captures Teardown
Vite 8 dev yes src/Filter.jsx 803 / 803 / 803 ports free
Next.js 16 dev (webpack) yes app/Filter.tsx 728 / 728 / 728 ports free
Next.js 16 dev (Turbopack) yes chunk URL → located by name 734 / 734 / 734 ports free
  • Worked fix: memo on the list row took Vite from 803 → 469 (deterministic). stats.mjs → KEEP.

QA

  1. In a React target repo, run node doctor.mjs. It should report page tools: react and the dev script.
  2. Run /browsergnome this list re-renders on every key. It should route to re-renders, inject without touching the target repo, and stop the dev server afterwards (lsof on its port should be empty).

Checklist

  • Add label Breaking Change if it applies.
  • Commits are atomic and logically separated.
  • Performance implications have been considered.
  • Security implications have been considered.
  • The new and updated code has good coverage.
  • API documentation, if required, has been created or updated.
  • New dependencies have been added to package.json.
  • The README file, if required, has been updated.
  • Architectural diagram, if required, has been updated.

Deploy notes

Existing installs pick up the new dependency on their next session start: the SessionStart hook's guard now checks for node_modules/react-devtools-cdt-mcp. The provider is experimental; bumping its pin means re-running the verification in references/page-tools.md.

@xavi-999 xavi-999 added the enhancement New feature or request label Oct 26, 2026
@xavi-999 xavi-999 self-assigned this Oct 26, 2026

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Findings marked 🟡 are optional suggestions and need no follow-up push.

Additional findings (outside the current diff — GitHub can't attach inline comments there):

  • 🟡 skills/browsergnome/scripts/doctor.mjs — Target repos get an untracked .bgn/render-profile.json after every render-overlay pass, because the new artifact is not gitignored. Unlike dep-pulse.json, it is not listed in GITIGNORE_CONTENT. SKILL.md:112 tells the agent to write this file, but .bgn/ is otherwise committed with the app. Fix: add render-profile.json to GITIGNORE_CONTENT, the bootstrap message at doctor.mjs:249 and SKILL.md:228, so the existing backfill also covers repos bootstrapped earlier.

    Why this was flagged

    SKILL.md:111-114 tells the agent to write .bgn/render-profile.json (dev-build render counts and selfMs timings) and pass it to build_perf_map.mjs --renders. .bgn/ lives in the target repo and is tracked: config, ledger and perf-memory are committed. The doctor.mjs:217 .gitignore covers only report.html, run-state.json and dep-pulse.json, its list of generated run artifacts. Nothing ignores the new file, so it appears as ?? .bgn/render-profile.json. The next doctor.mjs run lists it under "pre-existing change(s)" from parseGitState. It can also be swept into a commit, which puts dev-build timings into the app's history. On the base branch no such file is written. The existing backfill loop at doctor.mjs:242-245 appends missing GITIGNORE_CONTENT lines, so it would also fix older repos once the line is added. Nothing here adds it.

    Verification: SKILL.md:111-114 (added in this diff) tells the agent to "write .bgn/render-profile.json as [{file, renders, selfMs}]". GITIGNORE_CONTENT (doctor.mjs:217-221) lists only report.html, run-state.json and dep-pulse.json. The diff does not add render-profile.json there. As a result, the file shows up as ?? .bgn/render-profile.json in the target repo.

  • 🟡 skills/browsergnome/scripts/page_tools.mjs — nit: the check for leftover ESM syntax can reject a valid provider bundle. TOP_LEVEL_ESM (/^(import\s*[\w{*'"]|export\s)/m) also matches column-0 identifiers such as importScripts( or importantFlag = 1, because \s* can match zero characters. Then serve exits. Fix: require a real import statement, e.g. ^import(\s+[\w{*]|\s*['"{*]). Also add an unindented importScripts(); case to the self-test at line 723, which only tests the indented form.

    Why this was flagged

    toClassicScript runs on the provider's dist/register.js at serve time (page_tools.mjs:687). A future pinned react-devtools-cdt-mcp bundle could have a top-level line starting with an identifier that begins with import, which is common in unminified esbuild output. TOP_LEVEL_ESM would match it as import plus a \w character and throw 'provider script has top-level ESM syntax beyond the trailing export'. serve then exits 1 and every Page Tools pass is unavailable. The self-test case 'ignores "import"/"export" inside indented code and identifiers' only feeds an indented importScripts();, so it does not cover this.

    Verification: In skills/browsergnome/scripts/page_tools.mjs line 57, const TOP_LEVEL_ESM = /^(import\s*[\w{*'"]|export\s)/m; uses \s*, which can match zero characters. importScripts( matches as import + S. toClassicScript (lines 66-71) then throws, and it is called at serve time (line 202), so a valid bundle would be rejected. The self-test at line 238 only covers the indented importScripts();.

@xavi-999
xavi-999 force-pushed the feature/page-tools-react branch from 3927944 to 0a6df8f Compare November 3, 2026 12:02
@pgom
pgom merged commit 55733b0 into master Nov 5, 2026
7 checks passed
@pgom
pgom deleted the feature/page-tools-react branch November 5, 2026 10:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants