Skip to main content

pocketjs-devtools

Debug PocketJS apps with Pocket DevTools — deterministic input-tape time travel, component-tree inspection with on-device highlight, pause/step, REPL eval and console on real PSP hardware over PSPLINK, on-demand screenshots, and headless tape replay for regression evidence. Use when asked to debug UI behavior, reproduce or bisect a visual regression, inspect the component tree, capture device screenshots, or verify a fix against a recorded session.

Quellinformationen

Repository
pocket-nexus/pocketjs
Letzte Quellaktivität
11. September 2026 um 08:54
Erkannte Sprache von SKILL.md
Englisch
Sterne
1.662
Forks
94

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
pocketjs-devtools
description
Debug PocketJS apps with Pocket DevTools — deterministic input-tape time travel, component-tree inspection with on-device highlight, pause/step, REPL eval and console on real PSP hardware over PSPLINK, on-demand screenshots, and headless tape replay for regression evidence. Use when asked to debug UI behavior, reproduce or bisect a visual regression, inspect the component tree, capture device screenshots, or verify a fix against a recorded session.
# Pocket DevTools ## Overview PocketJS is fixed-dt deterministic and its entire per-frame input is one button bitmask, so a recorded input tape replays any session byte-exactly. DevTools (design: repo `docs/DEVTOOLS.md`) is built into every bundle: a flight recorder is ALWAYS on (36 000-frame ring), and a JSON-line debug channel connects the runtime to a desktop panel over WebSocket (browser host) or the PSPLINK USB mailbox (real PSP). Prefer the headless tape workflow for agent debugging — every question is answerable from the terminal. Use the panel when a human is co-driving. ## Headless workflow (no screen needed) Keep per-run tapes, hashes, captures, and logs in ignored output. For example: ```bash mkdir -p .pocket-build/validation/devtools bun run tape record hero-main --frames 180 --input "5:64,40:8192" --out .pocket-build/validation/devtools/t.json bun run tape replay hero-main .pocket-build/validation/devtools/t.json --hashes .pocket-build/validation/devtools/h.json bun run tape replay hero-main .pocket-build/validation/devtools/t.json --assert .pocket-build/validation/devtools/h.json --outdir .pocket-build/validation/devtools bun run tape replay hero-main .pocket-build/validation/devtools/t.json --png 60,120 --outdir .pocket-build/validation/devtools bun run tape tree hero-main .pocket-build/validation/devtools/t.json --at 60 bun run tape:check # committed session golden ``` - Input masks are contracts/spec/spec.ts `BTN` values (`CIRCLE=0x2000=8192`, `DOWN=0x40=64`). - A regression workflow: record/obtain a tape on the OLD build → `--hashes` → switch builds → `--assert` names the exact first frame that changed → `--png <that frame>` on both builds to see the difference. - Tapes exported from ANY host (panel "Export", or `__pocketDevtools.dumpTape()` in the REPL) replay headlessly. `startFrame > 0` means the ring wrapped — replay is then an approximation (warned automatically). - Committed session goldens live in `tests/tapes/`; regenerate hashes only when a visual change is intended. - A replay capture or exported session is temporary validation output. Promote it to a committed fixture only when a named regression test consumes it. Put selected screenshots in PR attachments and keep raw captures, logs and receipts outside the tracked tree, following `AGENTS.md`/`CLAUDE.md`. ## Panel workflow (one command) ```bash bun run devtools # panel + WS hub + mailbox bridge, one process bun run devtools cards # + build, USB-link and launch cards on a real PSP ``` Panel at `http://127.0.0.1:8130/devtools`. `bun run devtools` auto-detects an already-running `bun psplink` / `bun run hw` usbhostfs session and bridges into it instead of fighting for the cable (relaunch the app there so it probes the mailbox at boot). Shortcuts: `o` open panel, `r` rebuild+relaunch (managed sessions), `q` quit. Panel capabilities: hover tree → highlight ON THE DEVICE SCREEN (core-drawn overlay, works on real PSP); click pins; pause/step/resume freeze the whole world; tape strip with click-to-seek (browser host reloads and deterministically fast-forwards); REPL evals in the app global scope between frames; 📷 screenshot button downloads a PNG (browser: canvas; PSP: raw VRAM dump over usbhostfs, converted by the bridge — pixels never cross the JSON channel). ## Native desktop (macOS) specifics - `pocket-ui-wgpu` hosts (OpenStrike desktop, `engine/pocket3d/examples/uihost`) carry the same file-mailbox transport as the PSP, pointed at `$POCKETJS_DBG_DIR` (else the process cwd). Workflow: `bun run devtools --dir <root> --port 8131` FIRST (explicit `--dir` wins over a running PSPLINK session, so a PSP panel and a desktop panel can run side by side), then launch the app with that cwd — e.g. `cargo run -p openstrike` from the open-strike repo root. The app probes `pocketjs-dbg/enable` once at mount, like the PSP boot probe. - Everything except 📷 screenshots works identically (desktop `__dbgShot` is unimplemented; the panel button logs a warning). `hello` reports `host:"desktop"` via `ui.__host`. ## Real-PSP specifics - Transport = `pocketjs-dbg/{enable,in,out}.jsonl` on the usbhostfs share (`host0:`). The app probes `enable` ONCE at boot — start the bridge before launching the app, or relaunch after. No PSPLINK → zero cost. - Shim polls every 10 frames on PSP (~166 ms hover latency); every frame on other hosts. - `console.log` on PSP: the shim installs a safe no-op console at mount (QuickJS has none) and upgrades it to a channel mirror when a transport attaches. - PPSSPP finds the mailbox via `host0:` = the EBOOT's own directory (so `bun run devtools <app>` bridging the target dir works for PPSSPP loading that EBOOT too); the `ms0:` fallback also exists (memstick root). ## Protocol quick reference (JSON lines) Panel→device: `inspect{id}` (0 clears) · `pause` · `resume` · `step` · `getTree` · `eval{id,code}` · `dumpTape` · `screenshot` · `seek{frame}` / `replay{tape}` (browser host-level). Device→panel: `hello{app,host,frame}` · `tree{root:{i,t,n,c,x,k}}` · `inspect{id,rect|null}` · `stats{frame,nodes,tapeLen,paused}` · `log` · `error{frame,message}` · `evalResult{id,ok,value}` · `tape{tape}` · `screenshot{frame,data}`. ## Gotchas - `rect: null` / `debugRectXY() === -1` = the node is never painted (display:none, detached, or inside a `paint_3d` perspective subtree — 3D interiors aren't captured; the perspective ROOT is). - Adding spec ops: edit `contracts/spec/spec.ts` → `bun contracts/spec/gen-rust.ts` → implement on `Ui` + BOTH hosts (`hosts/psp/src/ffi.rs`, `engine/wasm/src/lib.rs`) + `hosts/web/ wasm-ops.js` + `HostOps` (optional members) — `tests/contract.ts` locks the spec halves together. - `debugName` / `<Named>` are JS-mirror-only: provably zero pixel impact (goldens + tape hashes unchanged). Name every function component's root element; the tree panel and `tape tree` output read like the source. - Shim tests live in `tests/devtools.test.ts` (mock ops + in-process transport; `--conditions=browser` required). Core pause/inspect tests in `engine/core/src/tests.rs`. - Missing QuickJS symbols (vendored libquickjs-sys is minimal): declare a local `extern "C"` block — precedent in `hosts/psp/src/main.rs` and `ffi.rs`. - NEVER hand VRAM addresses (`0x4xxxxxxx` uncached mirror) to usbhostfs IO: its send path runs dcache writeback + USB bulk DMA on the caller's buffer and hangs the device on the first 64 KB block (frozen PSP, 0-byte file). Bounce through a cached-RAM buffer in chunks (`dbg::shot`). ms0: (Memory Stick driver) tolerates VRAM-direct writes — that's why cap_dump_frame gets away with it and why PPSSPP won't reproduce the hang. - The GE writes framebuffer alpha as 0: any raw-framebuffer consumer must force alpha opaque (bridge `convertShot`, e2e's `-alpha off`) or the PNG renders fully transparent. - When hardware behavior "mysteriously regresses" after running e2e scripts, check WHICH build is on disk before debugging anything else: e2e capture specs rebuild the EBOOT/PRX in place, and capture builds `sceKernelExitGame()` after their window — reloading one on the PSP looks exactly like a freeze/hang a few seconds after boot. Rebuild your intended feature set first; only then trust symptoms. (Cost an hour on the OpenStrike bring-up: a phantom "mailbox freeze" was a self-exiting e2e build the whole time.) - The whole device-side path (eval, console, screenshot) is testable without hardware, at host0: fidelity: **PPSSPP maps the EBOOT's own directory as `host0:`**, so a mailbox in `hosts/psp/target/mipsel-sony-psp/debug/pocketjs-dbg/` is found by the SAME probe hardware uses. Create enable/in/out there, run PPSSPPHeadless with a timeout, append commands to in.jsonl mid-run, read out.jsonl. (This also means a stale hardware-session mailbox hijacks emulator runs — tests/e2e/ppsspp.ts removes it before each golden run.)
Auf GitHub ansehen