Skip to content

Add browser-trace skill#83

Merged
derekmeegan merged 5 commits intomainfrom
derek/add-browser-observability-skill
Apr 27, 2026
Merged

Add browser-trace skill#83
derekmeegan merged 5 commits intomainfrom
derek/add-browser-observability-skill

Conversation

@derekmeegan
Copy link
Copy Markdown
Contributor

@derekmeegan derekmeegan commented Apr 27, 2026

Summary

A new skill that captures a full DevTools-protocol trace alongside any browser automation, then bisects the stream into per-page searchable buckets. Drop in next to the existing browser skill — it is purely observational, never drives the page, and works with browse, Stagehand, Playwright, or anything else speaking CDP.

What's in the box

skills/browser-trace/:

  • SKILL.md — when to use it, local + Browserbase quickstarts, filesystem layout, summary shape, drill-down via query.mjs
  • REFERENCE.md — full bisect map, per-page drill-down recipes, Browserbase platform pairing, troubleshooting matrix
  • EXAMPLES.md — five end-to-end debug scenarios (failing form submit, third-party network audit, page hang, prod JS-exception repro, attaching to an in-flight Browserbase session)
  • scripts/ — Node ESM, no third-party deps (so no npm install step):
    • start-capture.mjs / stop-capture.mjs / snapshot-loop.mjs — lifecycle of a trace run
    • bisect-cdp.mjs — splits raw.ndjson into session-wide buckets + per-page subdirs and writes a structured summary.json
    • query.mjs — drill-down helper: list, page <pid> [bucket], errors, hosts, host <h>, timeline
    • bb-capture.mjs / bb-finalize.mjs — Browserbase wrappers that handle --keep-alive, connectUrl resolution, manifest stamping (session id, region, live debugger URL), and post-run artifact pulls (bb sessions get, bb sessions logs, bb sessions downloads get)
  • lib.mjs — shared helpers (run dir, JSONL I/O, BUCKETS table, isTopNav)

Filesystem layout produced

.o11y/<run-id>/
  manifest.json                                browserbase block on remote runs
  index.jsonl                                  ts → screenshot/dom/url joins
  cdp/
    raw.ndjson                                 full firehose, NDJSON
    summary.json                               sessionId, duration, totalEvents, pages[]
    network/, console/, page/, runtime/, log/, target/, dom/
    pages/<pid>/                               per-page slices, indexed by top-level frameNavigated
      url.txt, summary.json, raw.jsonl
      network/, console/, page/, ...
  screenshots/<ts>.png
  dom/<ts>.html
  browserbase/                                 written by bb-finalize on remote runs
    session.json, logs.json, downloads.zip

summary.json is the entry point for any analysis:

{
  "sessionId": "45f28023-…",
  "duration": { "startMs": 1777312533000, "endMs": 1777312609000, "totalMs": 76000 },
  "totalEvents": 420,
  "pages": [
    {
      "pageId": 0,
      "url": "https://example.com/",
      "startMs": 1777312533000, "endMs": 1777312538886, "durationMs": 5886,
      "eventCount": 60,
      "domains": {
        "Network": { "count": 18, "errors": 1 },
        "Console": { "count": 2 },
        "Page":    { "count": 24 },
        "Runtime": { "count": 13 }
      },
      "network": { "requests": 4, "failed": 1, "byType": { "Document": 2, "Script": 1, "Other": 1 } }
    }
  ]
}

Drilling in

node scripts/query.mjs my-run list                    # one-line table of pages
node scripts/query.mjs my-run page 1                  # full summary for page 1
node scripts/query.mjs my-run page 1 network/failed   # cat that bucket
node scripts/query.mjs my-run errors                  # all errors across pages, attributed by pid
node scripts/query.mjs my-run hosts                   # top hosts by request count
node scripts/query.mjs my-run host api.example.com    # all requests/responses for one host

Why "trace" and not "observability"

browser-trace reads more cleanly as a slash command, accurately describes the artifact (a time-ordered recording of CDP events plus aligned screenshots/DOM), and pairs naturally with future research-loop skills (e.g. autobrowse) that can use the per-page summary as their structured "what just happened" data.

Compatibility note

The CDP firehose is sourced via browse cdp <target>, which currently ships under the alpha tag of @browserbasehq/browse-cli (@browserbasehq/browse-cli@alpha, since browserbase/stagehand#1905). Lifecycle events (Page.lifecycleEvent) specifically require browserbase/stagehand#2056. The skill's compatibility: field calls this out, and bisect/query degrade gracefully on older builds.

Marketplace + README

  • marketplace.json registers a new browser-trace plugin pointing at ./skills/browser-trace (mirrors the browserbase-cli pattern — companion skill, own plugin, can be installed independently).
  • README.md skills table gets a new row, placed in the observability cluster between site-debugger and bb-usage.

How it was built

Dogfooded end-to-end against:

  • Local Chrome at localhost:9222
  • A live Browserbase session via bb sessions create --keep-alive + bb-capture.mjs <session-id>

Each iteration ran the full pipeline (capture → automation → stop → bisect → query) and surfaced bugs (CDP timestamp clock-mixing, race on session.json after --release, partial DOM-dump cleanup) that were fixed before submission.

Test plan

  • jq -e . .claude-plugin/marketplace.json — JSON validates
  • npx skills add browserbase/skills lists the new plugin
  • On Claude Code: /plugin install browser-trace@browserbase works
  • Run the local quickstart in SKILL.md against any debuggable Chrome — capture, bisect, query.mjs list produces the page table
  • Run the Browserbase quickstart with BROWSERBASE_API_KEY set — bb-capture.mjs --new stamps the manifest and prints the live debugger URL; bb-finalize.mjs --release writes session.json with status: COMPLETED

🤖 Generated with Claude Code


Note

Medium Risk
Adds a new tracing skill that spawns/kills background processes, writes artifacts to disk, and can create/release Browserbase sessions via bb, so incorrect usage could leak processes or impact active sessions. Existing skills are unchanged aside from marketplace/README registration.

Overview
Introduces a new browser-trace skill that records a read-only CDP “firehose” alongside screenshots and DOM snapshots, then post-processes the run into searchable per-domain and per-page JSONL buckets plus a cdp/summary.json rollup.

Adds Node (ESM) helper scripts to start/stop captures, bisect raw.ndjson into the output tree, query runs (list, page, errors, hosts, timeline), and optionally integrate with Browserbase via bb to create/attach sessions, stamp manifests with session metadata, fetch artifacts, and optionally release sessions.

Registers the new plugin in .claude-plugin/marketplace.json and documents it in README.md, with full reference and example playbooks under skills/browser-trace/.

Reviewed by Cursor Bugbot for commit 53fe4fb. Bugbot is set up for automated code reviews on this repo. Configure here.

Capture the full CDP firehose plus periodic screenshots and DOM dumps
alongside any browser automation (browse, Stagehand, Playwright, Puppeteer
— anything that speaks CDP), then bisect the stream into per-page
searchable buckets keyed by top-level navigations.

The skill is purely observational: it adds a second CDP client that
enables read-only domains (Network, Console, Runtime, Log, Page) and
never sends action commands, so it can attach to an in-flight automation
without disrupting it.

Layout produced under .o11y/<run-id>/:
  manifest.json, index.jsonl
  cdp/raw.ndjson                              full firehose, NDJSON
  cdp/summary.json                            {sessionId, duration, totalEvents, pages[]}
  cdp/<domain>/...                            session-wide buckets
  cdp/pages/<pid>/                            per-page slices, indexed by Page.frameNavigated
    summary.json, raw.jsonl, network/, console/, page/, ...
  screenshots/<iso-ts>.png, dom/<iso-ts>.html
  browserbase/                                added by bb-finalize.sh on remote runs

Scripts (pure bash + jq, no Python/Node deps beyond the existing browse CLI):
  start-capture.sh / stop-capture.sh / snapshot-loop.sh
  bisect-cdp.sh — splits raw.ndjson into session-wide and per-page buckets,
                  writes the structured summary.json
  query.sh      — drill-down helper: list, page <pid> [bucket], errors,
                  hosts, host <h>, timeline, summary
  bb-capture.sh / bb-finalize.sh — Browserbase wrappers; bb-capture handles
                  --keep-alive + connectUrl resolution + manifest stamping with
                  the session id, region, and live debugger URL; bb-finalize
                  writes browserbase/{session.json, logs.json, downloads.zip}
                  and optionally releases the session

The CDP firehose is sourced via `browse cdp <target>`, which streams
DevTools-protocol events as NDJSON. That command requires
@browserbasehq/browse-cli alpha or a release that includes
browserbase/stagehand#1905; lifecycle events specifically require
browserbase/stagehand#2056.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Comment thread skills/browser-observability/scripts/query.sh Outdated
Comment thread skills/browser-observability/scripts/query.sh Outdated
Comment thread skills/browser-observability/scripts/bb-capture.sh Outdated
Comment thread skills/browser-observability/scripts/query.sh Outdated
Mirrors the cookie-sync skill's pattern: each script becomes a Node ESM
module, with a shared scripts/lib.mjs for common helpers and a minimal
package.json (no dependencies — pure Node stdlib, so no `npm install`
step is needed).

  scripts/lib.mjs                      shared helpers, BUCKETS table, isTopNav
  scripts/start-capture.mjs            spawn detached `browse cdp` + sampler
  scripts/snapshot-loop.mjs            screenshot+DOM+url poll loop (internal)
  scripts/stop-capture.mjs             SIGTERM+grace+SIGKILL, sweep .partials
  scripts/bisect-cdp.mjs               page-aware bisect + structured summary.json
  scripts/query.mjs                    drill-down: list/page/errors/hosts/host/timeline
  scripts/bb-capture.mjs               Browserbase wrapper (--new or attach by id)
  scripts/bb-finalize.mjs              pull session.json/logs.json/downloads.zip

Output layout, per-page bisection logic, summary.json shape, and the
query.mjs subcommands are identical to the bash versions. SKILL.md,
REFERENCE.md, and EXAMPLES.md updated to invoke `node scripts/foo.mjs`
instead of `bash scripts/foo.sh`.

Dogfooded end-to-end against a Browserbase session and against the
saved /Users/d/Desktop/browserbase/o11y-demo-1 run — page bisection,
durations, error attribution, host counts all match the bash output.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Comment thread skills/browser-trace/scripts/query.mjs
Shorter, more action-oriented name that reads cleanly as a slash command
and pairs naturally with the autobrowse skill (which can use the trace
output as the structured "what just happened" data for its loop).

Renames:
- skills/browser-observability/ → skills/browser-trace/
- SKILL.md frontmatter `name`, doc H1s, package.json name
- marketplace.json plugin entry (name, skills path, description, keywords)
- README.md skills table row
- "observer" → "tracer" throughout the prose for consistency with the new name

No behaviour change — all script interfaces, output layout, summary.json
shape, and query.mjs subcommands are unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@derekmeegan derekmeegan changed the title Add browser-observability skill Add browser-trace skill Apr 27, 2026
Comment thread skills/browser-trace/scripts/query.mjs
Fix three issues flagged on PR #83:

- query.mjs cmdTimeline: was printing every navigation followed by every
  lifecycle event, so all `[NAV …]` lines bunched before any
  `[init]`/`[load]` lines. Now reads cdp/raw.ndjson directly and emits
  top-level frameNavigated and lifecycle events in actual stream order.

- query.mjs cmdHost: was using `url.startsWith('https://' + hostname)`,
  which would match `https://api.example.com.evil.tld/...` when the user
  asked for `api.example.com`. Switched to exact `new URL(url).hostname`
  comparison via a small `hostMatches` helper.

- query.mjs: removed the unused `emitJsonl` helper (defined but never
  called — same class of dead-code issue Bugbot raised against the bash
  version's `cat_bucket_across`).

Bugbot also flagged the bash bb-capture.sh's `| grep ... || true` that
silently swallowed start-capture failures. The .mjs version already
handles this correctly via `if (start.status !== 0) process.exit(...)`,
so no change needed there.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 4f28fb7. Configure here.

Comment thread skills/browser-trace/scripts/query.mjs
Comment thread skills/browser-trace/scripts/bisect-cdp.mjs
Two more issues from Cursor Bugbot on PR #83:

1. Console errors and warnings were being silently dropped from the
   per-page summary. computePageSummary classified each event by
   `method.split('.')[0]`, which puts Runtime.consoleAPICalled events
   into a `Runtime` bucket — but errors/warnings from those events were
   being recorded under the key `Console`. The output loop iterates
   `counts.entries()` and looks up errors/warnings by that domain key,
   so the Console errors/warnings never made it into the rollup.

   Added a `domainFor(method)` helper that returns 'Console' for
   `Runtime.consoleAPICalled` and the standard `method.split('.')[0]`
   otherwise. Now matches the schema documented in SKILL.md.

2. cmdHosts grouped by `URL.host` (port included) but cmdHost compared
   against `URL.hostname` (port stripped), so `hosts` output couldn't
   be piped into `host` when a port was present. Switched
   `hostMatches` to `URL.host` for consistency. Impostor protection
   (rejecting `example.com.evil.tld` for `example.com` queries) is
   preserved by the exact-equality check.

Verified against /Users/d/Desktop/browserbase/o11y-demo-1: every page
now shows Console + Runtime as distinct domain blocks; page 4
(httpbin/418) correctly attributes the 418 status to a Log error.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@derekmeegan derekmeegan merged commit 2a3bbb3 into main Apr 27, 2026
1 check passed
@derekmeegan derekmeegan deleted the derek/add-browser-observability-skill branch April 27, 2026 21:58
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.

2 participants