QA engineers spend their days reproducing bugs that were already reproduced once. The problem isn't skill. It's that we throw away the evidence.
Here's a loop every QA engineer knows by heart.
You find a bug. You write it up: steps to reproduce, expected, actual, a screenshot of the broken state. You attach it to the ticket. You feel good about it — that was a careful report.
Two days later it comes back: "Cannot reproduce."
So you go back. And this time you notice the request that returned a 500 only when the cart had exactly one item. Or the Authorization header that was missing because a token refresh had silently failed four clicks earlier. Or the localStorage flag left behind by a feature you tested an hour ago, which no clean browser profile will ever have.
None of that was in your report. Not because you were careless, but because a bug report is a lossy compression of a session. You witnessed a few hundred kilobytes of state and behavior, and you shipped a screenshot and five bullet points.
The developer then has to decompress your ticket back into a running system. They can't. Nobody can.
"Cannot reproduce" is a data problem
We treat irreproducible bugs as a communication failure — write better steps, be more precise, add more detail. That framing puts the burden on the person who has the least ability to fix it.
It's really a capture problem. At the moment the bug happened, all of the following existed in your browser, and all of it was true at the same time:
- Every
fetchandXHRthe page made, with headers, bodies, and status codes - Every WebSocket frame that went in or out, in order
- Your cookies — including the
HttpOnlyones you can't read from the console -
localStorageandsessionStorage, with whatever accumulated junk made your profile different from a clean one - What the page actually looked like, moving, at the moment it broke
- The exact sequence of clicks that got you there
And then you closed the tab.
Everything above is already in the browser. It just isn't collected unless somebody decided to collect it before the bug happened. Which is the trap — you don't know a bug is coming until it's already behind you.
What complete context looks like
I got tired of this loop, so I built a Chrome extension for it. It's called FullTrace, it's on the Chrome Web Store, and it does one thing: it records everything a bug report should have contained, and gives it to you as a single ZIP.
The workflow is deliberately boring:
- Open the page.
- Click Record.
- Reproduce the bug like you normally would.
- Click Stop, then Export ZIP.
- Drag the ZIP into Jira, Linear, or Slack.
That's it. No account, no setup, no project configuration.
What makes it useful isn't any single capture — it's that they all happen together, timestamped against the same session. The request log is only half an answer; the request log next to the cookies next to a video of what you clicked is a reproduction.
The anatomy of a capture
This is what lands on your disk. I find the shape of it more convincing than any feature list:
fulltrace-report-2026-10-26T14-22-09.zip
├── metadata.json URL, title, user agent, mode, counters
├── screenshot.png final state of the tab
├── screenshots/
│ ├── 01.png annotated by you, mid-session
│ └── 02.png
├── video.webm screen recording (optional)
├── network/
│ ├── requests.json machine-readable
│ ├── requests.txt human-readable dump
│ ├── websockets.json
│ └── websockets.txt timeline, → outgoing, ← incoming
├── cookies/
│ ├── cookies.json
│ └── cookies.txt
└── storage/
├── localStorage.json
└── sessionStorage.json
Two files for each data source — JSON for tooling, plain text for the human who opens the ticket at 9am and wants to skim. The .txt versions get read a lot more than I expected.
Six toggles, not two buttons
The first version had two modes and it was wrong. Capturing a screen recording of your entire desktop, with microphone audio, because you wanted to log one GraphQL call is absurd.
So the capture sources are individually switchable: Network, WebSocket, Cookies, Storage, Screen video, Microphone. Everything except the microphone is on by default, and the microphone is only available when video is on.
The header tells you what you're about to do, derived from the toggles — Full Trace with everything including video, Trace with everything but video, Custom recording for anything else. And the toggles really gate the capture; the service worker checks each source before collecting it, rather than collecting everything and filtering at export.
There are also two standalone quick actions for when you don't need a session at all: a one-shot annotated screenshot, and a video-only recording.
The details that turned out to matter
A few things I added reluctantly and now wouldn't ship without.
Drawings that disappear. While recording, you can draw on the page — brush, arrow, circle, rectangle — and each stroke holds for two seconds, then fades out over the next one. They're never saved anywhere. Their entire purpose is to exist inside the video for the three seconds where you're saying "this number, right here, is wrong." Permanent annotations clutter a recording; ephemeral ones direct attention exactly when you need it.
A toolbar that survives navigation. Once recording starts, a small floating toolbar appears in the top-right of the page — stop, screenshot, draw. It lives in a closed Shadow DOM at z-index: 2147483647, so the page's CSS can't break it and the page's own modals can't cover it. SPA navigation doesn't touch it, it re-attaches itself after a full page reload, and it survives the service worker being torn down mid-session, which Chrome does constantly under Manifest V3. Recording something that outlives a page reload is most of the value; a tool that quietly stops recording on navigation is worse than no tool.
WebSocket traffic, with its own filter. Realtime apps break in realtime ways, and DevTools makes WS frames genuinely painful to export. FullTrace subclasses window.WebSocket at document_start, so every connection the page opens afterward is instrumented — opens, every send(), every inbound message, close codes, errors. instanceof checks and the page's own handlers keep working untouched. HTTP and WebSocket get separate URL filters, because the pattern that usefully narrows /api/v2/ down to something readable will silently delete all of your WS traffic.
Numbered markers in the annotator. Rectangles and arrows are obvious. Numbered markers (①②③) turned out to be the tool I reach for most, because they let the screenshot carry an ordered argument instead of a pile of circles.
The honest limitations
Any tool that claims to capture everything is lying, so here's where this one stops.
The ZIP is sensitive. It contains your cookies and storage, which is effectively a clone of your authenticated session for that origin. Treat the file like a credential. Don't drop it into a public channel, and think before attaching it to a ticket that contractors can read. This is inherent to the problem — the cookies are why the bug reproduces — but it deserves saying out loud rather than burying in a privacy policy.
Binary WebSocket frames aren't decoded. You get type and byte length, not contents. If your app speaks Protobuf over WS, this won't decode it for you.
Text frames are truncated at 10,000 characters. Enough for essentially every JSON message, not enough for someone streaming a document through a socket.
Connections opened before you hit Record are partially covered. They keep working, and events on them are captured from the moment recording starts — but the handshake and anything before it are gone.
Screen recordings get large. Video is WebM (VP9, falling back to VP8), up to 30 fps. A few minutes of screen capture produces a file measured in tens of megabytes. For most bugs, the network log plus two annotated screenshots is the better trade.
Nothing leaves your machine
The extension has no analytics, no telemetry, and no accounts. There's no backend — there isn't a server to send anything to. Everything is captured into local browser storage and stays there until you click Export, at which point it becomes a file on your disk. The last 10 recordings are kept locally for 5 days by default, configurable up to 20 sessions and 10 days, and you can delete any of them from the popup.
This isn't a privacy posture adopted for marketing reasons. A tool that uploads your cookies and session storage somewhere would be indefensible, and I'd rather not have that liability.
Try it
FullTrace is a Manifest V3 extension, requires Chrome 116 or newer, and is localized into English, Ukrainian, Russian, German, French, Italian, and Simplified Chinese.
Install it from the Chrome Web Store — or read more at fulltrace.app.
If you try it on a real bug, I'd genuinely like to know what it failed to capture. That's the only feedback that's made this thing better so far.
Top comments (0)