qp-run — Claude Code skill: run QA test cases on a live app
Walks test cases from qp-plan against a running app through the session adapter and records a verdict for every step. Useful once the plan exists and someone has to execute it.
npx skills add chrono-meta/forge-harness --skill qp-run -a claude-codeWhat qp-run does
Input is qp/plan/tcs.tsv plus the adapter lines in qp/plan/inventory.md; output goes to its own run folder. Each case starts with its precondition. Navigation such as at:/about gets done, while state you cannot create is flagged UNMET_UNEXECUTABLE. A step-1 failure there is reported as BLOCKED, since the run never reached the screen.
On web, a step is an accessibility snapshot, the action, then another snapshot; desktop uses screenshots the same way. Observations are saved as files, and evidence.tsv gets their stable id from qp_tools.sh screen-id. Verdicts live in verdicts.tsv with seven fields: PASS, FAIL, BLOCKED or AMBIGUOUS, the MTM branch, and a MACHINE or JUDGMENT closure. MACHINE stands only when the assertion cell names the actual check; an empty assertion next to MACHINE is rejected.
Every text artifact passes through qp_tools.sh mask and only the cleaned copy stays. A screenshot showing a filled password field survives only with the field masked in the image. A run is done when mtm-check and run-verbs both exit 0. The skill needs an adapter: Playwright MCP for web, computer-use MCP for desktop.
What it helps with
- execute the test cases from a written QA plan
- record a closed verdict for each step of a case
- tell a precondition block apart from a real app failure
- strip emails and tokens from evidence before saving
- keep stable screen ids for later regression runs
Example prompts for the agent
- Run these test cases against the site and write the verdicts
- Execute the QA plan against the app through Playwright MCP
- Drive the app through the TCs and mask secrets in the evidence
How to install qp-run
1Skills CLI (Vercel)
npx skills add chrono-meta/forge-harness --skill qp-run -a claude-code2Manually: put the files in place
~/.claude/skills/qp-run/SKILL.mdCopy the whole skill folder; for one project use .claude/skills/qp-run/
1Skills CLI (Vercel)
npx skills add chrono-meta/forge-harness --skill qp-run -a codex2Manually: put the files in place
.agents/skills/qp-run/SKILL.mdFor all projects use ~/.agents/skills/
1Skills CLI (Vercel)
npx skills add chrono-meta/forge-harness --skill qp-run -a cursor2Manually: put the files in place
.cursor/skills/qp-run/SKILL.mdCursor also reads .agents/skills/ and .claude/skills/
1GitHub CLI
gh skill install chrono-meta/forge-harness qp-run2Manually: put the files in place
.github/skills/qp-run/SKILL.md1Skills CLI (Vercel)
npx skills add chrono-meta/forge-harness --skill qp-run -a gemini-cli2Platform CLI
gemini skills install https://github.com/chrono-meta/forge-harness --path plugins/fh-qp/skills/qp-runManually: put the files in place
.devin/skills/qp-run/SKILL.mdThe legacy .windsurf/skills/ path still works
Skills CLI (Vercel)
npx skills add chrono-meta/forge-harness --skill qp-run -a opencodeClaude.ai
Zip the folder and upload it in Settings → Capabilities → Skills
Step by step, with commands for each platform: How to install skills in Claude Code: five ways
Questions about qp-run
How do I install qp-run in Claude Code?
Run npx skills add chrono-meta/forge-harness --skill qp-run -a claude-code. The skill lands at ~/.claude/skills/qp-run/SKILL.md.
What does qp-run do?
It executes the test cases from qp-plan against a live app, gives each step a verdict, and saves masked evidence with emails, tokens and passwords removed.
When is a step marked BLOCKED?
When step 1 fails under an unmet precondition, so the run never reached the screen. That is not a claim about the app, and only step 1 gets this label.
Source text
SKILL.md · MIT
---
name: qp-run
description: Automation stage of QP — executes the test cases from qp-plan against the live app through the session's adapter (Playwright MCP for web, computer-use MCP for desktop), records one verdict per step with a closed status, an MTM branch, whether the step was closed by machine evidence or left to judgment, and masks emails/tokens/passwords out of the evidence before saving. Triggers on "run these test cases", "execute the QA plan against the site", "drive the app through the TCs", "TC 실행해줘", "플랜대로 돌려줘".
user-invocable: true
allowed-tools: ["Read", "Bash", "Write", "Glob", "Grep"]
model: sonnet
origin: chamber run fh-qa-generic (2026-09-05)
---
# qp-run — Automation (execute → verdict → mask)
Input: `qp/plan/tcs.tsv` + the adapter/engine lines from `qp/plan/inventory.md`. Output dir: `qp/run/<YYYYMMDD-HHMM>/`.
## 0. MTM state — decide once, write it first
`#mtm:` is the first line of `verdicts.tsv`:
| value | when |
|---|---|
| `ACTIVE` | a plan document was given **and** you opened it (path recorded) |
| `UNAVAILABLE` | no plan document — expected values come from the inventory; the branch *differs from plan* is **unreachable** and the vocabulary collapses to AS_PLANNED / CODE_DIFFERS |
| `FAILED` | a plan document was given but could not be read — say so; do not fall back silently |
| `DISABLED` | the user turned it off (reason recorded) |
`UNAVAILABLE` and `FAILED` are different events; never merge them.
## 1. Precondition first — attribution, not blocking
Before step 1 of each TC, establish its `precondition`. Navigation you can do (`at:/about`) you do; state you cannot create (`logged-in`, `has-an-order`) you mark `UNMET_UNEXECUTABLE`. A step-1 failure under an unmet precondition is **`BLOCKED`**, not `FAIL` — you never reached the screen, so it is not a claim about the app. Only step 1 is re-attributed; later steps failed after arrival.
## 2. Execute each step, observe before and after
web: accessibility snapshot before → action → snapshot after. desktop: screenshot before/after. Save each observation as `<tc_id>_step<N>_{before,after}.{md,png}` and append its stable id from `bash plugins/fh-qp/scripts/qp_tools.sh screen-id <file>` (a content hash — Playwright MCP ref tokens change every navigation, so a raw file hash is NOT stable; measured 2026-09-05) to `evidence.tsv` (`tc_id<TAB>step<TAB>screen_hash`) — this file is what `qp-regress` reads.
## 3. Verdict row per step — `verdicts.tsv` (7 tab-separated fields)
```
tc_id status branch closure verb assertion expected_source
```
- `status` ∈ `PASS FAIL BLOCKED AMBIGUOUS` (AMBIGUOUS = could not map the step to one element: 0 candidates or a tie).
- `branch` ∈ `AS_PLANNED DIFFERS_FROM_PLAN CODE_DIFFERS NONE`. `DIFFERS_FROM_PLAN` only under `#mtm: ACTIVE` and only with `expected_source=PLAN_DOC`. `CODE_DIFFERS` needs `expected_source` `CODE` or `PLAN_DOC`. `NONE` only for BLOCKED/AMBIGUOUS.
- `closure` = `MACHINE` **only if** `assertion` names the check that closed it (`text-visible:About`, `url==…`, `element-count==3`). An empty assertion with `MACHINE` is invalid — that is the muscle-not-skeleton hole this field exists to close. Everything you decided by reading a screenshot is `JUDGMENT`.
- Validate before you finish: `bash plugins/fh-qp/scripts/qp_tools.sh mtm-check verdicts.tsv` must print `OK`; `run-verbs verdicts.tsv` must print `OK` (≥1 click/input actually executed).
## 4. Mask, then save — original 0
Every text artifact goes through `qp_tools.sh mask <in> <out>` and only `<out>` is kept; delete `<in>`. `RESIDUE` (rc 5) → do not keep the file; report which artifact. Typed secret values (from the profile's `*_env`) are read from the environment at type-time and never written. Screenshots that show a filled password field are kept only if the field is masked in the image (crop or overlay); otherwise drop the screenshot and say so.
## Done When
1. `verdicts.tsv` passes `mtm-check` and `run-verbs` · **[mandatory-pass]** — rc 0 both.
2. `evidence.tsv` has ≥1 row per executed TC · **[measured]**.
3. Masking: `grep -cE '@|Bearer|eyJ' <saved artifacts>` is 0 outside `__REDACTED_*__` markers · **[measured]** — the lane reproduces it on a known-dirty fixture.
4. Every BLOCKED verdict names its unmet precondition; every JUDGMENT closure says what was read · **[judged — pairing: `qp-regress` re-runs the same TCs and flags verdicts that flip without a DOM change; a second session reviews BLOCKED rows against the before-snapshots]**.
## Independently executable
Yes, given a `tcs.tsv` in the documented shape (hand-written is fine) and an adapter.
© chrono-meta · MIT · chrono-meta/forge-harness