qa
Audits a feature that has shipped to a dev or staging server against the plan of record — spec entries and the Figma design. One mode stops once the checklist is drawn; the other clicks through the br
Files1
View on GitHubSKILL.mdplugins/fig/skills/qa/SKILL.md
GitHubqa — baseline-referenced QA (checklist → verification → defect report)
Once a feature is on a server, compares it against the plan of record, finds what diverged, and hands it over in a shape engineering can act on directly.
The premise: no verdict without a baseline. Not "this looks wrong" but "this breaks rule X in document Y". An item with no baseline is not a defect — it is needs confirmation.
When to invoke
- A dev or staging deploy has been announced and the behaviour needs looking at
- A reported defect needs reproducing and confirming
- A pre-release sweep for anything the plan called for and the build is missing
- A checklist only, with no verification (
baselinemode)
When NOT to invoke
- Auditing the design's own structure and naming →
/fig:lint - Carrying design-versus-code differences back into the file →
/fig:code - Reviewing a document or a message → not this skill
- Filing defects as tickets → whatever ticket tool you use. This skill stops at the report
Inputs
source(required): where the request came from — a thread link, a page, or the request as spokentarget(required): the screen or feature under verificationmode(optional):baseline(stop at the checklist) /full(verify and report). Omitted, it asksenv(optional): the address, the account scope, the data scope to test against (branch, org, and so on). Given a name alone, the address is looked up inqa.environments
Modes
baseline Steps 1–3 only. Emits the checklist and stops — for when someone else will run it,
or when the scope needs agreeing before verification starts
full Steps 1–7. Verifies through the browser and files the defect report
Even in full, the step-3 checklist is shown before verification begins. Start clicking without agreeing on what will be looked at and the scope drifts.
Where the rules come from
python3 ${CLAUDE_PLUGIN_ROOT}/_common/scripts/lib/resolve-config.py --js {fileKey}
qa.baseline, qa.environments and qa.report_dir are read from it. The PRD is then looked up, not read from the config directly:
python3 ${CLAUDE_PLUGIN_ROOT}/_common/scripts/lib/borrow-pm.py prd {fileKey}
This skill takes no spec_url. The script answers {type, ref, from, split, candidates, note}; act on it, do not re-derive it.
markdown, one candidate →Readit; a#…inrefnames the entry to start from. Several → ask which, offering the products. Splitdomain→Readindex.mdand the domain file the target belongs tomarkdown, no candidates → no document to read: pm's PRD folder holds no document yet, or the named file is missing. Show thenoteif there is one, and handle it asnonenotion→notion-fetchtherefnone→ no PRD: every item that would rest on it is markedneeds confirmation. Anoteis shown in one line- Exit 1 (fig's own config cannot be read) → say what it printed and stop; the config needs fixing first
1. Settle the target (reading, no confirmation needed)
Read the source and pull out the following. Do not guess.
- What shipped / where this round's scope ends
- Defects already reported — if there are any, they reproduce first
- Anything the requester specifically asked to have checked
- Exclusions (scope they said not to look at)
Whatever the source does not say, leave as TBD, and only ask about the parts that would change the verification result.
2. Collect the baseline (reading)
Gather the documents the verdicts will rest on. Internal assets first. Where to look is set by qa.baseline and, for the PRD, by the lookup above; any entry that is null is skipped.
| Source | What to pull |
|---|---|
| The PRD (from the lookup above) | Detailed behaviour (Given/When/Then), states and cases, rules and exceptions |
| The design | Screen composition, state variants, copy — at the version the last hand-over line pinned (its version-id); the live file where there is no pin (below) |
Existing tickets (qa.baseline.tasks, as { type, ref }; markdown or notion — there is no GitHub read path) |
Past decisions and reversals, undecided items. Where the type or ref is null, this row is skipped |
| The request itself | What is newly asked for in this round |
- Pull features and policies exhaustively, by domain. Look at only a handful and verification items leak out.
- Read
archivedanddeprecatedentries too — a deprecated feature still present on screen is a common defect. - An entry whose status is not
confirmedmay still serve as a baseline, but the report marks itbaseline unconfirmed. - Which design. Take the last hand-over line from the feature's task record — the lines read oldest to newest, so the last is the current handover — or a pinned link given in the request itself. The record is the one
qa.baseline.taskspoints at; where that is unset, it ispm's, found the way/fig:handofffinds where to write:python3 ${CLAUDE_PLUGIN_ROOT}/_common/scripts/lib/borrow-pm.py task {fileKey}, and where itsactioniswrite,python3 {script} list --dir {dir}and the record whose title matches the feature (ask when several could), reading the lines under{heading}. Any otheractiongives no line here. Where its links carryversion-id, read each frame at that version, by${CLAUDE_PLUGIN_ROOT}/_common/version-pin.md, Reading a pinned version as an image. The live file is read instead, and the report's head says why, where the line has noversion-id(pinning was off), where there is no hand-over line to read, and where there is no token. A frame whose image comes backnullat that version is read live on its own, and named. QA never stops on any of these.
3. Build the checklist
Group by screen and by feature, and write each item as a sentence that clicking will prove true or false.
① [screen name]
□ [behaviour] — baseline: spec detailed behaviour
□ [state] — empty / loading / error / permission
□ [rule] — value constraints, inheritance, linkage rules
← mark reported defects in place so it is visible that they are reproduction targets
Do not let any screen miss these four kinds.
- Happy path — exactly as the behaviour table has it
- States and cases — empty, loading, error, permission, pagination
- Rules and exceptions — inheritance, pinning, linkage: the "where does this value come from" rules
- Round trip — after saving or editing, does it land correctly in the list and on other screens
Items that cannot be seen for lack of an account or data are separated out here in advance as will not verify.
baseline mode ends here.
4. Prepare access
- Confirm the address and the data scope. Do not touch scope they said to leave alone.
- Never enter login or authentication details on the user's behalf. Ask for the screen to be brought up, then take over.
- Before verification starts, record the current state (total counts, the values of the target items) — it is the reference for before-and-after comparison and for restoring.
5. Run the verification
Work down the checklist in order. Batch clicks, input, waits, and captures together to cut round trips.
Data handling
- Do not create or delete. Go as far as the modal appearing, the required-field guard, and cancel. Even where
qa.allow_writeis true, do it only when genuinely necessary, and only after confirming. - If a value was changed, restore it before saving. For verification that must go through a save, confirm there is a path back first.
- Any change that could not be restored is recorded on the spot and carried into the report.
Ways of catching what gets missed
These have caught things repeatedly in practice. Run them whether or not they are on the checklist.
| Check | How | What it catches |
|---|---|---|
| First action after entry | Refresh → switch tab, filter, or search immediately, with no other interaction | The class of bug where the first click is swallowed whole. It behaves from the second onward, so manual QA keeps missing it |
| Cross-path comparison | Look at the same data through a different route (filter vs search vs detail) | Values or images going blank on one retrieval path only |
| Count comparison | Compare totals before and after a save or a toggle | Items lost or duplicated in the list |
| Round-trip reflection | Change it in the detail view, come back to the list, check | The class where the save works but the list never picks it up |
| Failure evidence | When an error appears, capture the network request and the console alongside | Which request failed with which code — the ground for handing it to engineering |
| Cross-list consistency | Compare the lists of two screens that handle the same items | Items present on one side only; values selectable elsewhere that the managing list does not have |
| Input guards | Actually type into a value that is supposed to be fixed | A no-edit rule that the screen does not enforce |
Reproduction and verdicts
- One failure is not a confirmed defect. Reproduce at least twice, varying the conditions.
- If it does not reproduce, write it up as
intermittentwith the conditions it was observed under. Do not delete it. - For anything that might be the data's fault (an unregistered value and the like), check the same item through another path to separate an implementation problem from a data problem.
- A difference with no baseline behind it is not a defect — it is
needs confirmation. When it disagrees with the design or the document, ask which of them is current first. - Watch for false positives: state-dependent checks are confirmed only after actually producing that state (an unsaved-changes warning, for instance, is verified by leaving with the value changed).
Capture every issue as it happens
Every defect and every needs confirmation item gets screenshots taken at the moment it is observed, saved to disk — not reconstructed afterwards. Going back to re-capture means touching the data and the environment a second time, and some states (a locked operating mode, a value that has since been restored) cannot be produced again without asking.
- One capture per reproduction step that shows something — the starting screen, the input, the result. A before-and-after pair is the minimum for anything that changes a value.
- Crop to the part that matters (zoom on the region) and keep one wider shot so the reader can tell where on the page it is.
- Name the files
{item number}-{step}-{what it shows}so the report can point at them and they sort in reading order. - Where the evidence is a request result rather than something visible, capture the screen it came from anyway, and put the request on the evidence line.
- Items verified good get one capture each at most — enough to show the state was reached, not a gallery.
6. The defect report
Keep what was reported and what was newly found apart. The requester looks first at what became of the item they raised.
[reported] reproduced or not, plus what was observed, for each
[new] by severity
Severity is cut by the size of the consequence. Labels come from qa.severity — every org calls these something different, so follow the wording in their bug tracker exactly.
Level 1 data loss, unrecoverable state, work stopped
Level 2 core behaviour impossible, baseline violated
Level 3 some information missing, lists disagreeing
Level 4 copy, icons, legibility
Match the fields to the issue template
Defects end up filed as issues. Write them into the issue template's fields one-to-one from the start, so moving them across fills the body as it is. The fields come from qa.report_fields; with no setting, use the ones below.
Whatever is common to the whole round goes at the head of the report once — not repeated per defect.
project / version, build
environment — server (dev, staging, production) · account permissions · data scope
baseline documents — feature and policy entries · design (the pinned version's label and version-id, or "live file — {why}") · spec
One defect:
{number} · {summary — one line, a noun phrase}
· Severity: one of `qa.severity`
· Frequency: one of `qa.frequency`
· Reproduce: 1) … 2) … 3) …
· Expected: … (baseline: which document, which part)
· Actual: …
· Evidence: captures (file names, in step order) · request results · count changes
Do not drop frequency — leaving an intermittent defect to read as "always" burns engineering time on reproduction. Write the verdict in plan language, and keep engineering detail such as request results and code on the evidence line only.
7. What the closing must always carry
- Verified good — listing what turned out fine is what makes coverage visible
- Not verified, with the reason (no account, out of scope, no data)
- Changes left behind — data that could not be restored, and how it happened
Without these three the report reads as "I looked at everything". Writing down what was not looked at is half of a report.
Delivering the report
The report goes to the response, and, where qa.report_dir names a folder, also to a markdown file there (named for the feature and the date). Where qa.report_dir is null it is the response only, and no report file is written (the captures are saved to disk either way). Show the file's whole text and path first and write it only after "go". Filing the defects anywhere else is outside this skill (see When NOT to invoke).
The person reading the report sees the captures, not a list of file names. Walk each issue step by step — what was done, the capture, what it shows — in plain language before any engineering detail, and send the images themselves (attach them, or use whatever file-sending tool the session has). Where the report is a markdown file, say which captures go with which item, and give their paths or links rather than leaving a name that opens nothing.