qapture2 0.7.9 → 0.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -92,7 +92,7 @@ working, and each new feature is off until someone turns it on.
92
92
  | | |
93
93
  |---|---|
94
94
  | **Screenshots frame the right thing** | Capture mode's own scroll lock used `overflow:hidden`, which **unstuck every `position: sticky` header** just before the screenshot was rendered — measured at 20px of wrongness in a 40px capture, now 0.0px. See [Screenshots: two engines](#screenshots-two-engines). |
95
- | **Pixel-exact capture (opt-in)** | A real photograph of the tab rather than a redraw — so canvas/WebGL, video, cross-origin iframes and exotic CSS all come out right. One prompt per session, Chromium desktop. |
95
+ | **Pixel-exact capture (opt-in)** | A real photograph rather than a redraw — so canvas/WebGL, video, cross-origin iframes and exotic CSS all come out right. Taken the instant capture opens, then the screen is handed straight back: one frame per capture, nothing recording in between. Desktop browsers. |
96
96
  | **Save straight to a folder** | Pick a QA folder once; every note is written to disk as it's saved, organised `Project / Campaign / notes + screenshots + REPORT.md`. See [Saving to a folder](#saving-to-a-folder). |
97
97
  | **Storage that explains itself** | A real usage meter, WebP screenshots (~10× smaller), a request to stop the browser evicting your data, and a "drop screenshots, keep findings" recovery valve. |
98
98
  | **A usable notes list** | Severity/status filter chips with counts, text search, and a "this page" toggle. |
@@ -372,23 +372,61 @@ canvas as a blank box, every time, in any tool built this way.
372
372
 
373
373
  ### `exact` — opt-in, pixel-for-pixel
374
374
 
375
- Uses the Screen Capture API to photograph **this tab's real composited
375
+ Uses the Screen Capture API to photograph **the viewport's real composited
376
376
  pixels**, then crops your rectangle out arithmetically. Nothing is
377
377
  re-rendered, so it cannot mis-frame, and everything above renders correctly
378
378
  because it was never re-drawn in the first place.
379
379
 
380
+ **The photograph is taken when you open capture mode, not when you finish
381
+ dragging.** That ordering is the whole design, and it decides three things at
382
+ once:
383
+
384
+ - **Nothing keeps recording.** The stream is acquired, one frame is taken, and
385
+ the track is stopped — about a third of a second — so the browser's sharing
386
+ indicator blinks and goes. Before 0.8 the stream was held for the entire
387
+ session so it would only prompt once, which on Safari (where the only option
388
+ is sharing a window or a whole screen) meant the indicator stayed lit and the
389
+ capture pipeline kept running behind every page. One prompt per capture is
390
+ the price of not recording you between them.
391
+ - **You crop what you can see.** The still is shown under the capture scrim
392
+ while you frame, so a page that animates cannot move between the moment you
393
+ point at something and the moment it is cropped — and a hover state, an open
394
+ dropdown or a tooltip survives being framed instead of being dismissed by the
395
+ mouse moving to start the drag.
396
+ - **Whether this shot is a photograph or a redraw is settled before you
397
+ start.** A still is either held or it is not. Previously the exact engine
398
+ reached for a frame at the end, from a stream that could quietly have died,
399
+ and fell through to `dom` without saying so — which is why the same click
400
+ could photograph one time and redraw the next.
401
+
402
+ Everything else:
403
+
380
404
  - **Turn it on** from the capture hint bar ("Pixel-exact shots") or Settings.
381
- - The browser asks once per session to share this tab. Nothing leaves the
382
- device the frames are cropped locally and never uploaded.
405
+ Turning it on does not prompt; the first capture does.
406
+ - **One prompt can cover several notes.** "Save + next" on the annotation card
407
+ (⌘/Ctrl + Shift + Enter) files the note and puts you back to framing the same
408
+ photograph. Worth knowing in **Safari**, where the per-site Screen Sharing
409
+ setting offers only *Ask* and *Deny* — there is no *Allow*, so every capture
410
+ prompts and the only lever is needing fewer captures. Three bugs on one
411
+ screen, one prompt.
412
+ - Nothing leaves the device — the frame is cropped locally and never uploaded.
383
413
  - The QA overlay is hidden for the captured frame, so the scrim, the selection
384
414
  outline and the annotation card never appear in the image.
385
- - **Chromium desktop only** (Chrome, Edge, Brave, Opera), because it depends
386
- on `preferCurrentTab`. Elsewhere the picker would let a tester share a
387
- screen or another window, whose pixels have nothing to do with our
388
- coordinate space so the option isn't offered, and if a shared surface
389
- somehow isn't this tab (wrong `displaySurface`, or a frame whose aspect
390
- ratio doesn't match the viewport), Qapture falls back to `dom` rather than
391
- returning a confidently wrong image.
415
+ - **Chromium** shares this tab directly (`preferCurrentTab`), so the frame *is*
416
+ the viewport measured at 0.0px error.
417
+ - **Safari and Firefox** have no tab capture, only a window or a screen. That
418
+ frame does contain the page, and Qapture finds it by *measuring*: it covers
419
+ the page with an opaque card carrying four known colours at four known
420
+ corners, photographs that, and solves for scale and origin from where the
421
+ colours landed. Toolbar height, pixel ratio and monitor layout all cancel
422
+ out. You see a dark flash for a fifth of a second. Two corners solve and the
423
+ other two verify — and a calibration that cannot be verified is **refused**,
424
+ falling back to `dom` rather than returning a confidently wrong image.
425
+ - The still belongs to the viewport it was taken in. Resize the window
426
+ mid-capture and the crop is refused for the same reason.
427
+ - Off-screen pixels do not exist in a photograph at any price, so a selection
428
+ that runs past the fold is trimmed to what was visible. The `dom` engine
429
+ re-renders and has no such limit.
392
430
 
393
431
  ---
394
432
 
@@ -828,7 +866,7 @@ The **capture hotkey** (default: `Shift+Alt+C`, i.e. `Option+Shift+C` on macOS)
828
866
  |---|---|---|---|
829
867
  | Capture, notes, export | ✅ | ✅ | ✅ |
830
868
  | `dom` screenshots (default) | ✅ | ✅ | ✅ |
831
- | Pixel-exact screenshots | ✅ opt-in | | — |
869
+ | Pixel-exact screenshots | ✅ opt-in (shares this tab) | opt-in (shares a window, located by measurement) | — |
832
870
  | Save to a folder, live | ✅ opt-in | — | — |
833
871
  | Save to a folder, as a zip | ✅ | ✅ | ✅ |
834
872
  | Storage meter | ✅ | ✅ (Safari reports coarse numbers) | ✅ |
package/dist/bin/init.cjs CHANGED
@@ -29,7 +29,7 @@ var fs6 = __toESM(require("fs"), 1);
29
29
  var process2 = __toESM(require("process"), 1);
30
30
 
31
31
  // src/artifacts/SKILL.md
32
- var SKILL_default = "---\nname: qapture\ndescription: >\n Activated when the user provides a `qa-notes-*.zip` file exported from\n Qapture. Reads the preamble block in `notes.md` (project context, stack, run\n commands, dev/test login credentials, red-zone coverage report, and\n invariants), flags any uncovered RED risk zones before acting, then\n triages every `## Point N` as a batch \u2014 clustering points that share a\n root cause via their runtime-context evidence \u2014 before orchestrating\n Sonnet subagents (one per point/cluster, parallelized across disjoint\n files) to reproduce each issue live, fix it, and self-verify. Finally\n grades coverage against the red zones and reports, including any adjacent\n improvements noticed along the way. Also activates on a single point\n pasted directly via Qapture's \"Copy as agent prompt\" (no ZIP, no preamble\n \u2014 just one point).\n\n **No AI is bundled in Qapture \u2014 YOU are the AI reading these artifacts.**\n Qapture is a 100% client-side, keyless, network-free capture widget.\ntriggers:\n - qa-notes-*.zip\n---\n\n# Qapture \u2014 Agent Skill\n\n> **Core principle:** Qapture ships zero AI. No model, no API keys, no network\n> calls. The CLI is a plain deterministic scaffolder. **You** \u2014 the coding agent\n> reading this skill \u2014 are the AI. The developer used Qapture to capture\n> annotated screenshots + notes from their live app; your job is to act on them.\n\n---\n\n## What Is Qapture?\n\nQapture is a drop-in in-browser widget (Shadow DOM, keyless, no telemetry).\nTesters annotate the live app: click an element or draw a region, add a note,\nand the widget captures a screenshot automatically. When done, they export a\n`qa-notes-*.zip`. That ZIP is the hand-off to you.\n\n---\n\n## Working Model \u2014 You're the Brain, Subagents Are the Muscle\n\nFor anything past a single trivial point, don't work through the ZIP\npoint-by-point yourself. Orchestrate:\n\n- **You cluster and plan; subagents reproduce, fix, and self-verify.** You\n read every point first (Step 3), decide what's really one bug wearing N\n reports, and spawn one subagent per point or cluster to do the actual work.\n You do the fixing yourself only for a single, obviously trivial point where\n spinning up a subagent is pure overhead.\n- **Every subagent is Sonnet, pinned explicitly on every single call.** Never\n let a spawned agent inherit whatever model you happen to be running as\n orchestrator \u2014 an inherited model silently drifts to whatever you're on,\n and that's the kind of thing nobody notices until the cost or the quality\n looks wrong in hindsight. Pin it every time, no exceptions.\n- **Effort is your judgment call per task, not a formula.** A one-line CSS\n fix gets low effort. A bug whose runtime-context evidence (below) doesn't\n cleanly explain the symptom gets high or max \u2014 you decide based on how\n ambiguous the root cause actually looks, case by case.\n- **Parallelize by file, not by point.** Points/clusters that land in\n disjoint files can run as concurrent subagents safely. Anything that lands\n in the *same* file goes to one agent, or runs serially \u2014 never two agents\n editing the same file at once. This is the actual failure mode to guard\n against, not an abstract \"be careful.\"\n- **Supervise by reality-checking, not re-reading.** Don't reread every\n subagent's full diff. Do: read exactly what each one's own report claims\n changed, at the file/location it names; always personally open and read\n the diff for anything touching a RED zone, no exceptions, regardless of\n what the subagent reports; and treat the project's own test/verify command\n \u2014 run by you, independently, after every subagent lands \u2014 as the actual\n gate. A subagent saying \"done\" is a claim, not a fact.\n\n**Creative suggestions are always welcome; creative *changes* are gated like\neverything else, by risk colour (see Risk Zone Reference below).** Noticing a\npattern, an adjacent bug, or a missing feature costs nothing to write down \u2014\nput it in a `### Suggestions` section in your final report (Step 5) and never\nsuppress it for being out of scope. Whether you're allowed to *implement* it\nwithout being asked depends on where it lands:\n\n| Zone | An idea beyond the literal reported point |\n| --- | --- |\n| **green** | Implement inline, no permission needed \u2014 \"fixed the label, also fixed 3 nearby with the same casing bug\" is fine. |\n| **amber** | Implement it, but call it out explicitly in the report \u2014 the developer should see at a glance what went beyond what was asked. |\n| **red** | Propose only. Write it down, never touch the code. Same rule as everything else in a red zone: no silent business-logic decisions. |\n\nThat keeps the upside of a genuinely observant agent \u2014 catching the adjacent\nbug, proposing the missing feature \u2014 without that same latitude becoming the\nmechanism for quietly rewriting a payment flow nobody asked you to touch.\n\n---\n\n## ZIP Layout\n\n```\nqa-notes-<timestamp>.zip\n\u251C\u2500\u2500 notes.md \u2190 ALWAYS read this first (see Step 1)\n\u2514\u2500\u2500 screenshots/\n \u251C\u2500\u2500 point-1.webp\n \u251C\u2500\u2500 point-2.webp\n \u2514\u2500\u2500 ...\n```\n\n### `notes.md` structure\n\n```\n[PREAMBLE BLOCK]\n Project name, one-liner, stack, run commands,\n Login Context (dev/test credentials \u2014 see security note below),\n Coverage Report (red/amber/green zone checklist),\n Invariants, Additional Context.\n\n---NOTES---\n\n## Point 1\nPage: /some/path\nSeverity: bug (bug | question | polish \u2014 tester's own triage)\nStatus: open (open | verified)\nJourney step: <lane> \u2192 <path> (present when linked to a journey step)\nSelector: #some-element (or [data-testid=\"foo\"] etc.)\nNote: the tester's free-text description of the issue / request\n\n<details>Runtime context at capture \u2014 recent console/network events + env snapshot</details>\n\n## Point 2\n...\n```\n\nNote: a tester may also hand you a **single point directly**, pasted via\nQapture's \"Copy as agent prompt\" button, with no ZIP and no preamble at all.\nTreat it exactly like one `## Point N` section below \u2014 skip Steps 1 and 2\n(there is no preamble or coverage report to read), and go straight to Step 3.\n\n---\n\n## Step 1 \u2014 Read the Preamble First\n\nBefore touching any code, open `notes.md` and parse everything **above** the\n`---NOTES---` separator. Extract and internalize:\n\n| Section | What to do |\n| ------------------ | --------------------------------------------------------------------- |\n| **Project / Stack** | Understand the framework, router, ORM, and any unusual constraints. |\n| **Run Commands** | Know how to start the dev server and seed the database. |\n| **Login Context** | DEV/TEST/SEED credentials only. Use these to log in during verification. **Never log, forward, or commit these values.** |\n| **Coverage Report**| List of RED / AMBER / GREEN zones and whether they are covered. |\n| **Invariants** | Absolute rules you must never violate (e.g. \"prices \u2265 0\", \"checkout requires auth\"). |\n| **Conventions** | Codebase naming, file organisation, import rules, validation approach. |\n\n---\n\n## Step 2 \u2014 Flag Uncovered RED Zones Before Acting\n\nAfter reading the preamble, check the Coverage Report for any RED zones that\nare **not yet covered** by an annotation in this ZIP.\n\nIf uncovered RED zones exist, **report them to the developer first**:\n\n```\n\u26A0\uFE0F Uncovered RED zones detected:\n \u2022 /checkout/payment \u2014 no annotation in this export\n \u2022 /seller/payouts \u2014 no annotation in this export\n\nThese are money/auth/irreversible flows. Do you want me to proceed with the\ncovered points only, or will you add annotations for the red zones first?\n```\n\nWait for developer confirmation before proceeding if any RED zone is uncovered.\n\n---\n\n## Step 3 \u2014 Triage, Then Act\n\n### 3a. Triage & cluster before touching anything\n\nRead **every** `## Point N` in the ZIP before acting on any of them. Points\nthat look unrelated on the surface (different pages, different testers, even\ndifferent sessions) can share one root cause \u2014 check each point's runtime\ncontext (3c below) for a repeated signature: the same failing network URL,\nthe same status code, the same console error message. Group matches into one\ncluster. A cluster gets one fix and N verifications (one per point in it),\nnot N separate patches that might silently disagree with each other.\n\nWrite the plan down as an actual artifact before editing anything \u2014 a short\nmarkdown list is enough: each point/cluster, your root-cause hypothesis, the\nproposed fix, and its risk zone. This is the thing you hand to subagents in\nStep 3b, and the thing a developer can skim to sanity-check your read of the\nbatch before code starts moving.\n\n### 3b. Decide who does the work\n\nSingle trivial point, obviously green-zone, no ambiguity \u2192 you can just fix\nit. Anything else \u2192 spawn a subagent per point or cluster, per the Working\nModel above (Sonnet, pinned; effort by your judgment; parallel only across\ndisjoint files). Hand each subagent its point(s), the relevant preamble\ncontext (stack, conventions, invariants, login), and its risk zone.\n\n### 3c. Read the annotation\n\n- **Page** \u2014 the route/URL where the issue was captured.\n- **Severity** \u2014 `bug` (default), `question`, or `polish`. A `question` may\n not need a code change at all \u2014 read the note text before assuming one.\n- **Status** \u2014 `open` (default) or `verified`. A `verified` point was already\n re-checked by the tester after a previous fix; treat it as lower priority\n unless the note says otherwise.\n- **Journey step** \u2014 present when the point was captured during the guided\n walkthrough, or auto-linked by route match. Cross-reference it against the\n Coverage Report: a covered RED step usually has one of these attached.\n- **Selector** \u2014 the CSS selector or aria identifier for the element.\n- **Runtime context** (collapsed `<details>` block, when present) \u2014 recent\n `console.error`/`console.warn` output, uncaught errors, and failed/slow\n network calls captured in the moments before the tester clicked capture,\n plus an environment snapshot (viewport, language, timezone, page-load time).\n **Read this before assuming a UI-only cause.** \"The button does nothing\" is\n very often actually a console `TypeError` or a `500` that already happened\n \u2014 the evidence for it is right there, not something you have to reproduce\n blind. Query strings in any URL shown here have already been redacted by\n Qapture before export (see `SECURITY.md`); do not assume you're seeing a\n full URL, and never assume request bodies/headers were captured \u2014 they\n weren't, by design.\n- **Note** \u2014 the tester's description of the problem or change request.\n- **Forensics** (when present, inside the runtime-context `<details>` block)\n \u2014 computed facts about the exact captured element: `contrastFlag`\n (`low`/`ok`), `hasAccessibleName`, `tabReachable`, plus its computed\n styles. This turns \"looks fixed\" into something checkable: if\n `contrastFlag: low`, the fix isn't done until you can show the new colour\n pair actually clears a 4.5:1 ratio; if `tabReachable: false` on something\n that visually reads as interactive, that's an objective bug independent of\n whatever the tester's note text says. Treat these as acceptance criteria,\n not supplementary trivia.\n\n### 3c-bis. Read \"Steps before this\"\n\nMost points carry a numbered **Steps before this** list \u2014 what the tester\nclicked, typed into, toggled and navigated on the way to the bug, recorded\nautomatically with timings. Treat it as the steps to reproduce: replay it\nbefore you theorise. Note that field CONTENTS are deliberately never\nrecorded, so \"typed in Email\" tells you a field was filled, not with what.\n\n### 3d. Open the screenshot\n\nLoad the screenshot named in that point's **Screenshot** line (\n`screenshots/point-N.webp`, or `.png` on browsers without WebP) to visually\nconfirm what the tester saw.\nThe screenshot is truth \u2014 if the selector doesn't resolve, the screenshot tells\nyou what element they meant.\n\n### 3e. Locate the code\n\nUse the selector priority chain below to find the relevant source:\n\n| Priority | Selector type | Action |\n| -------- | -------------------------------------------------- | --------------------------------------------------- |\n| 1 | `#some-id` | `grep -r 'some-id'` in `src/` |\n| 2 | `[data-testid=\"foo\"]` / `[data-test]` / `[data-cy]` | grep for the attribute value |\n| 3 | `aria-label` on interactive elements | grep for the label string |\n| 4 | `name` attribute on form fields | grep for `name=\"...\"` in the relevant form file |\n| 5 | Structural (e.g. `.card:nth-of-type(2) > button`) | narrow by page route \u2192 component file \u2192 visual match with screenshot |\n| Fallback | Selector didn't resolve | Use the screenshot: identify the element visually, search by text content or component name |\n\nNarrow your search by the **Page** field to avoid editing the wrong route's code.\n\n### 3f. Reproduce it live, before writing a fix\n\nDon't go straight from \"read the note\" to \"guess the fix.\" Run the app, log\nin as the relevant role using **Login Context**, navigate to **Page**, and \u2014\nif the point has a **Journey step** with an `expect` field \u2014 try to actually\ntrigger the failure the way the journey step describes. This catches two\nthings a static screenshot can't: a report that's already stale (fixed\nelsewhere, doesn't reproduce), and a bug whose real trigger is an\ninteraction, not the state the screenshot happened to capture. Only once\nyou've confirmed the failure and understand *why* it happens do you move to\n3g \u2014 writing a fix against a guess is how you end up patching the symptom\nin the screenshot instead of the actual defect.\n\n### 3g. Make the change\n\n- Follow the project's **Conventions** (from the preamble).\n- Respect all **Invariants** \u2014 never violate them even if the annotation implies it.\n- If the change touches a RED zone (money / auth / irreversible state), add an\n explicit comment: `// QA: red-zone change \u2014 reviewed <date>`.\n- Do **not** edit `qa.config.ts`, `qa.preamble.md`, or any qapture plugin files.\n\n---\n\n## Step 4 \u2014 Verify the Fix\n\n1. Run the app using the **Run Commands** from the preamble.\n2. Log in as the relevant role using **Login Context** credentials.\n (These are DEV/TEST/SEED only \u2014 never use production credentials.)\n3. Navigate to the **Page** listed in the annotation.\n4. **Reproduce** the original issue to confirm it existed, then verify it is fixed.\n5. In the browser console, run `document.querySelector('<selector>')` to confirm\n the element resolves as expected.\n6. Check adjacent paths for regressions, especially if the change is in a shared\n component.\n\n---\n\n## Step 5 \u2014 Grade and Report\n\nAfter acting on all points, produce a short report:\n\n```markdown\n## Qapture \u2014 Changes Summary\n\n| Point | Page | Severity | Change made | Verified | Risk |\n| ----- | --------------- | -------- | ----------------------------- | -------- | ----- |\n| 1 | /products | bug | Fixed button label | \u2713 | green |\n| 2 | /checkout | bug | Corrected total calculation | \u2713 | red |\n\n### Coverage vs Red Zones\n- [x] /checkout/payment \u2014 covered by Point 2\n- [ ] /seller/payouts \u2014 NOT covered (flagged in Step 2)\n\n### Uncovered items\nNone (all annotated points addressed).\n\n### Suggestions (proposed, not implemented)\n- [amber] /cart \u2014 quantity stepper has no debounce; noticed while fixing\n Point 1, not part of the report, flagging rather than touching it.\n```\n\nAn **amber** suggestion you *did* implement inline still gets called out\nhere, same as above but phrased as done, not proposed. A **green** one\ndoesn't need a separate line at all \u2014 just mention it in the affected\npoint's \"Change made\" cell. **Red** ideas are always proposal-only, never a\nline item that reads as if it happened.\n\n---\n\n## Risk Zone Reference\n\n| Risk | Examples | Rule |\n| ------- | ----------------------------------------------------------------- | ------------------------------------------------ |\n| **red** | Payment, checkout, auth, order state, refunds, payouts, user data | Never change without developer review + comment |\n| **amber** | Cart, product listings, search, seller dashboard, inventory | Change carefully; verify end-to-end |\n| **green** | Static content, labels, colours, copy, tooltips, layout | Change freely; quick smoke-test |\n\n---\n\n## Security Note\n\n- **Dev/test/seed credentials only.** The Login Context in `notes.md` contains\n credentials from `.env.example` or seeder files \u2014 never from production.\n- **Never log, forward, store, or commit** Login Context values outside the\n development environment.\n- **Never read** `.env`, `.env.local`, `.env.production`, or any `secrets/`\n path. Qapture's CLI enforces this; you must too.\n- Qapture is **100% client-side** \u2014 it makes no network calls, holds no API\n keys, and sends no data anywhere.\n- **Runtime context evidence is already redacted for you.** Any URL shown in\n a point's runtime-context block has had its query string stripped by\n Qapture before export, and request/response bodies, headers, cookies, and\n storage values were never captured in the first place \u2014 treat this section\n as safe local debugging evidence, not as something you need to further\n sanitize.\n- **Never push, publish, or deploy** changes without explicit human approval,\n regardless of risk level.\n\n---\n\n## Common Pitfalls\n\n- **Don't assume selectors always resolve.** Selector strings may be stale if\n the DOM changed after annotation. When in doubt, use the screenshot.\n- **Don't skip the preamble.** Acting without reading the invariants or run\n commands is the most common source of broken fixes.\n- **Don't edit qapture config or plugin files** (`qa.config.ts`,\n `qa.preamble.md`, `.claude/skills/qapture/`, `src/components/qa-overlay/`).\n- **Don't use production credentials** \u2014 ever.\n- **Don't push/publish without human approval** \u2014 always present the changes\n for review first.\n- **Don't violate invariants** even if the annotation seems to imply it.\n Surface the conflict to the developer instead.\n- **Don't ignore the runtime context block.** A point's collapsed \"Runtime\n context at capture\" section is often the actual root cause, not\n supplementary detail \u2014 check it before guessing at one from the note text\n and screenshot alone.\n- **Don't treat a `question`-severity, `fixed`- or `verified`-status point\n like a routine bug fix.** A `question` may just need an answer, not code; a\n `fixed` point is already claimed done and is sitting in the tester's\n re-test queue, so re-fixing it blindly risks undoing someone's work; a\n `verified` point was re-confirmed once and should be double-checked before\n you assume it's still broken.\n- **Don't work every point serially when they could parallelize.** If you\n catch yourself fixing point after point one at a time on a batch of five\n independent-file points, stop and re-read the Working Model section above.\n- **Don't let two subagents touch the same file at once.** File overlap, not\n point count, is what decides parallel vs serial \u2014 check this before you\n spawn anything.\n- **Don't treat a subagent's \"done\" as the verification gate.** Run the\n project's own test/verify command yourself, independently, after every\n subagent lands its change.\n- **Don't silently implement a creative idea beyond a green zone.** Amber\n gets implemented-and-disclosed; red gets proposed-and-left-alone. Never\n implemented-and-undisclosed.\n\n---\n\n_Qapture \u2014 https://github.com/mohammed-farhood/qapture_\n";
32
+ var SKILL_default = "---\nname: qapture\ndescription: >\n Activated when the user provides a `qa-notes-*.zip` file exported from\n Qapture. Reads the preamble block in `notes.md` (project context, stack, run\n commands, dev/test login credentials, red-zone coverage report, and\n invariants), flags any uncovered RED risk zones before acting, then\n triages every `## Point N` as a batch \u2014 clustering points that share a\n root cause via their runtime-context evidence \u2014 before orchestrating\n Sonnet subagents (one per point/cluster, parallelized across disjoint\n files) to reproduce each issue live, fix it, and self-verify. Finally\n grades coverage against the red zones and reports, including any adjacent\n improvements noticed along the way. Also activates on a single point\n pasted directly via Qapture's \"Copy as agent prompt\" (no ZIP, no preamble\n \u2014 just one point).\n\n **No AI is bundled in Qapture \u2014 YOU are the AI reading these artifacts.**\n Qapture is a 100% client-side, keyless, network-free capture widget.\ntriggers:\n - qa-notes-*.zip\n---\n\n# Qapture \u2014 Agent Skill\n\n> **Core principle:** Qapture ships zero AI. No model, no API keys, no network\n> calls. The CLI is a plain deterministic scaffolder. **You** \u2014 the coding agent\n> reading this skill \u2014 are the AI. The developer used Qapture to capture\n> annotated screenshots + notes from their live app; your job is to act on them.\n\n---\n\n## What Is Qapture?\n\nQapture is a drop-in in-browser widget (Shadow DOM, keyless, no telemetry).\nTesters annotate the live app: click an element or draw a region, add a note,\nand the widget captures a screenshot automatically. When done, they export a\n`qa-notes-*.zip`. That ZIP is the hand-off to you.\n\n---\n\n## Working Model \u2014 You're the Brain, Subagents Are the Muscle\n\nFor anything past a single trivial point, don't work through the ZIP\npoint-by-point yourself. Orchestrate:\n\n- **You cluster and plan; subagents reproduce, fix, and self-verify.** You\n read every point first (Step 3), decide what's really one bug wearing N\n reports, and spawn one subagent per point or cluster to do the actual work.\n You do the fixing yourself only for a single, obviously trivial point where\n spinning up a subagent is pure overhead.\n- **Every subagent is Sonnet, pinned explicitly on every single call.** Never\n let a spawned agent inherit whatever model you happen to be running as\n orchestrator \u2014 an inherited model silently drifts to whatever you're on,\n and that's the kind of thing nobody notices until the cost or the quality\n looks wrong in hindsight. Pin it every time, no exceptions.\n- **Effort is your judgment call per task, not a formula.** A one-line CSS\n fix gets low effort. A bug whose runtime-context evidence (below) doesn't\n cleanly explain the symptom gets high or max \u2014 you decide based on how\n ambiguous the root cause actually looks, case by case.\n- **Parallelize by file, not by point.** Points/clusters that land in\n disjoint files can run as concurrent subagents safely. Anything that lands\n in the *same* file goes to one agent, or runs serially \u2014 never two agents\n editing the same file at once. This is the actual failure mode to guard\n against, not an abstract \"be careful.\"\n- **Supervise by reality-checking, not re-reading.** Don't reread every\n subagent's full diff. Do: read exactly what each one's own report claims\n changed, at the file/location it names; always personally open and read\n the diff for anything touching a RED zone, no exceptions, regardless of\n what the subagent reports; and treat the project's own test/verify command\n \u2014 run by you, independently, after every subagent lands \u2014 as the actual\n gate. A subagent saying \"done\" is a claim, not a fact.\n\n**Creative suggestions are always welcome; creative *changes* are gated like\neverything else, by risk colour (see Risk Zone Reference below).** Noticing a\npattern, an adjacent bug, or a missing feature costs nothing to write down \u2014\nput it in a `### Suggestions` section in your final report (Step 5) and never\nsuppress it for being out of scope. Whether you're allowed to *implement* it\nwithout being asked depends on where it lands:\n\n| Zone | An idea beyond the literal reported point |\n| --- | --- |\n| **green** | Implement inline, no permission needed \u2014 \"fixed the label, also fixed 3 nearby with the same casing bug\" is fine. |\n| **amber** | Implement it, but call it out explicitly in the report \u2014 the developer should see at a glance what went beyond what was asked. |\n| **red** | Propose only. Write it down, never touch the code. Same rule as everything else in a red zone: no silent business-logic decisions. |\n\nThat keeps the upside of a genuinely observant agent \u2014 catching the adjacent\nbug, proposing the missing feature \u2014 without that same latitude becoming the\nmechanism for quietly rewriting a payment flow nobody asked you to touch.\n\n---\n\n## ZIP Layout\n\n```\nqa-notes-<timestamp>.zip\n\u251C\u2500\u2500 notes.md \u2190 ALWAYS read this first (see Step 1)\n\u251C\u2500\u2500 verify.md \u2190 the checklist you must fill in and hand back (Step 4)\n\u2514\u2500\u2500 screenshots/\n \u251C\u2500\u2500 point-1.webp\n \u251C\u2500\u2500 point-2.webp\n \u2514\u2500\u2500 ...\n```\n\n### `notes.md` structure\n\n```\n[PREAMBLE BLOCK]\n Project name, one-liner, stack, run commands,\n Login Context (dev/test credentials \u2014 see security note below),\n Coverage Report (red/amber/green zone checklist),\n Invariants, Additional Context.\n\n---NOTES---\n\n## Point 1\nPage: /some/path\nSeverity: bug (bug | question | polish \u2014 tester's own triage)\nStatus: open (open | verified)\nJourney step: <lane> \u2192 <path> (present when linked to a journey step)\nSelector: #some-element (or [data-testid=\"foo\"] etc.)\nNote: the tester's free-text description of the issue / request\n\n<details>Runtime context at capture \u2014 recent console/network events + env snapshot</details>\n\n## Point 2\n...\n```\n\nNote: a tester may also hand you a **single point directly**, pasted via\nQapture's \"Copy as agent prompt\" button, with no ZIP and no preamble at all.\nTreat it exactly like one `## Point N` section below \u2014 skip Steps 1 and 2\n(there is no preamble or coverage report to read), and go straight to Step 3.\n\n---\n\n## Step 1 \u2014 Read the Preamble First\n\nBefore touching any code, open `notes.md` and parse everything **above** the\n`---NOTES---` separator. Extract and internalize:\n\n| Section | What to do |\n| ------------------ | --------------------------------------------------------------------- |\n| **Project / Stack** | Understand the framework, router, ORM, and any unusual constraints. |\n| **Run Commands** | Know how to start the dev server and seed the database. |\n| **Login Context** | DEV/TEST/SEED credentials only. Use these to log in during verification. **Never log, forward, or commit these values.** |\n| **Coverage Report**| List of RED / AMBER / GREEN zones and whether they are covered. |\n| **Invariants** | Absolute rules you must never violate (e.g. \"prices \u2265 0\", \"checkout requires auth\"). |\n| **Conventions** | Codebase naming, file organisation, import rules, validation approach. |\n\n---\n\n## Step 2 \u2014 Flag Uncovered RED Zones Before Acting\n\nAfter reading the preamble, check the Coverage Report for any RED zones that\nare **not yet covered** by an annotation in this ZIP.\n\nIf uncovered RED zones exist, **report them to the developer first**:\n\n```\n\u26A0\uFE0F Uncovered RED zones detected:\n \u2022 /checkout/payment \u2014 no annotation in this export\n \u2022 /seller/payouts \u2014 no annotation in this export\n\nThese are money/auth/irreversible flows. Do you want me to proceed with the\ncovered points only, or will you add annotations for the red zones first?\n```\n\nWait for developer confirmation before proceeding if any RED zone is uncovered.\n\n---\n\n## Step 3 \u2014 Triage, Then Act\n\n### 3a. Triage & cluster before touching anything\n\nRead **every** `## Point N` in the ZIP before acting on any of them. Points\nthat look unrelated on the surface (different pages, different testers, even\ndifferent sessions) can share one root cause \u2014 check each point's runtime\ncontext (3c below) for a repeated signature: the same failing network URL,\nthe same status code, the same console error message. Group matches into one\ncluster. A cluster gets one fix and N verifications (one per point in it),\nnot N separate patches that might silently disagree with each other.\n\nWrite the plan down as an actual artifact before editing anything \u2014 a short\nmarkdown list is enough: each point/cluster, your root-cause hypothesis, the\nproposed fix, and its risk zone. This is the thing you hand to subagents in\nStep 3b, and the thing a developer can skim to sanity-check your read of the\nbatch before code starts moving.\n\n### 3b. Decide who does the work\n\nSingle trivial point, obviously green-zone, no ambiguity \u2192 you can just fix\nit. Anything else \u2192 spawn a subagent per point or cluster, per the Working\nModel above (Sonnet, pinned; effort by your judgment; parallel only across\ndisjoint files). Hand each subagent its point(s), the relevant preamble\ncontext (stack, conventions, invariants, login), and its risk zone.\n\n### 3c. Read the annotation\n\n- **Page** \u2014 the route/URL where the issue was captured.\n- **Severity** \u2014 `bug` (default), `question`, or `polish`. A `question` may\n not need a code change at all \u2014 read the note text before assuming one.\n- **Status** \u2014 `open` (default) or `verified`. A `verified` point was already\n re-checked by the tester after a previous fix; treat it as lower priority\n unless the note says otherwise.\n- **Journey step** \u2014 present when the point was captured during the guided\n walkthrough, or auto-linked by route match. Cross-reference it against the\n Coverage Report: a covered RED step usually has one of these attached.\n- **Selector** \u2014 the CSS selector or aria identifier for the element.\n- **Runtime context** (collapsed `<details>` block, when present) \u2014 recent\n `console.error`/`console.warn` output, uncaught errors, and failed/slow\n network calls captured in the moments before the tester clicked capture,\n plus an environment snapshot (viewport, language, timezone, page-load time).\n **Read this before assuming a UI-only cause.** \"The button does nothing\" is\n very often actually a console `TypeError` or a `500` that already happened\n \u2014 the evidence for it is right there, not something you have to reproduce\n blind. Query strings in any URL shown here have already been redacted by\n Qapture before export (see `SECURITY.md`); do not assume you're seeing a\n full URL, and never assume request bodies/headers were captured \u2014 they\n weren't, by design.\n- **Note** \u2014 the tester's description of the problem or change request.\n- **Forensics** (when present, inside the runtime-context `<details>` block)\n \u2014 computed facts about the exact captured element: `contrastFlag`\n (`low`/`ok`), `hasAccessibleName`, `tabReachable`, plus its computed\n styles. This turns \"looks fixed\" into something checkable: if\n `contrastFlag: low`, the fix isn't done until you can show the new colour\n pair actually clears a 4.5:1 ratio; if `tabReachable: false` on something\n that visually reads as interactive, that's an objective bug independent of\n whatever the tester's note text says. Treat these as acceptance criteria,\n not supplementary trivia.\n\n### 3c-bis. Read \"Steps before this\"\n\nMost points carry a numbered **Steps before this** list \u2014 what the tester\nclicked, typed into, toggled and navigated on the way to the bug, recorded\nautomatically with timings. Treat it as the steps to reproduce: replay it\nbefore you theorise. Note that field CONTENTS are deliberately never\nrecorded, so \"typed in Email\" tells you a field was filled, not with what.\n\n### 3d. Open the screenshot\n\nLoad the screenshot named in that point's **Screenshot** line (\n`screenshots/point-N.webp`, or `.png` on browsers without WebP) to visually\nconfirm what the tester saw.\nThe screenshot is truth \u2014 if the selector doesn't resolve, the screenshot tells\nyou what element they meant.\n\n### 3e. Locate the code\n\nUse the selector priority chain below to find the relevant source:\n\n| Priority | Selector type | Action |\n| -------- | -------------------------------------------------- | --------------------------------------------------- |\n| 1 | `#some-id` | `grep -r 'some-id'` in `src/` |\n| 2 | `[data-testid=\"foo\"]` / `[data-test]` / `[data-cy]` | grep for the attribute value |\n| 3 | `aria-label` on interactive elements | grep for the label string |\n| 4 | `name` attribute on form fields | grep for `name=\"...\"` in the relevant form file |\n| 5 | Structural (e.g. `.card:nth-of-type(2) > button`) | narrow by page route \u2192 component file \u2192 visual match with screenshot |\n| Fallback | Selector didn't resolve | Use the screenshot: identify the element visually, search by text content or component name |\n\nNarrow your search by the **Page** field to avoid editing the wrong route's code.\n\n### 3f. Reproduce it live, before writing a fix\n\nDon't go straight from \"read the note\" to \"guess the fix.\" Run the app, log\nin as the relevant role using **Login Context**, navigate to **Page**, and \u2014\nif the point has a **Journey step** with an `expect` field \u2014 try to actually\ntrigger the failure the way the journey step describes. This catches two\nthings a static screenshot can't: a report that's already stale (fixed\nelsewhere, doesn't reproduce), and a bug whose real trigger is an\ninteraction, not the state the screenshot happened to capture. Only once\nyou've confirmed the failure and understand *why* it happens do you move to\n3g \u2014 writing a fix against a guess is how you end up patching the symptom\nin the screenshot instead of the actual defect.\n\n### 3g. Make the change\n\n- Follow the project's **Conventions** (from the preamble).\n- Respect all **Invariants** \u2014 never violate them even if the annotation implies it.\n- If the change touches a RED zone (money / auth / irreversible state), add an\n explicit comment: `// QA: red-zone change \u2014 reviewed <date>`.\n- Do **not** edit `qa.config.ts`, `qa.preamble.md`, or any qapture plugin files.\n\n---\n\n## Step 4 \u2014 Verify the Fix\n\n**`verify.md` is the deliverable, not a formality.** It holds one unticked box\nper point, and the tester will be walked back through those same checks on the\nreal page \u2014 in that order, one stop at a time \u2014 and asked about each: *is this\nnow what I asked for?* Whatever they say is still wrong comes back to you as\nround two with their new words attached.\n\nSo when you are done:\n\n- Tick a box only when the check is true on a **fresh load** of the page named\n beside it, with the tester doing nothing extra.\n- Where you could not do something, or you think it is the wrong thing to do,\n **leave the box unticked and write one line under it saying why.** An\n unticked box with a reason is a good answer. A ticked box that does not hold\n up is the only bad one \u2014 it costs the tester a wasted trip to find out.\n- Hand `verify.md` back with the work.\n\nBefore ticking anything:\n\n1. Run the app using the **Run Commands** from the preamble.\n2. Log in as the relevant role using **Login Context** credentials.\n (These are DEV/TEST/SEED only \u2014 never use production credentials.)\n3. Navigate to the **Page** listed in the annotation.\n4. **Reproduce** the original issue to confirm it existed, then verify it is fixed.\n5. In the browser console, run `document.querySelector('<selector>')` to confirm\n the element resolves as expected.\n6. Check adjacent paths for regressions, especially if the change is in a shared\n component.\n\n---\n\n## Step 5 \u2014 Grade and Report\n\nAfter acting on all points, produce a short report:\n\n```markdown\n## Qapture \u2014 Changes Summary\n\n| Point | Page | Severity | Change made | Verified | Risk |\n| ----- | --------------- | -------- | ----------------------------- | -------- | ----- |\n| 1 | /products | bug | Fixed button label | \u2713 | green |\n| 2 | /checkout | bug | Corrected total calculation | \u2713 | red |\n\n### Coverage vs Red Zones\n- [x] /checkout/payment \u2014 covered by Point 2\n- [ ] /seller/payouts \u2014 NOT covered (flagged in Step 2)\n\n### Uncovered items\nNone (all annotated points addressed).\n\n### Suggestions (proposed, not implemented)\n- [amber] /cart \u2014 quantity stepper has no debounce; noticed while fixing\n Point 1, not part of the report, flagging rather than touching it.\n```\n\nAn **amber** suggestion you *did* implement inline still gets called out\nhere, same as above but phrased as done, not proposed. A **green** one\ndoesn't need a separate line at all \u2014 just mention it in the affected\npoint's \"Change made\" cell. **Red** ideas are always proposal-only, never a\nline item that reads as if it happened.\n\n---\n\n## Risk Zone Reference\n\n| Risk | Examples | Rule |\n| ------- | ----------------------------------------------------------------- | ------------------------------------------------ |\n| **red** | Payment, checkout, auth, order state, refunds, payouts, user data | Never change without developer review + comment |\n| **amber** | Cart, product listings, search, seller dashboard, inventory | Change carefully; verify end-to-end |\n| **green** | Static content, labels, colours, copy, tooltips, layout | Change freely; quick smoke-test |\n\n---\n\n## Security Note\n\n- **Dev/test/seed credentials only.** The Login Context in `notes.md` contains\n credentials from `.env.example` or seeder files \u2014 never from production.\n- **Never log, forward, store, or commit** Login Context values outside the\n development environment.\n- **Never read** `.env`, `.env.local`, `.env.production`, or any `secrets/`\n path. Qapture's CLI enforces this; you must too.\n- Qapture is **100% client-side** \u2014 it makes no network calls, holds no API\n keys, and sends no data anywhere.\n- **Runtime context evidence is already redacted for you.** Any URL shown in\n a point's runtime-context block has had its query string stripped by\n Qapture before export, and request/response bodies, headers, cookies, and\n storage values were never captured in the first place \u2014 treat this section\n as safe local debugging evidence, not as something you need to further\n sanitize.\n- **Never push, publish, or deploy** changes without explicit human approval,\n regardless of risk level.\n\n---\n\n## Common Pitfalls\n\n- **Don't assume selectors always resolve.** Selector strings may be stale if\n the DOM changed after annotation. When in doubt, use the screenshot.\n- **Don't skip the preamble.** Acting without reading the invariants or run\n commands is the most common source of broken fixes.\n- **Don't edit qapture config or plugin files** (`qa.config.ts`,\n `qa.preamble.md`, `.claude/skills/qapture/`, `src/components/qa-overlay/`).\n- **Don't use production credentials** \u2014 ever.\n- **Don't push/publish without human approval** \u2014 always present the changes\n for review first.\n- **Don't violate invariants** even if the annotation seems to imply it.\n Surface the conflict to the developer instead.\n- **Don't ignore the runtime context block.** A point's collapsed \"Runtime\n context at capture\" section is often the actual root cause, not\n supplementary detail \u2014 check it before guessing at one from the note text\n and screenshot alone.\n- **Don't treat a `question`-severity, `fixed`- or `verified`-status point\n like a routine bug fix.** A `question` may just need an answer, not code; a\n `fixed` point is already claimed done and is sitting in the tester's\n re-test queue, so re-fixing it blindly risks undoing someone's work; a\n `verified` point was re-confirmed once and should be double-checked before\n you assume it's still broken.\n- **Don't work every point serially when they could parallelize.** If you\n catch yourself fixing point after point one at a time on a batch of five\n independent-file points, stop and re-read the Working Model section above.\n- **Don't let two subagents touch the same file at once.** File overlap, not\n point count, is what decides parallel vs serial \u2014 check this before you\n spawn anything.\n- **Don't treat a subagent's \"done\" as the verification gate.** Run the\n project's own test/verify command yourself, independently, after every\n subagent lands its change.\n- **Don't silently implement a creative idea beyond a green zone.** Amber\n gets implemented-and-disclosed; red gets proposed-and-left-alone. Never\n implemented-and-undisclosed.\n\n---\n\n_Qapture \u2014 https://github.com/mohammed-farhood/qapture_\n";
33
33
 
34
34
  // src/artifacts/AGENTS_SECTION.md
35
35
  var AGENTS_SECTION_default = "<!-- qa-studio-section -->\n\n## Qapture\n\nThis project uses **Qapture** \u2014 an in-browser QA capture widget that ships\n**zero AI** (no model, no API keys, no network calls). **You** are the AI.\n\n### When you receive a `qa-notes-*.zip`\n\n1. **Unzip** the file.\n2. **Read `notes.md` top-to-bottom**, starting with everything above the\n `---NOTES---` separator:\n - **Project context** \u2014 name, stack, run commands, conventions.\n - **Login Context** \u2014 dev/test/seed credentials for the relevant roles.\n _(DEV/TEST/SEED only \u2014 never commit, log, or forward these values.)_\n - **Coverage Report** \u2014 red/amber/green zone checklist.\n - **Invariants** \u2014 rules you must never violate (e.g. \"prices \u2265 0\",\n \"checkout requires auth\").\n3. **Flag uncovered RED zones** before acting. RED = money / auth / irreversible\n state. If any red zone has no annotation in this ZIP, report it and ask the\n developer whether to proceed.\n4. **Triage the whole batch before touching code.** Read every point first \u2014\n points on different pages can share one root cause (check their runtime\n context for a repeated failing URL or error message) and deserve one fix,\n not N. Write the plan down before editing anything.\n5. **Orchestrate, don't grind through it yourself.** For anything past a\n single trivial point: you're the brain, not the muscle. Spawn one Sonnet\n subagent per point/cluster (model pinned explicitly on every call, never\n inherited; effort is your judgment call per task), parallelized across\n points that touch disjoint files \u2014 never two agents on the same file. You\n supervise by checking what each subagent's report actually claims and by\n running the project's own verify/test command yourself afterward, not by\n rereading every diff or trusting a subagent's \"done.\"\n6. **Each point/cluster, before fixing:**\n - **Page** + **Selector** + **Note** \u2192 locate the element in the source\n (priority: `#id` \u2192 `[data-testid]` \u2192 `aria-label` \u2192 `name` \u2192 visual match\n via the screenshot named in the point's **Screenshot** line \u2014\n `screenshots/point-N.webp`, or `.png` on browsers without WebP).\n - **Severity** (`bug`/`question`/`polish`) and **Status**\n (`open`/`fixed`/`verified` \u2014 `fixed` means someone says it is done but\n nobody has re-tested it yet)\n tell you how to treat the point \u2014 a `question` may not need a code\n change; a `verified` point was already re-checked once.\n - **Runtime context** (when present, in a collapsed `<details>` block) \u2014\n recent console errors/warnings and failed network calls captured right\n before the tester clicked capture, plus an environment snapshot, plus\n **forensics** (contrast/accessibility flags on the captured element) when\n present \u2014 treat these as objective acceptance criteria, not just the\n tester's prose. Read it before assuming a UI-only cause. Query strings in\n any URL there are already redacted; bodies, headers, cookies, and storage\n were never captured at all.\n - **Reproduce it live first** \u2014 run the app, log in as the relevant role,\n navigate to the page, actually trigger the failure \u2014 before writing a\n fix. A static screenshot can hide a stale report or an interaction-only\n bug.\n - Make the change following the project conventions and invariants, then\n re-verify the same repro.\n7. **Report** a summary table of changes, risk levels, and coverage status,\n plus a `### Suggestions` section for anything noticed beyond the reported\n points. Whether you may *implement* an extra idea (not just suggest it)\n follows the same red/amber/green gating as everything else: green \u2014 do it\n inline; amber \u2014 do it and disclose it; red \u2014 propose only, never touch it.\n\n### A single point, no ZIP\n\nA tester can also send you **one point directly**, pasted via Qapture's\n\"Copy as agent prompt\" button, with no ZIP and no preamble. Treat it exactly\nlike one point from step 6 above \u2014 there's nothing to cluster or triage with\nonly one point, and there's no Coverage Report to check or RED-zone flag to\nraise, since there's no journey context at all. Just fix it yourself if it's\ntrivial, or spawn a single subagent if it isn't.\n\n### Full protocol\n\n`.claude/skills/qapture/SKILL.md` (always kept current by `qapture init`).\n\n### Rules\n\n- Never read `.env`, `.env.local`, `.env.production`, or any `secrets/` path.\n- Never edit `qa.config.ts`, `qa.preamble.md`, or any qapture plugin files.\n- Never push/publish/deploy without explicit human approval.\n- Dev/test/seed credentials only \u2014 never use or request production credentials.\n\n_Qapture \u2014 https://github.com/mohammed-farhood/qapture_\n\n<!-- /qa-studio-section -->\n";