I've lost many afternoons to a CI line that says:
Element not visible: [data-test=submit]
That's the entire debug surface. The screenshot shows a white page. The console log is empty because the assertion fired before the app had a chance to throw anything. If there's a video, it's hosted on a vendor behind an auth wall and gone in 30 days.
I released tracelane and peek this month. They're related — they share a recording engine and a design philosophy. This is why they work the way they do.
tracelane
The fix to the CI debugging problem isn't novel. rrweb records the DOM and console at usable fidelity. That part already exists.
The constraint I wanted to enforce is the interesting part: the resulting artifact must be a single .html file on disk.
Player inlined. Event blob inlined. No cloud upload. No external service. Open it in any browser, fully offline.
This sounds like a limitation. It isn't. The constraint changes everything about where the artifact can live. Attach it to a Jira ticket without configuring a vendor integration. Drop it in Slack, archive it in S3, send it to a contractor outside your network. Your bug tracker doesn't need to learn about a new vendor.
The file is the product.
How it works: one WebdriverIO Service that injects rrweb, polls the in-page buffer, attaches Chrome DevTools Protocol for failed-network capture, and builds the HTML when a test fails. Session replay, console errors, network failures — single artifact, every run.
The tradeoff is real: large test suites produce large files, and there's no cross-run history or aggregation. If you want to query "how often does this selector fail across 500 runs?", this isn't the tool. It's for debugging a specific failure, not analysing a pattern across many.
The closest commercial equivalents are Cypress Cloud, Replay.io, Sentry Session Replay. All good products. But they all require you to operate infrastructure or pay for a platform. I didn't want to do either for a side project, and the self-contained constraint is genuinely a different tradeoff — not a worse one.
npx @tracelane/cli init # WebdriverIO: installs and wires in one command
Playwright and Cypress integrations follow later this year.
peek
I spent an afternoon debugging a rendering issue with Claude Code. The component was flickering on scroll. I described what I was seeing — the timing, the element, the scroll depth — and got back confident hypotheses about CSS reflow and will-change. None of them were the problem. The problem was a stale cache response I could see in the network panel. Claude Code couldn't see the network panel.
That's the gap. Claude Code, Cursor, Cline — they can read the source on your disk. They can read documentation. They cannot see the rendered DOM, the network panel, or the console.error that just fired in the iframe you're wrestling with. Describing a bug in plain English to an AI agent is fixing a JavaScript bug over a phone call.
Peek is a Chrome MV3 extension and a stdio MCP server. Enable it per-origin from the side panel — off by default for every site. It records via the same substrate as tracelane, writes to a local SQLite DB at ~/.peek/sessions.db, and exposes 10 tools to your AI client over MCP — read tools for sessions, console errors, network failures, DOM snapshots, and a Playwright-repro generator; two gated write tools (see below):
- What the user clicked before the error fired
- Network requests with status ≥ 400 on a specific origin
- Reconstructed DOM at the timestamp a click happened
Write tools — clicks, inputs, navigation — are defined on the MCP surface but currently gated by an in-progress IPC bridge between your agent's MCP process and the browser native host. For now peek is effectively read-only; the consent-banner round-trip lands in a follow-up alpha.
No remote. The transport is stdio. Your agent launches peek-mcp as a child process, talks over stdin/stdout, kills it on exit. Captures live in your home directory until you delete them.
The constraint here is also the point: stdio means local-only. If your AI client runs in a sandboxed cloud environment, peek won't reach it. This is a tool for local development workflows, not CI pipelines.
npm i -g @peekdev/cli && peek init # wires the MCP in your client's config
Chrome Web Store submission is pending. Alpha testers load the extension unpacked.
Why one repo, one substrate
Both tools record. So the recorder is shared.
I forked PostHog's rrweb lineage into @cubenest/rrweb-core — pinned to a specific commit, with the masking primitives and screenshot fallback I needed already in place. PostHog's fork is ahead of upstream on the things that matter here: masking and large-DOM throttling.
The fork is vendored, not a transitive npm dep. That decision has a specific cause. The Shai-Hulud 2.0 supply-chain attack in late 2025 hit PostHog's rrweb lineage directly. Watching it unfold made the cost of every transitive dep that touches user DOM visible in a way it hadn't been before. One pinned SHA. One audit surface. Two products downstream.
The cost of vendoring is that I now own the security update cycle for the rrweb lineage manually. When PostHog ships a security patch, I have to evaluate it, test it, and cut a release. Renovate helps — the lineage has a 21-day cooldown so I'm not pulled into every upstream churn — but it doesn't eliminate the work.
Both live in one repo because the recorder is load-bearing. Splitting now means syncing the fork across two repositories manually. When each product justifies its own release cadence, they'll split.
What to expect from a side project
These are alpha. Every version starts with 0.1.0-alpha.. The API may shift.
I'm one maintainer. No SLA on response time. No promised release cadence. No paid infrastructure — the cost of doing nothing for a quarter is zero by design.
I wrote a SUSTAINABILITY.md that documents this honestly. It includes the wind-down plan: what happens if the project goes quiet, in what order, and what explicitly doesn't happen — no forced unpublish, no repo deletion, no quietly abandoning users while the README still says "actively maintained."
Supply-chain hygiene is in from day one: SLSA provenance on every publish, OpenSSF Scorecard weekly, Renovate with a 7-day cooldown on deps and 21 days on the rrweb lineage specifically. Not because I expect a problem. Because the cost of doing it right at the start is two days. The cost of being un-hardened the day a Shai-Hulud-class attack hits is the project.
If you're evaluating either tool for production use, treat it as alpha-tier OSS: pin the version, audit before adopting, be prepared to fork if it goes quiet. That's not a disclaimer — it's just accurate, and the doc says so plainly.
The same constraint that makes tracelane's artifact vendor-agnostic and peek's data local-only applies to the project itself: nothing here requires a central server to keep working. If I disappear for six months, your pinned version still generates test reports and your agent still has browser context. The design doesn't depend on my continued availability any more than it has to.
Apache 2.0. No telemetry from either tool. Issues and PRs at Cubenest/rrweb-stack.
If you find them useful, GitHub Sponsors keeps maintenance at the cadence I can sustain alongside a day job.