QPD Documentation

QA Pretest Design — Figma vs live
← Back to app

What QPD is#

QPD (QA Pretest Design) compares a Figma design against a live pretest page, section by section, and reports every visual and copy difference — with a box drawn around each one on the live screenshot.

It exists to replace the manual pass where someone puts the design and the built page side by side and squints at them looking for wrong copy, wrong colours, missing badges and shifted spacing. QPD does that pass, lists what it found, and lets you confirm or reject each item and hand the result to a developer.

Good to know QPD never changes your Figma file or the live page. It only reads them. Everything it produces lands in your local runs folder.

Core concepts#

Five words are used throughout the app. Learning them makes everything else obvious.

TermWhat it means
RunOne QA session against one Figma file + one pretest URL. Identified by a timestamp like 20260723_093719, and it owns a folder on disk. A run's identity never changes.
SectionOne horizontal band of a page — hero, USP ribbon, FAQ. QPD slices both the Figma frame and the live page into sections automatically during Phase 1.
ComparisonA pairing of one Figma section with one live section — the unit QPD actually compares. You build these on the Pair page.
VersionEvery time you run a comparison you get a new version (v1, v2, v3…). Versions are immutable snapshots — re-running never overwrites earlier results, so you can always look back.
FindingOne reported difference: a label, what Figma shows, what the live page shows, a suggested fix, a severity, and a box on the live image.

How it works#

Phase 1 — capture

Pure Python, no AI. Two branches run in parallel:

  • Live — opens the pretest URL in a headless Chromium, hides popups, and slices the page into sections. For each it saves a PNG plus rich data: every element's position and computed styles.
  • Figma — exports each top-level frame as a PNG, plus the full node tree and every text string with its position and font.

Typically ~60–90 seconds. This is the step that produces the thumbnails you pair up.

Phase 2 — compare

Runs per comparison, and this is the part that uses AI:

  1. A deterministic copy diff (no AI) computes exactly which text differs between Figma and live. It compares the rendered text — Figma's text-transform / textCase and the live page's CSS casing are applied first — and it is case-sensitive, so HYBRID vs Hybrid is flagged, but a label that merely renders uppercase on both sides is not.
  2. A single Claude Opus review pass (high thinking) reads both images plus all the rich data, finds every difference, self-verifies its own list — dropping anything it can't confirm on the image and correcting boxes — and outputs the final findings in one go.
One pass, not two Earlier versions ran a separate finder then verifier. QPD now does both jobs in a single high-thinking Opus pass — just as accurate, noticeably faster, and it doesn't hand off a list between two models. (You can switch back to the two-model path with QPD_PHASE2_MODE=split — see Configuration.)

Requirements#

WhatWhyNotes
Claude Code CLI, logged inruns the Phase 2 AI reviewNeeds a Claude account with Code access. This is the one thing that cannot be automated.
Python 3.10+the whole appPre-installed on macOS; must be installed on Windows.
Chromium (via Playwright)captures the live page~150 MB, downloaded once by a command below.
Figma accessreads the designA REST token or the bundled plugin — see Figma access.
~600 MB free diskbrowser + deps + runsEach run adds roughly 20–60 MB of screenshots.
Not required You do not need Node.js, npm, or bun. Earlier versions did; the relay is now pure Python.

Install — macOS mac#

  1. Get the code.
    git clone https://github.com/rooban-sappani/puffy-qa-pretest-app.git ~/puffy-qa-pretest-app
    cd ~/puffy-qa-pretest-app
  2. Install the Python packages.
    python3 -m pip install -r requirements.txt
  3. Download the browser (~150 MB, once).
    python3 -m playwright install chromium
  4. Install Claude Code if you don't have it.
    curl -fsSL https://claude.ai/install.sh | bash
    Then log in.
  5. Install the helper scripts. They ship inside this repo — just copy them into place:
    mkdir -p ~/.claude/skills
    cp -R vendor/skill/qa-pretest-design ~/.claude/skills/
    Prefer not to touch ~/.claude? You don't have to. Point QPD at the bundled copy instead: export QPD_SKILL_DIR=~/puffy-qa-pretest-app/vendor/skill/qa-pretest-design (see Configuration).
  6. Start it.
    python3 server/app_server.py
    Open http://localhost:8791.
First launch macOS may ask whether Python can accept incoming network connections. Allow it — that's the local web server.

Install — Windows windows#

  1. Install Python 3.10+ from python.org.
    Don't miss this Tick “Add Python to PATH” on the first screen of the installer. Almost every Windows setup problem traces back to this checkbox.
  2. Get the code (PowerShell):
    git clone https://github.com/rooban-sappani/puffy-qa-pretest-app.git $HOME\puffy-qa-pretest-app
    cd $HOME\puffy-qa-pretest-app
  3. Install the Python packages.
    python -m pip install -r requirements.txt
  4. Download the browser.
    python -m playwright install chromium
  5. Install Claude Code, then log in.
    irm https://claude.ai/install.ps1 | iex
  6. Install the helper scripts — they ship in this repo:
    New-Item -ItemType Directory -Force $HOME\.claude\skills
    Copy-Item -Recurse -Force vendor\skill\qa-pretest-design $HOME\.claude\skills\
  7. Start it.
    python server\app_server.py
    Open http://localhost:8791.
Windows notes

Use python, not python3. If Windows Firewall prompts on first start, allow access on private networks.

Claude Code login#

QPD drives the Claude Code CLI on your machine. It uses your Claude account, and comparisons consume your usage — nothing is billed to a shared key.

claude

On first launch it opens a browser to sign in. Complete that, then quit with Ctrl+C. This is a one-time step per machine.

Check you're logged in

claude --version

To confirm an account is actually attached, look for oauthAccount in ~/.claude.json (%USERPROFILE%\.claude.json on Windows). If it's missing, you're not logged in.

Required Without this, Phase 1 still works (you'll get all your sections and thumbnails) but every comparison in Phase 2 will fail.

Verify your install#

Run these before your first real run — each should print without error.

CheckCommandExpected
Python packagespython3 -c "import playwright, websocket, websockets, PIL"no output
Browserpython3 -m playwright install --dry-run chromiumreports already installed
Claude CLIclaude --versiona version number
Helper scriptsls ~/.claude/skills/qa-pretest-design/scriptsa list of *_qpd.py files
Paths QPD will usepython3 bridge/paths.pyskill dir, runs dir, ports

Starting & stopping#

Start

cd ~/puffy-qa-pretest-app
python3 server/app_server.py

Then open localhost:8791. Leave the terminal open — closing it stops the server.

Run it in the background

mkdir -p ~/Documents/QPD
nohup python3 server/app_server.py > ~/Documents/QPD/server.log 2>&1 &

Stop

Ctrl+C in the terminal, or:

pkill -f app_server.py          # macOS
powershell -NoProfile -Command "Get-CimInstance Win32_Process | Where-Object { $_.CommandLine -like '*app_server.py*' } | ForEach-Object { Stop-Process -Id $_.ProcessId -Force }"  # Windows
Safe to stop Stopping the server never loses results — everything is written to disk as it completes. A comparison that was mid-run is marked failed and can simply be run again.

Choose what opens first

In the app header, choose File → Settings. Under Opening screen, choose:

  • Home — see, search, rename, select, and delete your runs.
  • Open the most recent run — continue from the latest results.
  • Show New Run — open the New Run form immediately, pre-filled with the latest run’s inputs.

Settings also contains theme, default AI, Figma token, and Asana PAT. Secrets are saved privately and are never displayed again. A direct link to a specific run always opens that run instead.

Home & run library#

Click the Puffy logo and QPD name at any time to return Home. Every run appears as a card with its status, AI-comparison progress, capture count, and total time.

ActionHow it works
Open a runClick anywhere on its card outside the checkbox and rename button.
Find a runSearch by name or run ID, then sort by newest, name, or completion.
RenameUse the pencil on a card, or select one card and choose Edit → Rename selected run.
Delete severalSelect their checkboxes—or Select shown—then choose Delete. Runs are moved to Trash.

Figma access — which option#

QPD needs to read your Figma design. There are two ways, offered side by side in the New-run form. Neither is a fallback — pick whichever suits you.

REST API tokenTalk-to-Figma
One-time setupgenerate a tokeninstall the bundled plugin
Per runnothingconnect plugin, paste channel
Figma desktop appnot neededrequired
Can readany file the token reachesthe file you have open
Best whenyou want runs to just workyou'd rather not make a token
Recommendation Use a REST token. It's less to remember per run, and the rotating channel is the single most common cause of failed runs.

Figma REST API token#

  1. In Figma: avatar → SettingsSecurity tab.
  2. Under Personal access tokens, click Generate new token.
  3. Name it QPD. File content only needs Read.
  4. Copy it immediately — Figma shows it once. It starts with figd_.
  5. In QPD's New-run form choose REST API and paste it.
It's your personal token It carries your Figma access. Don't paste it into shared docs or send it to teammates — each person should generate their own.

Talk-to-Figma plugin#

Reads the design straight out of your open Figma file — no token. The plugin ships with QPD in vendor/figma-plugin/.

No Node, no bun The relay QPD needs on ws://localhost:3055 is built in and starts automatically. You only install the plugin.

Install (once)

  1. Open the Figma desktop app — browser Figma can't load local plugins.
  2. Menu → PluginsDevelopmentImport plugin from manifest…
  3. Choose vendor/figma-plugin/manifest.json inside your QPD folder.
  4. It appears under Plugins → Development as Claude MCP Plugin.

Use on each run

  1. Open the Figma file you want QA'd.
  2. Plugins → Development → Claude MCP Plugin.
  3. Leave the port at 3055Connect.
  4. Copy the channel it shows (e.g. tdg2yco1).
  5. In the New-run form choose Talk-to-Figma and paste the channel.
The channel changes every reconnect This is the most common cause of a failed Figma step. If a run can't reach Figma, reopen the plugin and copy the current channel — an old one silently fails.

Your first run#

  1. Open localhost:8791 and click + New run (top right).
  2. Paste your Figma URL (it must contain node-id=…) and the Pretest URL.
  3. Choose your Figma access and fill in the token or channel.
  4. Click Run QA. Phase 1 captures both sides — about a minute. You'll see friendly progress.
  5. You land on the Pair page with thumbnails of every section from both sides.
  6. Click one Figma thumbnail and one live thumbnail → they preview on the right → Add pair. Repeat for each section you care about. (Or set a scale anchor and let QPD suggest them.)
  7. Click Run all not-run. Each comparison goes off to the Phase 2 AI review.
  8. When they finish, click Open on a pair — or Open AI comparisons.
  9. Work through the findings: Confirm the real ones, Reject false positives, fix any boxes.
  10. Use Copy or Download to hand the annotated image to a developer.
Start small On your very first run, pair a single obvious section like the hero. You'll see the whole loop in a couple of minutes instead of waiting on ten comparisons.

The New-run form#

Opened by + New run. Close it with ×, Esc, or a click outside.

FieldWhat it does
Input modeFigma + Pretest — paste both URLs. Asana task — paste an Asana task URL and QPD pulls the Figma link and target page out of it.
Figma URLMust include node-id=…, which identifies the frame. Copy it via right-click → Copy link on the frame. Optional — leave it blank to capture the live side only (see Capture one side or both).
Pretest URLThe built page to compare against. Optional — leave it blank to capture the Figma side only. Fill in whichever side(s) you have; QPD captures exactly those.
VariantWhich A/B variant to activate on the pretest page before capturing, via the Growthbook QA panel. Defaults to B. Choose skip if the page has no variant switcher. If the switcher can't be found the run aborts rather than QA the wrong variant.
ConcurrencyHow many comparisons run at once (default 4). Lower it if your machine struggles.
Comparison flowPage sections is the existing default. Exact Figma target exports only the node named by the Figma link, uses the optimized Method 2 visual matcher (including opening collapsed controls), and asks you to confirm one of the top three candidates before comparison.
Run throughLeave on Phase 1 only — the app always captures first, then you build comparisons on the Pair page.
Target sectionsOptional comma-separated names to capture only some sections. Blank captures everything.
Figma accessREST API token or Talk-to-Figma channel. If you pick Talk-to-Figma you must enter a channel ID — the run is blocked until you do.
Pre-capture actionsOptional. Things to do on the live page before it's captured — open an FAQ, pick a size, open a popup. Three modes; see Pre-capture actions.
Cart contentsOptional. Populate the cart before capture, to QA a cart page. Empty by default; see Cart simulation.
Dry runResolves the plan and stops. For checking your URLs without spending time or usage.

Pair page#

Where you pair captured Figma and live sections, then run them through AI comparison.

Left — the section picker

Two thumbnail columns, Figma and live. Click one on each side; they preview on the right. Collapse the whole panel with ◀ Sections (remembered between visits).

Right — preview and pairs

The current selection shows full size, then every pair you've built. Each row has:

ControlWhat it does
Add pairTurns the current selection into a comparison.
Set as scale anchorSee below.
OpenOpens AI comparisons for that pair (only once it has results).
Run / Re-runCompares it. Re-running creates a new version.
EditRe-pick either side. If it already ran it becomes needs re-run.
RemoveDeletes the comparison and all its versions. Asks first.
checkbox + Run selectedRuns several at once.
Run all not-runRuns everything still draft or stale.
Open AI comparisonsOpens AI comparisons with every finished pair included.

Status badges

BadgeMeaning
not runBuilt but never compared.
running…In progress. Controls are disabled.
doneFinished — results ready.
failedErrored. Check the log and run it again.
needs re-runYou changed the pairing after it ran; old results remain viewable.

Timings

QPD records how long things took and keeps it. Each pair shows its Figma and live capture duration plus the latest AI comparison duration.

Scale anchor & auto-pairing#

Entirely optional. Skip it and nothing changes. It does two jobs.

Setting it

Pick one Figma + one live section you know match, then click Set as scale anchor.

Job 1 — records the export scale

Figma might export at 1920px wide while the live page is captured at 3840px (retina) — a 2× difference. Told this, QPD passes it to the Phase 2 AI review so it stops reporting the export scale as a size bug ("font is 32px in Figma but 64px live"). Width only — live height reflows and isn't reliable.

Job 2 — suggests the remaining pairs

Both lists run top to bottom, so anchoring one pair lets QPD line up the rest. It compares section names, shapes and position down the page, and is allowed to skip on either side — real pages have sections the design doesn't (nav, footer) and vice versa.

Suggestions appear with confidence scores. Nothing is added until you say so — low-confidence guesses arrive unticked. Review, adjust, then Add selected.

Why confidence is sometimes low Figma layers are often named Frame 1597880521 or v2, which say nothing. QPD detects those and leans on shape and position instead. And when both names are meaningful but disagree, confidence drops on purpose — that's evidence against the pair.

Clear the anchor any time with Clear. It only affects future runs.

Running a comparison#

You'll see a progress panel with a plain-language step — Comparing your design against the live page… — and four stages: Capture → Compare → Verify → Locate. Expand Show technical details for the raw log.

Expect roughly 3–10 minutes per comparison, depending on section size. Several run at once up to your concurrency setting.

If a run dies If the server is stopped or a job crashes, comparisons left running… are automatically flipped to failed next time the app loads, so nothing hangs forever.

AI comparisons#

Where you work through what QPD found. Two panels: pick which sections to show on the left, read and triage findings on the right.

Reading a finding card

PartMeaning
Number badgeMatches the numbered box on the live image, and is the same colour.
Coloured railSeverity at a glance.
Figma → Live blockWhat the design intends (muted) versus what's actually built (emphasised).
FixThe suggested change, in green.
COPY tagA text/copy difference, found deterministically — these are exact, not judgement.
Figma pos tagThe element is missing on live, so the box shows where it should be, using the Figma position. Drawn dashed.

Severity

  • Critical — broken or badly wrong; must fix before launch.
  • Major — clearly visible and wrong.
  • Minor — noticeable to a careful eye.
  • Cosmetic — a pixel or two.

Triage

Confirm marks a finding real; Reject marks it a false positive. Click the active one again to clear it. Confirmed cards get a green border; rejected ones dim and strike through, so a long list stays scannable. Decisions save immediately.

Showing only some sections

Tick sections in the left panel, or use All / None. Collapse the panel with ◀ Sections.

Unsaved edits Changing which sections are shown reloads findings. If you have unsaved annotation edits, QPD warns you first — click Save before switching.

Annotations & boxes#

Every finding gets a numbered box on the live screenshot. Boxes are fully editable — QPD's placement is good but not perfect, and a correct box is what makes the report useful to a developer.

ActionHow
Find a finding's boxClick anywhere on its card — the box highlights and scrolls into view.
Find a box's findingRight-click the box → a popover with the details → Open full card.
Move a boxDrag it.
Resize a boxDrag the white corner handle.
Redraw one boxThe box icon on the card, then drag a new rectangle.
Add your own annotation+ Add box in the section header, then drag. It's marked user.
Edit the textThe pencil icon — label, severity, Figma, Live and Fix all editable.
DeleteThe trash icon.
Undo / redoToolbar buttons or shortcuts.
SaveSave in the toolbar. A dot means unsaved changes.
Save your edits Box moves and text edits are not saved automatically. Confirm/Reject decisions are. Hit Save before leaving AI comparisons.

Zoom & pan

Both images of a pair share one zoom, so they stay aligned as you inspect detail.

  • / + / buttons — out, in, reset.
  • /Ctrl + scroll, or pinch on a trackpad — zoom at the cursor.
  • Two-finger scroll while zoomed — pan.

Boxes scale with the image, so you can zoom in and place them precisely.

Versions#

Re-running a comparison creates v2, v3 and so on. Earlier versions are never overwritten.

When a comparison has more than one, a version dropdown appears in its AI comparison header showing each version's finding count and duration. Your edits and decisions belong to the version they were made on.

Why re-run The models aren't perfectly deterministic. If a run looks thin, re-running often surfaces more — and you can compare the two versions rather than losing the first.

Export & sharing#

Each section header has an export dropdown plus Copy and Download:

  • Annotated live — the live screenshot with every numbered box drawn on.
  • Side-by-side — Figma on the left, annotated live on the right, labelled.

Copy puts the image on your clipboard for pasting into Slack, Asana or a doc. Download saves a PNG. Both render at full resolution and follow your current theme.

File → Share results downloads one self-contained interactive HTML file. It includes the Figma and live images, annotation boxes, current finding edits, Confirm/Reject decisions, and every successful comparison version. The file follows the current Hide copy changes setting. Source URLs, capture HTML, DOM data, logs, prompts, and tokens are never included or uploaded.

Edits stay with the exported review The downloaded HTML works offline. Reviewers can edit, add, move, resize, and delete annotations; confirm or reject findings; hide or show copy; use undo/redo; save in their browser; and download another HTML containing their edits. Those changes do not sync back to the original QPD run.
Handing off The side-by-side composite plus your confirmed findings is usually all a developer needs — the numbers on the boxes line up with the numbered cards.

Capture one side or both#

You don't have to capture both sides every time. QPD looks at which URLs you filled in and captures exactly those:

You fill inQPD captures
Figma URL onlyThe Figma side only.
Pretest URL onlyThe live side only.
BothBoth sides.

There's no "capture both" switch to set — it's inferred. Useful when you only need to re-capture one side, e.g. the design changed but the page didn't.

Phase 2 still needs both A comparison pairs a Figma section with a live section, so to actually compare you need both sides captured. One-side capture is for building up a run in two goes, or for eyeballing one side.

Pre-capture actions#

By default QPD captures whatever is on the page when it loads. Anything hidden behind a click — a collapsed FAQ, a size selector, a "See what's inside" panel, a popup — never got captured, so it was never checked against Figma.

Pre-capture actions fix that: in the New-run form, type what to do on the live page before it's captured. There are three modes.

ModeWhat you writeHow it runs
Natural language defaultPlain English, e.g. Select the Queen size, dismiss any popup, and expand all FAQs.An agent looks at the page and decides what to click, step by step. Most flexible, slowest (it thinks between steps).
StructuredOne exact step per line (see below).Runs deterministically, no AI. Fastest and most predictable — use it when you know the exact steps.
HybridPlain English.One AI call converts your English into structured steps, then runs them deterministically. A middle ground.

Structured steps

One per line. Supported:

StepDoes
click QueenClick the element whose text is "Queen".
click css=.faq-toggleClick by CSS selector.
fill #email a@b.comType into a field.
select #size QueenChoose a dropdown option.
press EscapePress a key.
dismissClose a popup / overlay.
scroll 800Scroll down 800px.
wait 500Wait 500ms for things to settle.
If a click doesn't "take" QPD falls back through several ways of clicking (including a direct DOM click) before giving up, so buttons that are technically off-screen or covered still work. In natural-language mode, if the agent repeats the same action that isn't working, it stops instead of looping.
Also on re-capture The same three modes appear in the Re-capture live page modal, so a new capture version can run its own actions. The field pre-fills with whatever the current live capture used; blank captures the page as-is. Applies to that one re-capture only.

Capturing popups#

Normally QPD hides popups, cookie banners and chat widgets so they don't cover the page. But if your pre-action deliberately opens a popup (e.g. an "Upgrade to Royal for FREE" upsell), that popup is the thing you want to QA — so QPD keeps it open and captures the popup itself as its own section.

  • The popup shows up as a section named Popup: …, listed first.
  • The page behind it is not what gets captured for that section — the dialog is.
  • Cookie / consent / chat / region-switcher noise is still stripped as usual.

You don't turn this on — just open a popup in your pre-actions and it's captured.

Cart simulation#

To QA a cart page you need products in the cart. Doing that by hand — walking through PDPs and adding items — is slow and flaky. QPD populates the cart instantly instead, server-side, before it captures.

How to use it

  1. In the New-run form, set Cart contents to Add products (default is Empty cart).
  2. Click Load products from this URL — QPD fetches the product list live from the site. No product list or external token needed. Works on pretest and preview links too.
  3. Pick a product, size (variant) and quantity, click Add. Repeat for as many items as you want — one, many, or a mix.
  4. Run as normal. The cart page is captured with those items already in it.
Empty by default Leave Cart contents on Empty cart and nothing changes — the page is captured as-is. Cart simulation only kicks in when you add at least one product.

Runs survive a restart#

A comparison run no longer depends on the server staying up. Each run writes its progress to a log file and runs detached from the server process.

  • If the server restarts — or you restart it — mid-run, it reconnects to the still-running job when it comes back and keeps streaming progress.
  • No more timers frozen at some number (e.g. "89.3s") and no lost runs.
  • Shutting the server down does not kill runs in flight.

Nothing to configure — this is just how runs work now.

Keyboard shortcuts#

ActionmacOSWindowsWhere
Undo+ZCtrl+ZReview
Redo++ZCtrl+YReview
Zoom in / out+scrollCtrl+scrollany image pair
Close the New-run formEscEscmodal
Typing is protected Undo/redo are ignored while you're typing in a text field, so your browser's normal text undo keeps working.

Full UI reference#

Every control, including the small ones.

Header (always visible)

ControlWhat it does
Puffy logo and QPD nameReturns to Home from anywhere in QPD.
FileStart or open runs, manage runs and known issues, save and sync AI-comparison edits, share results, or open Settings.
EditShows only actions that apply to the current page and selection. On AI comparisons it can create a new combined Asana task; QPD keeps the newest task link.
RunAvailable on the Pair page for selected, pending, or all pairs. Hidden in AI comparisons.
ViewReplaces Run in AI comparisons. Shows or hides finding types and changes issue grouping.
DocsOpens this guide in a new tab.
Review all results / Pair pageOne context-aware button switches between pairing and AI comparisons.
Create Asana task / Open Asana taskOn AI comparisons, creates one combined task beside Pair page. After creation, the button becomes the saved task link.
Status badgeThe current run's state.

Behaviours worth knowing

  • Startup is configurable — open Home, the latest run, or a pre-filled New Run form.
  • Deep linkslocalhost:8791/?run=20260723_093719 opens a specific run.
  • Panel state is remembered — collapsed sections panel and your theme persist.
  • Duplicate pairs are refused — the exact same Figma+live combination can't be added twice.
  • Running comparisons lock their controls so you can't edit or remove mid-flight.
  • Boxes are colour-coded by severity; copy findings are purple; Figma-positioned boxes are dashed.
  • Empty descriptions show — not specified — rather than collapsing, so the comparison stays readable.
  • Progress is plain language, with the raw log tucked behind a disclosure.

Files & folders#

Runs live in ~/Documents/QPD/runs by default. If you have an older install with ~/Desktop/Puffy/QA-Pretest, QPD keeps using it so nothing goes missing. Check with python3 bridge/paths.py.

run_20260723_093719/
├─ run.json              run name + every capture version + which is active
├─ comparisons.json      every comparison, version, and the scale anchors
├─ state.json            run status + Phase 1 timing
├─ params.json           what you entered in the New-run form
├─ captures/
│  ├─ f1/                Figma PNGs + node trees + text  (capture 1)
│  ├─ l1/                live PNGs + DOM data + computed styles
│  └─ l2/                a second live capture, after you changed the link
└─ comparisons/
   └─ c1/v1/             one comparison, one version
      ├─ findings.json          final findings
      ├─ findings_edited.json   your edits (once you Save)
      ├─ ui_decisions.json      confirm / reject
      └─ text_diff.json         deterministic copy diff

Changing a link mid-run. Use Re-capture in the run bar to fetch a Figma or pretest link again — including a different link. That adds a new capture version rather than overwriting the old one, so every comparison you already ran keeps its exact images and results. Switch versions with the selectors in the run bar; each one keeps its own pairs and scale anchor. When you move to a new version, Carry them here… re-creates your existing pairs against it — matched by section identity, then name, then appearance, and always shown for you to confirm first.

Backing up Copy the whole run_… folder — it's self-contained. Nothing outside it is needed to view results.

Configuration#

Environment variables, all optional. Set them before starting the server.

VariableDefaultWhat it does
QPD_RUNS_DIR~/Documents/QPD/runsWhere runs are stored.
QPD_SKILL_DIR~/.claude/skills/qa-pretest-designWhere the helper scripts live.
QPD_PORT8791Web UI port.
QPD_BRIDGE_PORT35848Internal Claude bridge port.
QPD_FIGMA_RELAY_PORT3055Figma plugin relay. Change it in the plugin too.
QPD_PHASE2_MODEcombinedPhase 2 model path. combined = one high-thinking Opus pass (default). Any other value (e.g. split) = the older two-model finder→verifier path. See How it works.
QPD_PORT=9000 QPD_RUNS_DIR=~/qpd-runs python3 server/app_server.py

Troubleshooting#

The page won't load at localhost:8791

Check the server is actually running and watch its terminal for errors. If the port is taken, start it on another: QPD_PORT=9000 python3 server/app_server.py.

Every comparison fails immediately

Almost always Claude Code. Run claude --version, then claude to confirm you're logged in. See Claude Code login.

The Figma step fails or times out

  • Talk-to-Figma — the channel almost certainly changed. Reopen the plugin, copy the current one. Confirm it says connected, and that you're in the Figma desktop app.
  • REST — check the token hasn't expired and can reach that file. It must start with figd_.
  • Either — the Figma URL must contain node-id=….

"Variant switcher not found"

QPD aborts rather than QA the wrong variant. Either the pretest URL has no Growthbook QA panel, or the page changed. Set Variant to skip if the page genuinely has no switcher.

The live capture is blank or missing sections

Usually a page that lazy-loads slowly, or a popup that didn't get dismissed. Re-run — captures aren't deterministic on heavy pages. Check the URL loads normally in a browser.

Boxes are in the wrong place

Some findings — especially things missing on live — have no element to anchor to, so placement is a best guess. Drag the box, or redraw it with the box icon, then Save. If every box is off, set a scale anchor.

Findings look thin

Re-run it — you'll get a new version and can compare. Also make sure the pair is actually right: comparing a hero against a footer produces confused output.

A comparison is stuck on "running…"

If the job died, QPD flips it to failed next time the app loads. Reload the page, then run it again.

Nothing happens when I click Copy

Clipboard image writing needs a secure context. localhost qualifies, but some browsers still block it — use Download instead.

Windows: "python is not recognized"

Python isn't on your PATH. Re-run the installer, choose Modify, and enable Add Python to PATH.

Windows: "claude is not recognized"

Close and reopen PowerShell after installing Claude Code — the PATH change only applies to new terminals.

FAQ#

Does QPD change my Figma file or the live page?

No. It only reads. Everything it writes goes to your local runs folder.

Whose Claude usage does this consume?

Yours — QPD drives the Claude Code CLI logged in on your machine.

Can I use QPD offline?

On the website, you can open your run library and review completed runs already backed up to Drive without connecting your computer. Connect QPD Agent only when you need to capture, run AI, edit, delete, save/sync, or create an Asana task. A new capture still needs internet access to Figma and the live page, and the selected AI CLI must be available on the connected computer.

How accurate is it?

Copy differences are computed deterministically and are exact. Visual findings are model judgement — the review pass self-verifies and removes most false positives, but you are the final check. That's what Confirm/Reject is for.

Why two sizes for the same element?

Probably the export scale, not a bug. Set a scale anchor.

Can two people share one run folder?

Not simultaneously — comparisons.json would conflict. Copy a finished run folder to share results.

Can I delete old runs?

Yes, delete the run_… folder. Nothing else references it.

Why does re-running create a version instead of replacing?

So you never lose a good result to a worse re-run, and can compare the two.

Do I need Node, npm or bun?

No. The Figma relay is built into QPD in Python.

↑ Back to top