Observify

User Tests · clipify
← all user tests
USER TEST PLAN

clipify

Derived from: commit adf8984 (dev, worktree _repro/dev), 2026-08-07, against the built

0/74 checked · 0 pass · 0 fail

Flows

check each step and final check
F1

Build and load the fixed extension

Proves the extension every other flow tests is the real, current dev build, correctly installed.

Persona / precondition: a terminal at ~/Projects/clipify/_repro/dev; Chrome open with Developer mode available.

0/5

Run bun install && bun run build. Expect: exits 0; dist/ contains manifest.json,
background.js, content.js, panel.js, panel.html, demo.js, demo.html, options.js,
options.html, icons/16.png, icons/48.png, icons/128.png. ⚠ Verified without a browser
in this session: the worktree's git status is clean and every dist/*.js file already
postdates every src/**/*.ts file, and dist/content.js already contains the literal string
captured only the (FID-T008's warning) — so a fresh build should be byte-for-byte equivalent
to what's already on disk.

Open chrome://extensions, enable Developer mode (top-right toggle), click Load
unpacked
, select _repro/dev/dist. Expect: a card titled Clipify, version 0.1.0,
description Select a text region on any page and save it as a Markdown file. — exact text
from manifest.json.

Hover Clipify's new toolbar icon (pin it via the puzzle-piece menu if hidden). Expect:
tooltip reads exactly Clipify — click to start clipping.

On the card, click Details. Expect: permissions listed are exactly activeTab,
scripting, downloads, storage, sidePanel — no others (G-MIN-PERMISSIONS's statically
guarded allowlist).

Final check: click Chrome's own side-panel toolbar icon (separate from the puzzle piece,
usually near the profile avatar) — Clipify appears in its list of available panels.

F2

First-run cold-start tutorial

Proves the zero-context bar: a stranger's very first click teaches the whole workflow, not just the current page.

Persona / precondition: a genuinely fresh Chrome profile (or the seen-flag cleared as above) with F1's build freshly loaded, on any open tab.

0/8

Click Clipify's toolbar icon for the very first time. Expect: instead of activating clip
mode on your current page, a new tab opens titled Clipify — Try it, heading Welcome to
Clipify
, two demo paragraphs, and a dark step panel top-right.

Read the step panel. Expect: title Start clipping, body Click the Clipify icon in your
toolbar to start clipping this page.

Click Clipify's toolbar icon again, on this new demo tab. Expect: clip mode activates on
the demo page and the step panel advances to title Pick two things to save, body Click on
two paragraphs below to select them — selected text gets a green outline.

Click both demo paragraphs. Expect: each gets a green outline, and the step panel advances
to title Open your basket, body Click the Clipify side panel icon in your toolbar to see
what you selected.

Open Clipify's side panel. Expect: two cards appear, and the step panel advances to title
Save it, body Click Save in the side panel to download your selections as one Markdown
file.

Click the panel's Save button. Expect: a .md file lands in Downloads, and the step
panel reaches title You're all set!, body That is the whole workflow — select, open the
panel, save. Try it on any page.

Click Clipify's toolbar icon on any ordinary page. Expect: clip mode activates immediately
— no redirect to the demo page this time (the seen-flag is now set).

Final check: open the side panel's footer, or the options page (chrome://extensions
Clipify → DetailsExtension options) — both show a Replay the tutorial link; clicking
it reopens demo.html at Start clipping without disturbing anything clipped since.

F3

Core clip: select one region, save with Enter

Proves the base loop: click a region, see it marked, save a real file with real content.

Persona / precondition: F1's build loaded, tutorial already dismissed. Fixtures served locally; open http://127.0.0.1:8342/article.html.

0/6

Click the Clipify icon. Expect: a dark hint box top-right reads exactly Clipify: click
regions to add/remove them from your clip — Enter (or the counter) saves, Esc finishes
, and a
counter bottom-right reads Clipify: 0 selected.

Hover the first paragraph (This paragraph has bold text…). Expect: a blue outline
appears around exactly that <p>, not its parent <article> or the whole page.

Click it. Expect: the outline turns green and the counter updates to Clipify: 1 selected
— click to save
.

Press Enter. Expect: a green notice reads Clipify: saving 1 region(s) as Markdown…,
the clip-mode UI disappears, and a .md file lands in Downloads.

Open the saved file. Expect: it starts # Ordinary Article, a > Source: line with the
fixture's URL, a > Saved: line with a real ISO-8601 timestamp, then the paragraph rendered as
This paragraph has **bold text**, _italic text_, and a [reference link](https://example.com/reference)
inside it.
— nothing from the second paragraph, the blockquote, the code block, or the list.

Final check: reload the page fresh and click the SAME paragraph twice in one session (click,
then click again before saving) — the second click removes it (outline clears, counter returns to
Clipify: 0 selected), proving toggle-off works, not just toggle-on.

F4

Esc mid-session: Save or Discard

Proves the exit-with-unsaved-work guard doesn't lose or silently drop a clip.

Persona / precondition: F1's build loaded; article.html open.

0/5

Enter clip mode, click one region (the blockquote). Expect: counter reads Clipify: 1
selected — click to save
.

Press Esc. Expect: a dark modal centers on the page reading exactly Clipify: save 1
selected region before exiting?
with a green Save button and a red Discard button.

Click Discard. Expect: the modal closes, clip mode exits, and no new file appears in
Downloads.

Re-enter clip mode, select 2 regions, press Esc, then click Save. Expect: the modal
now reads Clipify: save 2 selected regions before exiting? (plural), and Save produces a
.md with both regions, same as a counter-click save.

Final check: enter clip mode with zero regions selected and press Esc — clip mode exits
immediately with no modal at all (nothing to save).

F5

Multi-select across page areas (cross-panel accumulation)

Proves the primary multi-select scenario Dominick specified: sidebar + main content + footer into one document.

Persona / precondition: F1's build loaded; open http://127.0.0.1:8342/multi-panel.html (has a sidebar, a main article, and a footer with distinct content).

0/4

Enter clip mode. Click the sidebar note (A sidebar note pointing to related reading
elsewhere on the site.
), then the footer blurb (A footer widget blurb with a widget link
inside it.
). Expect: counter reads Clipify: 2 selected — click to save, and both regions
show green outlines simultaneously — selection isn't limited to one area of the page.

Click the counter. Expect: the confirm notice appears and a .md saves.

Open the saved file. Expect: header # Multi-Panel Page, then the sidebar sentence, then a
bare --- rule, then A footer widget blurb with a [widget link](https://example.com/widget)
inside it.
— in click order (sidebar first). Repeat clicking footer FIRST, then sidebar, and
confirm the saved order flips to match your click order, not DOM order.

Final check: with clip mode active and 0 regions selected, click the counter — nothing happens
(no notice, no download) since an empty basket has nothing to save.

F6

Field-bug regression check: a container that merely holds an editor

Proves the fix for CLIPIFY-CAPTURE-WRONG-REGION (found in Dominick's own dogfood, FID-T007) still holds: identity, not containment.

Persona / precondition: F1's build loaded; open http://127.0.0.1:8342/capture-contract.html — its #card box deliberately holds a plain paragraph, a filled-in textarea, AND a fake code editor together, the exact shape that broke capture in the field.

0/6

Enter clip mode, hover the card's paragraph (PROSE-START the sentence the user actually wants
to clip…
). Expect: ONLY that paragraph highlights blue — the surrounding #card box does
NOT highlight.

Click it, then save. Expect: the saved file contains PROSE-START the sentence the user
actually wants to clip.
and does NOT contain CODE-LINE-1 anywhere.

Re-enter clip mode. Click the card's own empty padding (inside #card, outside the paragraph,
textarea, and code area). Expect: the WHOLE #card box highlights and selects.

Save. Expect: the file contains all three — PROSE-START the sentence the user actually
wants to clip.
, TYPED-START the explanation the user wrote by hand. (the textarea's typed
value, not its blank original markup), and a fenced block:

CODE-LINE-1 def score(submission):
CODE-LINE-2 return sum(submission.milestones)

Re-enter clip mode and click directly on a code line inside the fake editor. Expect: the
same fenced block appears in the saved file, and the file does NOT contain PROSE-START
clicking the editor captures only the editor.

Final check: click #region-b (a long paragraph of 70 numbered sentences) and save — the file
contains BRAVO-START, BRAVO-END, and all 70 BRAVO-BODY-0XX sentences intact, with nothing
from the neighboring paragraphs — proving a long region isn't truncated by the identity fix.

F7

Virtualized editor: honest partial-capture warning

Proves FID-T008: a long code file that's actually virtualized in the DOM says so, instead of silently passing off a slice as the whole file.

Persona / precondition: F1's build loaded; open http://127.0.0.1:8342/codemirror-virtual.html (a real CodeMirror 6 instance holding a 400-line file in a 150px-tall, scrollable viewport).

0/4

Wait for the editor to render, then enter clip mode and click inside it (#clip-target).
Expect: it highlights and selects as one region, same as any other editor.

Save, then open the file. Expect: it contains const line000 (what was actually on
screen) and does NOT contain LAST_LINE_SENTINEL (the file's real last line, never rendered).

Check the end of the fenced code block. Expect: immediately after the closing fence, a
blockquote reading ⚠️ Clipify captured only the N lines this editor had rendered on screen —
it virtualizes, so the rest of the file was not in the page to capture. Scroll the editor and
clip again for another slice, or copy from the source.
(N = however many code lines were
actually visible; if you count exactly 1 the wording is 1 line singular, not 1 lines).

Final check: repeat on http://127.0.0.1:8342/codemirror.html (a short, fully-visible editor,
NOT virtualized) — the saved file contains function greet(name) in full and carries NO
captured only warning at all, proving the warning is conditional on being cut off, not shown on
every editor.

F8

Form-control state capture

Proves md-fidelity's form handling: what's actually filled in right now, not the page's original blank markup.

Persona / precondition: F1's build loaded; a fresh load of http://127.0.0.1:8342/form.html (all controls start blank/unchecked).

0/5

Before entering clip mode: select the Yes radio, check Subscribe to the newsletter, choose
Canada from the country select, and type Great service, thank you! into Comments.
Expect: the page reflects your inputs normally.

Enter clip mode, click the #clip-target div wrapping the whole form. Expect: the whole
form section highlights and selects as one region.

Save and open the file. Expect: the body reads exactly, in order: Would you recommend us?
— **Yes**
, a blank line, - [x] Subscribe to the newsletter, a blank line, Country —
**Canada**
, a blank line, Comments:, a blank line, > Great service, thank you!

Reload the page fresh (clears your inputs), select Canada again, then enter clip mode and
click the <select> element ALONE (not the surrounding div). Expect (documented limit, not
a bug):
the saved file reads (unlabeled) — **Canada** — label resolution can't see the
<label for="country"> living outside the clicked fragment; clip the enclosing section (step 2)
if you need the label attached.

Final check: with nothing checked/selected/typed (a truly blank form), clip the whole
#clip-target — every control still renders (an unchecked box as - [ ], neither radio bolded,
an empty textarea's blockquote empty) and nothing crashes.

F9

Table capture: whole table and single cell

Proves GFM table output, and that per-cell clipping still works alongside it.

Persona / precondition: F1's build loaded; open http://127.0.0.1:8342/table.html.

0/5

Enter clip mode, click the table's caption (Monthly plans, which resolves up to the table).
Expect: the whole table highlights.

Save and open the file. Expect: a GitHub-flavored Markdown table, exactly:

| Plan | Price | Storage |
| --- | --- | --- |
| Free | $0 | 5 GB |
| Pro | $9/mo | 100 GB |

Re-enter clip mode, click the single cell containing Pro (the second row's Plan cell).
Expect: ONLY that cell highlights, not its row or the table.

Save. Expect: the file's body is just Pro — no table syntax, no surrounding row.

Final check: every column separator in step 2's table reads a plain --- (three hyphens)
regardless of alignment — column alignment is not preserved, by design (RUNBOOK.md).

F10

Side panel: reorder, remove, retitle, save

Proves the basket-management surface: preview, drag, remove, retitle all reflect in the saved document.

Persona / precondition: F1's build loaded; article.html open; side panel opened once already (F1's final check).

0/7

Enter clip mode and click, in order: the first paragraph, the blockquote, and the
Formatting Demo paragraph (3 regions). Open the side panel. Expect: the title input
defaults to Ordinary Article (the page title), a count line reads 3 regions in this
basket.
, and three cards appear in that order, each with a source-order number, a drag handle
a braille-dots grip handle, and a an X remove control remove button.

Press and hold the blockquote card's a braille-dots grip handle handle, drag it above the first card, release.
Expect: the list reorders live — the blockquote is now card 1.

Click the Formatting Demo card's an X remove control. Expect: it disappears immediately, the count line
updates to 2 regions in this basket., AND back on the page that paragraph's green outline is
gone too (both sides share one store).

Click the title field, clear it, type My Test Basket, then click elsewhere. Expect: the
field keeps the new value (no revert).

Click the green Save button. Expect: its text becomes Saving… and is briefly
disabled, then a .md lands in Downloads and the panel returns to No regions clipped yet.
with Save disabled again.

Open the saved file. Expect: heading # My Test Basket (your custom title, not the page
title), body order blockquote-then-first-paragraph (post-drag order), and NOTHING from the
formatting-demo paragraph (it was removed).

Final check: close the side panel entirely, select 1 new region, and reopen the panel — it
shows exactly that region, proving the basket survives panel close/reopen (chrome.storage.session,
not the panel's own page state).

F11

Options page: filename template and header toggle

Proves the one settings surface: validated input, a persisted preference, and effect on the very next save.

Persona / precondition: F1's build loaded; options page open (chrome://extensions → Clipify → DetailsExtension options).

0/6

Look at the Filename field. Expect: pre-filled {title}-{date}-{time} (the default), with
a hint below reading Use {title}, {date}, and {time} to build the saved filename.

Clear it, type clip-{unknown}, then click elsewhere (blur). Expect: an inline error
appears reading exactly That template is not valid — use only {title}, {date}, {time}, and
keep at least one.
, and the invalid value is never saved (reload the page — the field still
shows the last VALID template, not clip-{unknown}).

Clear it, type clip-{date}, blur. Expect: no error, and reloading the options page shows
clip-{date} persisted.

Go to article.html, clip one region, save. Expect: the downloaded filename matches
clip-YYYYMMDD.md with today's real date, not the old {title}-{date}-{time} scheme.

Back on options, uncheck Include a title/source/saved-at header. Clip and save another
region. Expect: the saved file has NO # title line and NO > Source:/> Saved: lines —
just the region's body text.

Final check: re-check the header checkbox and reset the filename template to
{title}-{date}-{time} — the very next save returns to the original heading/source/saved-at
format, proving the toggle isn't one-way.

F12

KNOWN DEFECT: basket desyncs when you navigate mid-session (UI-T006)

Exposes an open, unfixed ticket: the shared basket is keyed globally, not per-tab or per-page, so navigating mid-session can silently save a stranger's region alongside — or instead of — the one the on-page counter claims.

Persona / precondition: F1's build loaded; two fixture pages reachable in ONE tab, e.g. http://127.0.0.1:8342/article.html and http://127.0.0.1:8342/table.html.

0/5

On article.html, enter clip mode and click one region (the first paragraph). Expect:
counter reads Clipify: 1 selected — click to save.

In the SAME tab, navigate to table.html (type the URL or click a link) WITHOUT pressing
Enter/Esc first — this destroys the old content-script instance without it ever running its
own cleanup. On the new page, click the Clipify icon to enter clip mode fresh. Expect (the
correct behavior — pick one, per the open design decision recorded in _handoff/clipify.md:
a fresh per-tab basket, or an honest reflection of a basket that now spans pages):
the
counter should accurately describe what will actually be saved if you save right now.
Observed instead (UI-T006, CONFIRMED, unfixed on dev as of adf8984): the counter resets
to Clipify: 0 selected, but chrome.storage.session's single clipify/basket key still
silently holds article.html's region underneath it.

Click one region on table.html (e.g. the Pro cell). Expect: counter reads Clipify: 1
selected — click to save
, matching what will be saved. Currently fails: the counter still
says 1, but the shared store now holds 2 regions — the stale article.html one plus this new
one.

Click the counter to save. Expect: given the counter said 1 selected, the saved file
should contain exactly 1 region. Currently fails: the file contains BOTH the stale
article.html paragraph AND the table.html cell — a region from a page you already left,
that the on-page UI never told you was still pending.

Final check: this is filed as UI-T006 in LEDGER.md, RESUME.md, and _handoff/clipify.md — a
fix was attempted and REVERTED (clearing the store at session start broke co-live multi-tab
clipping worse than this bug). Do not attempt another fix without the human design decision those
files describe (per-tab keying vs. store-is-truth) being made first.

F13

KNOWN DEFECT (best-effort): panel/page write race can resurrect a deleted card (UI-T007)

Exposes a filed, pre-existing, timing-dependent defect: the panel mutates the basket store directly outside any serializer, so two concurrent writers can produce a lost update. This is a race — a few attempts may be needed, and it may not reproduce at all on a fast machine; that is itself useful information, not a failed test.

Persona / precondition: F1's build loaded; article.html open; side panel open with 2+ regions already clipped.

0/3

Position your mouse over an unselected page region and the an X remove control button of one panel card, so
both are one click away without moving the mouse. Expect: both are visible and clickable.

As close to simultaneously as you can manage (or with a second person, one hand on each), click
the panel's an X remove control (removing that card) and click the new page region (adding one) at the same
instant. Expect: the basket ends at exactly (previous count − 1 + 1) regions — both the
removal and the addition landed. Possible failure (UI-T007): the removed card reappears in
the panel a moment later — the addition's read-modify-write silently overwrote the removal
because both read the pre-removal state before either wrote back.

Final check: whatever the outcome, record it against RESUME.md/_handoff/clipify.md's
existing UI-T007 entry rather than filing a new ticket — this is the same defect those files
already describe (panel.ts calling removeRegion/reorderRegions directly, outside any
serializer).

F14

Promotion sanity: does `prod` still ship the original field bug?

Answers the question this whole handoff is blocked on: is promoting devprod still what fixes CLIPIFY-CAPTURE-WRONG-REGION for real users, and has anyone promoted since?

Persona / precondition: Dominick, deciding whether to promote. F1's dev build already loaded. repo/dist (the prod build currently on disk) needs no rebuild.

0/5

Confirm the commit repo/dist corresponds to: git -C ~/Projects/clipify/repo log --oneline
-1 prod
. Expect: af817d6 (unchanged since _handoff/clipify.md, dated 2026-07-19, last
confirmed it). ⚠ Verified in this session, read-only, no browser involved: repo/dist/content.js
(on disk since Jul 11 17:35) contains ZERO occurrences of the string captured only the (the
FID-T008 warning literal), while _repro/dev/dist/content.js (Jul 12 22:01) contains exactly
one — confirming repo/dist genuinely predates the fix, not just a stale label.

In chrome://extensions, click Load unpacked again and select
~/Projects/clipify/repo/dist. Expect: a second Clipify card appears (a different
generated ID) with its own toolbar icon — Chrome allows both unpacked builds loaded side by
side.

Open capture-contract.html fresh. Click the OLD build's toolbar icon, enter clip mode, and
click the card's prose paragraph (#card-prose) exactly as in F6 step 1. Expect (the
original bug, still shipping):
the WHOLE #card box highlights, not just the paragraph.

Save. Expect (the original bug, still shipping): the file contains CODE-LINE-1 and does
NOT contain PROSE-START the sentence the user actually wants to clip. — the exact inverse of
F6 step 2's fixed behavior, on the same page, same click.

Final check: remove the repo/dist copy from chrome://extensions when done (it exists only
for this comparison) — and if steps 3-4 confirmed the bug is still live in what a real install
currently loads, that is Dominick's own outstanding "promote devprod" step from
RESUME.md, not a new finding.

Not covered and why

  • The full bun run gate re-run (typecheck/lint/vitest/Playwright e2e — 329 unit + 21 e2e as

of adf8984): deliberately not triggered in this authoring session — LEDGER.md (2026-07-11)
records a real "SHARED-MACHINE VIOLATION" from an earlier automated Playwright run opening
Chromium windows unexpectedly on the human's live seat. Re-running the gate is a human (or an
explicitly-authorized headless) step, not something to trigger silently while writing a plan.

  • UI-T007's race (F13) is inherently non-deterministic — a clean pass here doesn't retire

the ticket, and failing to force it doesn't clear it either.

  • Cross-browser / non-Chrome: this is a Chrome MV3 extension (sidePanel,

chrome.storage.session access levels); Firefox/Safari are out of scope entirely, not merely
untested.

  • The original authenticated Snorkel page that surfaced CLIPIFY-CAPTURE-WRONG-REGION in the

field: auth-gated, per LEDGER.md. F6 reproduces the identical DOM shape on a public fixture
instead, as the project's own worker did.

  • Ace editor, custom in-house editors, iframes, and shadow-DOM content: explicitly out of

scope per RUNBOOK.md's "Known limits" — not specially detected; captures as plain markup or
not at all.

  • Two-tab, two-basket concurrent clipping (the scenario UI-T006's reverted fix attempt

broke): needs a second real browser window with clip mode live in both tabs at once; flagged as
a risk in _handoff/clipify.md, not scripted here as its own flow.

Results

FlowVerdictFailures → tickets
F1 build & load
F2 cold-start tutorial
F3 core clip
F4 Esc save/discard
F5 multi-select
F6 field-bug regression
F7 virtualized editor warning
F8 form capture
F9 table capture
F10 side panel
F11 options page
F12 basket desync (UI-T006)
F13 panel race (UI-T007)
F14 promotion sanity
updated just nownext 3m 00s