This file is the source of truth for the next Codex session. Do not assume prior chat context. Start here.
Build react-devtool-cli (rdt) into a Playwright-native CLI for inspecting live React apps.
Current session architecture is:
rdt session open-> local Playwright launchrdt session connect-> Playwright protocolwsEndpointrdt session attach-> Chromium CDP fallbackrdtowns Playwright directly through the Node API
Implemented:
- CLI entrypoint and command routing
- session daemon process with local HTTP RPC
- session transports:
open,connect,attach - transport/capability metadata in session status
- basic React runtime injection via
__REACT_DEVTOOLS_GLOBAL_HOOK__ - commands:
session open|connect|attach|status|closetree getnode inspect|search|highlight|pickprofiler start|stop|summary|exportsource reveal
- output formats:
- default
json - compact
yaml/pretty - profiler raw export
jsonl/jsonl.gz
- default
- local React validation target:
test-app/Vite React template for real-app verification
- snapshot-aware lookup:
tree getreturnssnapshotIdnode inspect|search|highlightandsource revealaccept--snapshot <id>- runtime keeps a small in-memory snapshot cache
- dev-only React DevTools references:
react-devtools-corereact-debug-tools- added for agent reference and data-model comparison, not runtime integration
- DevTools concept mapping note:
docs/devtools-concept-mapping.mdmaps currentrdtpayloads and runtime concepts toreact-devtools-core- use it as the source of truth for terminology alignment before changing runtime behavior
Implemented files to inspect first:
npm testpasses.node bin/rdt.js --helpworks.import('playwright')works locally.- A local Vite React app was scaffolded at
test-app/and dependencies were installed. - Real browser launch was verified once with:
node bin/rdt.js session open --url data:text/html,%3Chtml%3E%3Cbody%3Ehello%3C/body%3E%3C/html%3E --session smoke-open-escalated --timeout 5000
node bin/rdt.js session status --session smoke-open-escalated
node bin/rdt.js session close --session smoke-open-escalated- The smoke page was not a React app, so
reactDetectedwas correctlyfalse. session connecterror path was verified against an invalid endpoint and returned a structured failure.- Real React app validation was run successfully against
http://127.0.0.1:3000/fromtest-app/. node bin/rdt.js session open --url http://127.0.0.1:3000 --session app --timeout 10000returnedreactDetected: true.tree getreturned a real React tree with one root and expectedApp/ host nodes.node search App --session appandnode inspect <id> --session appreturned meaningfulprops,state,hooks, anddomdata.- profiler flow worked against real updates after adding an auto-incrementing
tickstate totest-app/src/App.jsx. profiler stopreportedcommitCount: 42,maxNodeCount: 64,roots: ["root-1"].profiler export --session app --compressproduced a localapp-profile-mmt78xlw.jsonl.gzartifact.- In this environment, both the Vite dev server port bind and
rdtsession commands that talk to the local session daemon required sandbox escalation. tree getnow returns asnapshotIdfor snapshot-local node lookup.node search App --session app --snapshot <snapshotId>andnode inspect <nodeId> --session app --snapshot <snapshotId>were verified against the same collected snapshot after later React updates.- strict snapshot-time inspect behavior was verified:
tree getcapturedAppwithAuto tick: 1- after waiting for later React updates,
node inspect <nodeId> --snapshot <same-id>still returnedtick = 1
node highlight <nodeId> --session app --snapshot <snapshotId>worked with snapshot-aware lookup.source reveal <nodeId> --session app --snapshot <snapshotId>executed successfully; forAppit returnednull, consistent with missing_debugSourcerather than a snapshot lookup failure.node pickwas validated through a CDP-attached session:- clicking
button.counterreturned a snapshot-scoped inspect result for thebuttonhost node - the returned
snapshotIdandnodeIdwere then reused successfully withnode inspect,node highlight, andsource reveal
- clicking
react-devtools-coreandreact-debug-toolswere added asdevDependenciesfor official reference during future agent maintenance.docs/devtools-concept-mapping.mdwas added to document how currentrdtconcepts map to public DevTools concepts and where the CLI intentionally diverges.
- Node identity appears unstable across updates.
- Repro:
- Start
test-app/with Vite. - Open an
rdtsession tohttp://127.0.0.1:3000. - Trigger React updates, either via HMR or the auto-incrementing
tickstate intest-app/src/App.jsx. - Compare
tree getoutput withnode search Appoutput, then immediately runnode inspect <id>.
- Start
- Observed behavior:
tree getshowedAppasn64whilenode search Applater returnedn127.node inspect n64 --session appreturnednull.node inspect n127 --session appsucceeded and returned current hook/state data.
- Interpretation:
- there may be a bug in node ID stability, tree snapshot freshness, or lookup synchronization after updates/HMR
- React inspection is functional, but node references are not yet trustworthy enough to call fully stable
snapshot-localnode identity is now the active implementation direction.- Working definition:
tree getshould create asnapshotId- each node
idis only guaranteed to be valid within that snapshot - follow-up commands such as
node search,node inspect, andnode highlightshould resolve against the same snapshot instead of re-walking the latest live fiber tree
- Rationale:
- current IDs are keyed by live fiber object identity and are not stable across updates or HMR
- forcing globally stable IDs across React commits would be more complex and less reliable than first making snapshot semantics explicit
- snapshot-local IDs are sufficient for a CLI flow where users first fetch a tree, then inspect/search/highlight within that same view
- Expected implementation shape:
- keep a small in-memory snapshot cache in the page runtime
- store
snapshotId, serialized nodes, and anodeId -> fiberlookup for that snapshot - make server/CLI commands accept a snapshot reference where needed
- document that a node ID without its snapshot context is not globally meaningful
- Snapshot lookup now keeps node identity stable enough for
tree get -> search/inspect/highlight/source. - Current implementation status:
- explicit
--snapshot <id>is supported - omitting
--snapshotfalls back to the latest collected snapshot - runtime snapshot cache size is currently
5per session - if an explicitly requested snapshot has been evicted, commands now fail with
snapshot-expiredinstead of silently falling back
- explicit
- Snapshot cache decision:
- keep cache size fixed at
5for now - do not make it user-configurable until there is evidence that
5is too small in real agent workflows - revisit configurability only if snapshot eviction becomes a recurring operational problem
- keep cache size fixed at
- Agent-oriented operating rule:
- agents should treat
tree getas the start of a lookup cycle - agents should persist the returned
snapshotIdand pass it to all later node/source commands - latest-snapshot fallback exists for convenience, not as the preferred deterministic path
- agents should treat
- Terminology alignment status:
- response field names are kept stable for now
- concept and field semantics are documented in
README.mdanddocs/devtools-concept-mapping.md - future naming changes should only happen if the current names create real maintenance confusion
- Browser launch may require sandbox escalation in this environment.
attachis Chromium-only and lower fidelity than Playwright protocol transport.- Current React inspection uses a custom runtime script, not
react-devtools-coreyet. - Session state is stored under
.react-devtool-cli/sessionsin the repo by default.
- Evaluate adding
react-devtools-coreand related DevTools packages asdevDependencies, not immediate runtime dependencies. - Current decision:
react-devtools-coreandreact-debug-toolsare now installed asdevDependencies- they are reference-only for agent maintenance and model comparison
- do not import them into the runtime path unless a later tradeoff review justifies that coupling
- Purpose:
- improve type stability while exploring DevTools data structures
- compare the current custom runtime against official DevTools concepts and event flow
- decide whether later integration should stay custom, adopt official types only, or move closer to official runtime pieces
- Decision rule:
- prefer development-time references and type usage first
- avoid runtime deep imports unless the stability tradeoff is clearly acceptable
- This should be revisited during the first real React app validation pass.
Primary next milestone:
- decide whether any remaining field names or payload shapes need behavioral changes after the terminology-alignment documentation pass
Concrete tasks:
- Reuse
test-app/as the default local React validation target. - Re-run the documented agent workflow exactly as written in README / skill docs:
tree get- persist
snapshotId node search|inspect|highlight|source revealwith--snapshot
- Review current response field names against
docs/devtools-concept-mapping.mdand decide whether any rename is worth the compatibility cost. - Decide whether
node pickneeds a more prominent README example now that the behavior is verified. - Re-run the same React validation after the doc / UX cleanup:
session openreturnsreactDetected: truetree getreturns credible roots, nodes, and a snapshot identifiernode search,node inspect,node highlight,source reveal, andnode pickwork on IDs from that same snapshot- profiler still captures real commits
- Revisit whether official DevTools types or references would help stabilize node identity modeling.
- Re-read TASK.md, README.md, and skills/react-devtool-cli-repo/SKILL.md.
- Run:
npm test
node bin/rdt.js --help- Launch the local React target:
cd test-app
npm run dev -- --host 127.0.0.1 --port 3000- In another shell, run:
node bin/rdt.js session open --url http://127.0.0.1:3000 --session app --timeout 10000
node bin/rdt.js tree get --session app
node bin/rdt.js node search App --session app
node bin/rdt.js node inspect <id-from-search-or-tree> --session app
node bin/rdt.js profiler start --session app
node bin/rdt.js profiler stop --session app
node bin/rdt.js profiler summary --session app
node bin/rdt.js profiler export --session app --compress
node bin/rdt.js session close --session appPlanned post-change shape:
node bin/rdt.js tree get --session app
# => returns snapshotId, roots, nodes
node bin/rdt.js node search App --session app --snapshot <snapshotId>
node bin/rdt.js node inspect <nodeId> --session app --snapshot <snapshotId>
node bin/rdt.js node highlight <nodeId> --session app --snapshot <snapshotId>
node bin/rdt.js source reveal <nodeId> --session app --snapshot <snapshotId>- If port binding, browser launch, or session RPC is blocked, rerun the affected command with sandbox escalation.
rdtstill works againsttest-app/or another real React app withreactDetected: true.tree getreturns a snapshot identifier and node IDs scoped to that snapshot.node search,node inspect, andnode highlightresolve nodes correctly when given the same snapshot context.source revealalso resolves correctly with snapshot-aware lookup.- snapshot behavior is documented clearly enough that users know when
--snapshotis required, how long snapshots live, and what happens on expiry. - profiler flow still captures at least one real React update after the node identity fix.
- any discovered defects are either fixed or documented in this file with exact repro steps.
- Update this file first.
- Record:
- what changed
- why it changed
- what still works
- what remains unverified