diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json
index 0e8f577ff..835baaefb 100644
--- a/.claude-plugin/marketplace.json
+++ b/.claude-plugin/marketplace.json
@@ -82,10 +82,10 @@
{
"name": "aidd-qa",
"source": "./plugins/aidd-qa",
- "description": "Acceptance QA: validates observable behavior against acceptance criteria and records reviewer evidence. Browser is the only supported interface today.",
+ "description": "Acceptance QA: verify browser or CLI criteria and demonstrate journeys.",
"strict": true,
"metadata": {
- "recommended": false
+ "recommended": true
}
}
]
diff --git a/README.md b/README.md
index 362d9c5e1..c8c2e68d4 100644
--- a/README.md
+++ b/README.md
@@ -11,7 +11,7 @@ Unify **engineering teams** around **standardized workflows** and **shared best
๐งฑ **IDE agnostic** ยท ๐๏ธ **Legacy systems** ยท ๐ฑ **Token-optimized** ยท ๐ซ๐ท **Made in France**
- 9 plugins ยท 52 skills ยท 2 agents
+ 9 plugins ยท 53 skills ยท 2 agents
[](https://opensource.org/)
@@ -58,7 +58,7 @@ Why not just write your own commands? โ [FAQ](docs/FAQ.md#-why-aidd-instead-of
### Claude Code
-Installs the 6 stable plugins (`aidd-ui` is ๐ง alpha, `aidd-telemetry` ๐งช beta, and `aidd-qa` ๐ new, install those separately โ see [Plugins](#-plugins)).
+Installs the 7 recommended plugins (`aidd-ui` is ๐ง alpha and `aidd-telemetry` ๐งช beta, install those separately โ see [Plugins](#-plugins)).
**In the session** (slash commands)
@@ -70,6 +70,7 @@ Installs the 6 stable plugins (`aidd-ui` is ๐ง alpha, `aidd-telemetry` ๐งช be
/plugin install aidd-vcs@aidd-framework
/plugin install aidd-pm@aidd-framework
/plugin install aidd-orchestrator@aidd-framework
+/plugin install aidd-qa@aidd-framework
```
@@ -84,6 +85,7 @@ claude plugin install aidd-dev@aidd-framework
claude plugin install aidd-vcs@aidd-framework
claude plugin install aidd-pm@aidd-framework
claude plugin install aidd-orchestrator@aidd-framework
+claude plugin install aidd-qa@aidd-framework
```
@@ -324,9 +326,9 @@ Answers what a piece of work cost โ tokens, models, and which skill spent them
### ๐ฌ [aidd-qa](plugins/aidd-qa/README.md) ๐
-`1 skill` ยท **new**
+`2 skills` ยท **new**
-Acceptance QA โ locks browser scenarios from acceptance criteria and records reviewer evidence.
+Acceptance QA: verify browser or CLI criteria and demonstrate observed journeys.
diff --git a/aidd_docs/memory/architecture.md b/aidd_docs/memory/architecture.md
index 7ba45d316..498e69671 100644
--- a/aidd_docs/memory/architecture.md
+++ b/aidd_docs/memory/architecture.md
@@ -40,6 +40,6 @@ The concern-to-plugin taxonomy is canonical in [`docs/ARCHITECTURE.md`](../../do
## Gotchas
-- 9 plugins ship, 3 off the curated install path: `aidd-ui` is alpha, `aidd-telemetry` beta and opt-in, `aidd-qa` new and unproven outside this repository.
+- 9 plugins ship, 2 off the curated install path: `aidd-ui` is alpha, `aidd-telemetry` beta and opt-in.
- A skill never links outside itself: the tree ships both flat and as a marketplace, so no relative path survives both.
- Bundled hooks run Node. No `node` on `PATH`, no memory refresh and no run journal.
diff --git a/aidd_docs/memory/testing.md b/aidd_docs/memory/testing.md
index eb1b17575..477579d44 100644
--- a/aidd_docs/memory/testing.md
+++ b/aidd_docs/memory/testing.md
@@ -8,12 +8,12 @@ How the project is tested: the layers, the tools, and the conventions. Where tes
| Surface | Validated by |
| --- | --- |
-| Skills, agents, rules (markdown) | each action's own `## Test`, run end to end against a real project |
+| Skills, agents, rules (markdown) | static guards check structure; verify execution through host traces and artifacts from a frozen version |
| `scripts/` and bundled hooks | `node --test` under the wrapper below |
| `cli/` | vitest, four projects โ see the CLI bank |
| `kanban/` | its own vitest suite. It shares no code with `cli/` |
| Per-tool distributions | golden snapshots in `cli/tests/golden/`, mirrored by the `build-per-tool` CI matrix; Claude Code's own `plugin validate` over a fresh claude build, in `cli-ci.yml` |
-| Browser journeys | `aidd-qa:01-acceptance-qa`, see below |
+| Browser journeys | none in this repository |
## Tools
@@ -41,9 +41,3 @@ How the project is tested: the layers, the tools, and the conventions. Where tes
| `pnpm exec lefthook run pre-push` | the local gate before pushing |
CI runs more: `validate.yml` re-runs the whole pre-commit over the whole tree on every push and pull request, and `cli-ci.yml` adds jobs no local hook has. Both are in `deployment.md`.
-
-## Browser QA
-
-- Runner: `npx --yes @playwright/cli@0.1.17`, the framework pin. Never `latest` during QA.
-- Also required: `ffmpeg` and `ffprobe`. Output is WebM evidence per scenario.
-- Owned by `aidd-qa:01-acceptance-qa`; this repository ships the capability, it has no browser journey of its own.
diff --git a/aidd_docs/tasks/2026_10/2026_10_12_qa-verify-and-demo/phase-1.md b/aidd_docs/tasks/2026_10/2026_10_12_qa-verify-and-demo/phase-1.md
new file mode 100644
index 000000000..e7ce6ecdb
--- /dev/null
+++ b/aidd_docs/tasks/2026_10/2026_10_12_qa-verify-and-demo/phase-1.md
@@ -0,0 +1,21 @@
+---
+status: done
+---
+
+# Instruction: split
+
+## Architecture projection
+
+```txt
+plugins/aidd-qa/
+โโโ .claude-plugin/plugin.json โ๏ธ lists 02-demo
+โโโ skills/
+ โโโ 01-acceptance-qa/ โ๏ธ verify only: no recording, encoding, ffmpeg
+ โโโ 02-demo/ โ
the recording rules, moved unchanged
+```
+
+## Acceptance criteria
+
+- Verify writes `qa.md` with no video, and needs no `ffmpeg`.
+- On the golden fixtures, verdicts by criterion, run verdict, out-of-interface and rejected sets match `next`.
+- The demo records a `qa.md` scenario with the moved rules: VP9, 1280 wide, at most 12 s.
diff --git a/aidd_docs/tasks/2026_10/2026_10_12_qa-verify-and-demo/phase-2.md b/aidd_docs/tasks/2026_10/2026_10_12_qa-verify-and-demo/phase-2.md
new file mode 100644
index 000000000..b5715c2fe
--- /dev/null
+++ b/aidd_docs/tasks/2026_10/2026_10_12_qa-verify-and-demo/phase-2.md
@@ -0,0 +1,25 @@
+---
+status: done
+---
+
+# Instruction: verify-rules
+
+## Architecture projection
+
+```txt
+plugins/aidd-qa/skills/01-acceptance-qa/ โ๏ธ one clause per rule, where the decision is made
+```
+
+## Acceptance criteria
+
+- One happy path per journey: a spec with two journeys runs both.
+- A CLI criterion is verified by its command and expected output.
+- Invoked by another skill or agent, it never asks: a missing decision yields `blocked` with the question.
+- Test-only storage is proven before any start, by memory, an isolated fixture, or the user; an environment name is not proof.
+- A crashed or UI-less entry gets one documented fallback, else `blocked`.
+- Viewport and permissions come from the criterion, then `Browser QA` memory, then the default.
+- A vague criterion becomes one observable condition, else it is rejected.
+- A timed boundary is probed on both sides; measured values reach `Actual`.
+- A short transition is measured inside `run-code`.
+- Destructive means data outside the scenario's fixture or an irreversible external effect.
+- `qa.md` holds `Limitations`; a limitation that weakens proof yields `blocked`.
diff --git a/aidd_docs/tasks/2026_10/2026_10_12_qa-verify-and-demo/phase-3.md b/aidd_docs/tasks/2026_10/2026_10_12_qa-verify-and-demo/phase-3.md
new file mode 100644
index 000000000..9f51c3361
--- /dev/null
+++ b/aidd_docs/tasks/2026_10/2026_10_12_qa-verify-and-demo/phase-3.md
@@ -0,0 +1,19 @@
+---
+status: done
+---
+
+# Instruction: demo
+
+## Architecture projection
+
+```txt
+plugins/aidd-qa/skills/02-demo/ โ๏ธ browser video and CLI transcript
+```
+
+## Acceptance criteria
+
+- A browser journey yields one video; a CLI journey yields the command and its output.
+- A failing journey is shown as observed and labelled not expected.
+- Invoked by another skill or agent, it asks nothing and returns its paths.
+- A video lands in the folder the caller names, else beside the journey's source, never in a temporary directory.
+- A video is named after its journey, never `happy-path` for a bug.
diff --git a/aidd_docs/tasks/2026_10/2026_10_12_qa-verify-and-demo/phase-4.md b/aidd_docs/tasks/2026_10/2026_10_12_qa-verify-and-demo/phase-4.md
new file mode 100644
index 000000000..a9b280c22
--- /dev/null
+++ b/aidd_docs/tasks/2026_10/2026_10_12_qa-verify-and-demo/phase-4.md
@@ -0,0 +1,17 @@
+---
+status: done
+---
+
+# Instruction: sdlc-wiring
+
+## Architecture projection
+
+```txt
+plugins/aidd-orchestrator/skills/01-sdlc/references/03-check.md โ๏ธ QA after review, demo for the pull request
+plugins/aidd-dev/agents/checker.md โ๏ธ may invoke QA and demo
+.claude-plugin/marketplace.json, README.md โ๏ธ aidd-qa on the recommended path
+```
+
+## Acceptance criteria
+
+- #919's acceptance criteria.
diff --git a/aidd_docs/tasks/2026_10/2026_10_12_qa-verify-and-demo/plan.md b/aidd_docs/tasks/2026_10/2026_10_12_qa-verify-and-demo/plan.md
new file mode 100644
index 000000000..6e123ca87
--- /dev/null
+++ b/aidd_docs/tasks/2026_10/2026_10_12_qa-verify-and-demo/plan.md
@@ -0,0 +1,30 @@
+---
+objective: "Acceptance QA verifies browser and CLI criteria without video, a demo skill shows any journey, and the SDLC runs both before the draft pull request."
+status: implemented
+---
+
+# Plan: QA verifies, demo shows
+
+## Overview
+
+| Field | Value |
+| ---------- | ----- |
+| **Goal** | Split `aidd-qa` into verify and show, harden verify for real journeys, wire both into the SDLC |
+| **Source** | #918 (agreed contract in its body), #919 |
+
+## Phases
+
+| # | Phase | File |
+| --- | ----------------- | ---------------------------- |
+| 1 | split | [`phase-1.md`](./phase-1.md) |
+| 2 | verify-rules | [`phase-2.md`](./phase-2.md) |
+| 3 | demo | [`phase-3.md`](./phase-3.md) |
+| 4 | sdlc-wiring | [`phase-4.md`](./phase-4.md) |
+
+## Decisions
+
+| Decision | Why |
+| --- | --- |
+| Verify carries no media; the demo owns it | a verdict is the step results; a missing `ffmpeg` must not block it |
+| The demo never decides where its artifact goes | the caller (pull request, defect, user) does |
+| Golden is verdicts by criterion, not bytes | an LLM run varies in wording, never in what it proves |
diff --git a/docs/CATALOG.md b/docs/CATALOG.md
index a32432450..d0ec95b67 100644
--- a/docs/CATALOG.md
+++ b/docs/CATALOG.md
@@ -10,7 +10,7 @@ The exhaustive list of AIDD plugins, skills, and actions. Skills are invoked thr
- [aidd-orchestrator](#-aidd-orchestrator) - async orchestration (optional)
- [aidd-ui](#-aidd-ui) - UI / UX (๐ง alpha, not ready)
- [aidd-telemetry](#-aidd-telemetry) - measurement, hooks and skills (๐งช beta, off the curated path)
-- [aidd-qa](#-aidd-qa) - acceptance QA (๐ new, off the curated path)
+- [aidd-qa](#-aidd-qa) - acceptance QA (๐ new)
---
@@ -125,8 +125,9 @@ CLI, and each skill says so before doing anything else if it is missing.
## ๐ฌ aidd-qa
-Locks a scenario scope from acceptance criteria, runs it against a reviewed candidate, and hands back a per-scenario verdict with recorded evidence. Browser only, for now.
+Verify browser or CLI criteria; demonstrate observed journeys.
| Skill | Role | Actions |
| ------------------- | --------------------------------------------------------------------- | --------------------------------------------------------------------- |
-| `01-acceptance-qa` | Lock scenarios from acceptance criteria, run them, and report a verdict per scenario | `01-load-scope`, `02-prerequisites`, `03-prepare-run`, `04-run-scenarios` |
+| `01-acceptance-qa` | Verify criteria into an acceptance report | `01-load-scope`, `02-prerequisites`, `03-prepare-run`, `04-run-scenarios` |
+| `02-demo` | Show a journey as video or transcript | `01-record` |
diff --git a/plugins/aidd-dev/agents/checker.md b/plugins/aidd-dev/agents/checker.md
index 0122b6033..5dedb3c04 100644
--- a/plugins/aidd-dev/agents/checker.md
+++ b/plugins/aidd-dev/agents/checker.md
@@ -14,7 +14,7 @@ You are the checker. Your job is to judge finished work against its validator an
- Run the checklist on every code or diff, leaving no item unchecked.
- Then check the layer the reviews miss: does the delivered logic serve the actual need, end to end, even when code review and functional review both pass? Name any gap between intent and result.
- Demand command output or file evidence, never bare claims. Lean strict: a false alarm costs less than a missed defect.
-- When a review skill fits the work, run it and let it write its report; that report is your deliverable and your judgment is what fills it. Never hand-write a parallel prose review beside it.
+- Run the requested skill, otherwise a fitting review skill. Let it write its report; never hand-write a parallel review.
- Return your verdict, findings, and score on top. Hold yourself accountable for whatever you pass.
# Checklist
@@ -43,3 +43,5 @@ This is the behavioral baseline. Apply it to every code or diff, and extend it w
- `/aidd-dev:05-review`
- `/aidd-dev:04-audit`
- `/aidd-refine:02-challenge`
+- `/aidd-qa:01-acceptance-qa`
+- `/aidd-qa:02-demo`
diff --git a/plugins/aidd-orchestrator/skills/01-sdlc/SKILL.md b/plugins/aidd-orchestrator/skills/01-sdlc/SKILL.md
index 3083149eb..3a22ed142 100644
--- a/plugins/aidd-orchestrator/skills/01-sdlc/SKILL.md
+++ b/plugins/aidd-orchestrator/skills/01-sdlc/SKILL.md
@@ -8,7 +8,7 @@ argument-hint: request | interactive request
## Behavior
-Decide the mode as `mode.md` says, then read only it and the current zone's reference. Verify that every named provider is installed before calling it. A zone that cannot proceed stops the run and says what it would take to resume.
+Decide the mode as `mode.md` says, then read only it and the current zone's reference. Verify every required provider is available through the host before calling it. A zone that cannot proceed stops the run and says what it would take to resume.
Spawn specialized agents for isolated work. Parallelize independent work when it is faster. Give each agent one focused task that a smaller model can execute. Repeat the responsible zone when delegated work returns an actionable gap.
diff --git a/plugins/aidd-orchestrator/skills/01-sdlc/references/03-check.md b/plugins/aidd-orchestrator/skills/01-sdlc/references/03-check.md
index ca5657884..bd5c47373 100644
--- a/plugins/aidd-orchestrator/skills/01-sdlc/references/03-check.md
+++ b/plugins/aidd-orchestrator/skills/01-sdlc/references/03-check.md
@@ -2,8 +2,7 @@
## Behavior
-- Use one fresh checker, independent from implementation, to review the candidate against the contract, plan, and validation evidence.
-- After the review clears, the same checker challenges whether the real outcome is trustworthy and serves the user.
+Use one fresh, independent `@aidd-dev:checker` throughout the diagram, one skill per call.
## Findings
@@ -17,7 +16,7 @@ A checker's finding is routed by what it is about. A refusal the user gives at a
## Handoff
-- Commit whatever this zone wrote, then open the draft pull request when the checker returns no actionable finding.
+- Commit whatever this zone wrote, then open the draft pull request with the report and artifact paths when the checker returns no actionable finding.
```mermaid
---
@@ -32,6 +31,9 @@ flowchart TD
ValidationReports["$validation_reports"]
Checker(["@aidd-dev:checker"])
Review["/aidd-dev:05-review"]
+ QA["/aidd-qa:01-acceptance-qa"]
+ QAReport["$qa.md"]
+ Demo["/aidd-qa:02-demo"]
end
subgraph ChallengeStage["Challenge the outcome"]
@@ -45,6 +47,7 @@ flowchart TD
Frame["01 Frame"]
Todo["/aidd-dev:10-todo"]
Deliver["02 Deliver"]
+ Stop["Blocker + resume action"]
end
subgraph ShipStage["Ship the reviewed candidate"]
@@ -59,7 +62,14 @@ flowchart TD
ValidationReports --> Checker
Checker --> Review
Review -- "Return actionable review findings." --> Findings
- Review -- "When the review clears, challenge the outcome." --> Challenge
+ Review -- approve --> QA
+ QA -- pass --> QAReport
+ QAReport -- "Read passing scenarios." --> Demo
+ Demo --> Challenge
+ QA -- skipped --> Challenge
+ QA -- fail --> Deliver
+ QA -- "blocked: criteria" --> Frame
+ QA -- "blocked: execution" --> Stop
Challenge -- "Return actionable challenge findings." --> Findings
Challenge -- "When the outcome is trustworthy, open the draft pull request." --> PullRequest
Findings -- "Use product findings as the next Frame source." --> Frame
@@ -73,8 +83,8 @@ flowchart TD
classDef artifact fill:#DCFCE7,stroke:#16A34A,color:#14532D,stroke-width:2px
classDef zone fill:#F1F5F9,stroke:#64748B,color:#0F172A,stroke-width:2px
- class Review,Challenge,Todo,PullRequest skill
+ class Review,QA,Demo,Challenge,Todo,PullRequest skill
class Checker agent
- class Contract,Plan,CommittedCandidate,ValidationReports,Findings,PullRequestUrl artifact
- class Frame,Deliver zone
+ class Contract,Plan,CommittedCandidate,ValidationReports,QAReport,Findings,PullRequestUrl artifact
+ class Frame,Deliver,Stop zone
```
diff --git a/plugins/aidd-qa/.claude-plugin/plugin.json b/plugins/aidd-qa/.claude-plugin/plugin.json
index 6e94ad5cd..7e707fb00 100644
--- a/plugins/aidd-qa/.claude-plugin/plugin.json
+++ b/plugins/aidd-qa/.claude-plugin/plugin.json
@@ -2,13 +2,14 @@
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
"name": "aidd-qa",
"version": "1.0.0",
- "description": "Acceptance QA: validates observable behavior against acceptance criteria and records reviewer evidence. Use when a reviewed candidate needs evidence that it meets its acceptance criteria. Do NOT use for diff review, unit or integration tests, or fixing the application.",
+ "description": "Verify browser or CLI acceptance criteria and demonstrate journeys with observed evidence.",
"author": {
"name": "AI-Driven Dev",
"url": "https://github.com/ai-driven-dev"
},
"skills": [
- "./skills/01-acceptance-qa"
+ "./skills/01-acceptance-qa",
+ "./skills/02-demo"
],
"keywords": [
"qa",
diff --git a/plugins/aidd-qa/CATALOG.md b/plugins/aidd-qa/CATALOG.md
index 189426589..8d687f910 100644
--- a/plugins/aidd-qa/CATALOG.md
+++ b/plugins/aidd-qa/CATALOG.md
@@ -9,6 +9,7 @@ Auto-generated index of skills, agents, references and assets shipped by the `ai
- [`.claude-plugin`](#claude-plugin)
- [`skills`](#skills)
- [`skills/01-acceptance-qa`](#skills01-acceptance-qa)
+ - [`skills/02-demo`](#skills02-demo)
---
@@ -30,5 +31,13 @@ Auto-generated index of skills, agents, references and assets shipped by the `ai
| `actions` | [04-run-scenarios.md](skills/01-acceptance-qa/actions/04-run-scenarios.md) | - |
| `assets` | [qa-report-template.md](skills/01-acceptance-qa/assets/qa-report-template.md) | - |
| `references` | [interface-browser-playwright-cli.md](skills/01-acceptance-qa/references/interface-browser-playwright-cli.md) | - |
-| `-` | [SKILL.md](skills/01-acceptance-qa/SKILL.md) | `Validate a reviewed candidate's observable behavior against its acceptance criteria and record short named videos as reviewer evidence. Use when acceptance criteria exist and a browser-observable journey needs proof it holds. Do NOT use for diff review, unit or integration tests, or application fixes.` |
+| `-` | [SKILL.md](skills/01-acceptance-qa/SKILL.md) | `Prove reviewed browser or command journeys against acceptance criteria. Use for acceptance QA, not diff review, unit or integration tests, or application fixes.` |
+
+#### `skills/02-demo`
+
+| Group | File | Description |
+|-------|------|---|
+| `actions` | [01-record.md](skills/02-demo/actions/01-record.md) | - |
+| `references` | [interface-browser-playwright-cli.md](skills/02-demo/references/interface-browser-playwright-cli.md) | - |
+| `-` | [SKILL.md](skills/02-demo/SKILL.md) | `Show a journey or bug through a browser video or CLI transcript. Use for reviewer evidence, not acceptance verification or fixes.` |
diff --git a/plugins/aidd-qa/README.md b/plugins/aidd-qa/README.md
index 575ce2064..8d2dac41b 100644
--- a/plugins/aidd-qa/README.md
+++ b/plugins/aidd-qa/README.md
@@ -2,16 +2,13 @@
# aidd-qa
-Acceptance QA concern for the AI-Driven Development framework.
+Verify browser or CLI acceptance criteria into `qa.md`; demonstrate journeys with a video or transcript.
-> Status: new, off the curated install path until proven.
-
-First time? Install with `/plugin install aidd-qa@aidd-framework`, then run `aidd-qa:01-acceptance-qa`.
-
-Validates a reviewed candidate's observable behavior against its acceptance criteria and records reviewer evidence. Scenarios come only from acceptance criteria, never from the diff or the source code. Browser is the only supported interface today; a scenario without a browser-observable outcome is listed out of interface rather than tested.
+Install: `/plugin install aidd-qa@aidd-framework`.
## Skills
| Skill | Description |
|---|---|
-| [acceptance-qa](skills/01-acceptance-qa/SKILL.md) | Validate a reviewed candidate against its acceptance criteria and record one short named video per locked scenario. |
+| [acceptance-qa](skills/01-acceptance-qa/SKILL.md) | Verify a reviewed candidate against its criteria. |
+| [demo](skills/02-demo/SKILL.md) | Show a journey's observed outcome. |
diff --git a/plugins/aidd-qa/skills/01-acceptance-qa/SKILL.md b/plugins/aidd-qa/skills/01-acceptance-qa/SKILL.md
index 6d4f83e31..fea8afcab 100644
--- a/plugins/aidd-qa/skills/01-acceptance-qa/SKILL.md
+++ b/plugins/aidd-qa/skills/01-acceptance-qa/SKILL.md
@@ -1,32 +1,24 @@
---
name: 01-acceptance-qa
-description: Validate a reviewed candidate's observable behavior against its acceptance criteria and record short named videos as reviewer evidence. Use when acceptance criteria exist and a browser-observable journey needs proof it holds. Do NOT use for diff review, unit or integration tests, or application fixes.
+description: Prove reviewed browser or command journeys against acceptance criteria. Use for acceptance QA, not diff review, unit or integration tests, or application fixes.
argument-hint: acceptance criteria | reviewed candidate
---
# Acceptance QA
-```mermaid
-flowchart LR
- scope["load-scope"] --> prerequisites["prerequisites"] --> prepare["prepare-run"] --> run["run-scenarios"] --> report["qa.md"]
- scope -- skipped --> report
-```
-
## Actions
-Read only the next action's file before running it.
+Run actions in order; read only the next action's file.
-| # | Action | Does |
-| --- | --------------- | ---------------------------------------------------------- |
-| 01 | `load-scope` | Lock at most one happy path and the edge cases its acceptance criteria name |
-| 02 | `prerequisites` | Verify the browser runner and media dependencies |
-| 03 | `prepare-run` | Resolve the shortest deterministic path to executable runs |
-| 04 | `run-scenarios` | Record, normalize, verify, reset, and report every scenario |
+| Action | Does |
+| --- | --- |
+| [load-scope](actions/01-load-scope.md) | select criterion-mapped scenarios |
+| [prerequisites](actions/02-prerequisites.md) | verify the runner |
+| [prepare-run](actions/03-prepare-run.md) | prepare isolated runs |
+| [run-scenarios](actions/04-run-scenarios.md) | execute and report |
## Transversal rules
-- Run against a reviewed change and never patch the application.
-- Never delete data not proven test-only.
-- Never derive a scenario from the diff or the source code; every scenario traces to an acceptance criterion.
-- Never spawn agents. Batch independent reads and tool checks, but keep state-changing browser work sequential.
-- Do not narrate action transitions, searches, fixtures, selectors, or successful checks. Report only a blocker, a required decision, or the final verdict and paths.
+- Never patch the application or spawn agents; serialize mutations.
+- Invoked by another skill or agent, return `blocked` with any pending question; never ask.
+- Write every run's report to `/qa.md`, using [the report template](assets/qa-report-template.md), even when blocked.
diff --git a/plugins/aidd-qa/skills/01-acceptance-qa/actions/01-load-scope.md b/plugins/aidd-qa/skills/01-acceptance-qa/actions/01-load-scope.md
index 94f0fad70..ad852bfc7 100644
--- a/plugins/aidd-qa/skills/01-acceptance-qa/actions/01-load-scope.md
+++ b/plugins/aidd-qa/skills/01-acceptance-qa/actions/01-load-scope.md
@@ -1,31 +1,20 @@
# 01 - Load Scope
-Lock the smallest defensible acceptance QA scope before execution.
-
## Input
-Acceptance criteria (issue, spec, user story, or the user) and the reviewed candidate reference (branch, commit, or running URL).
+Acceptance criteria and reviewed candidate (branch, commit, URL).
## Output
-- Scope: at most 1 happy path and its edge cases, each with the criteria it proves.
-- Out of interface: criteria, or quoted parts, with no browser-observable outcome.
-- Rejected: scenario, criterion, reason.
-- Evidence folder, source label (criteria path), and candidate reference.
+Criterion-mapped scenarios, exclusions and rejections; source, candidate and evidence folder.
## Process
-1. **Resolve.** Take criteria from the issue, spec, story, or user; never a plan. Note the candidate reference.
-2. **Filter.** Keep browser-observable criteria; split a partial one and quote the rest out of interface. Never test by reading code.
-3. **Locate.** Use the source's feature folder, else `aidd_docs/tasks//_/`.
-4. **Skip.** Nothing kept: write `qa.md` from [qa-report-template.md](../assets/qa-report-template.md) with verdict `skipped`, then stop.
-5. **Lock.** 1 happy path from the primary journey; edge cases only when a criterion names them. An edge from the diff, code, tests, or an unmapped plan edge is never a candidate. Ask once when journeys conflict or an edge is external or destructive.
-6. **Validate.** Reject a scenario missing a trigger or an observable outcome; keep the reason.
-7. **Show.** Evidence folder, happy path, `Edge case | Criteria | Decision` table, Out of interface and Rejected when non-empty. Never repeat steps.
+1. **Resolve.** Read acceptance criteria from the supplied source, never plans, diffs or code. Save evidence beside the criteria file; otherwise in `aidd_docs/tasks//_/`.
+2. **Filter.** Keep browser or CLI criteria. Quote other criteria, including API-only and code inspection, in [Out of interface](../assets/qa-report-template.md#out-of-interface); these exclusions do not block QA. None remain => report `skipped`; stop.
+3. **Select.** One happy path per journey and criterion-named edges only. Ask for conflicting journeys or effects outside fixtures or irreversible effects; verified fixture teardown is not destructive.
+4. **Validate.** Define one observable condition for vague outcomes. Reject missing triggers or unresolvable outcomes with reasons; show scope only for a decision.
## Test
-- Every scenario traces to a criterion; none comes from a plan, the diff, code, or tests.
-- An unobservable criterion or part is quoted out of interface, never scoped.
-- Nothing kept => `qa.md` with `skipped`, and no later action runs.
-- A rejection keeps its reason; an unmapped plan edge is never listed as rejected.
+Every scenario traces to a criterion.
diff --git a/plugins/aidd-qa/skills/01-acceptance-qa/actions/02-prerequisites.md b/plugins/aidd-qa/skills/01-acceptance-qa/actions/02-prerequisites.md
index dee1bf07f..2669d4de7 100644
--- a/plugins/aidd-qa/skills/01-acceptance-qa/actions/02-prerequisites.md
+++ b/plugins/aidd-qa/skills/01-acceptance-qa/actions/02-prerequisites.md
@@ -1,28 +1,17 @@
# 02 - Prerequisites
-Verify the runner dependencies once the scope is locked.
-
## Input
The locked scope.
## Output
-Verified `npx`, Playwright CLI `0.1.17`, `ffmpeg`, and `ffprobe`.
+Verified browser runner, when needed.
## Process
-1. **Check.** In one pass, resolve `npx`, run `npx --yes @playwright/cli@0.1.17 --version`, and resolve `ffmpeg` and `ffprobe`.
-2. **Continue.** When every check passes, continue without reporting it.
-3. **Resolve.** When a dependency is missing, ask one concise question: the user installs the listed dependencies, or authorizes you to install them now.
- - If the user installs them, provide only the shortest platform-appropriate commands and stop until they confirm completion.
- - If authorized, install only the missing dependencies, then rerun every check.
-4. **Stop.** Report the shortest decisive error when installation is declined or a recheck fails.
-5. **Protect.** Never add runner or media dependencies to the application manifest.
+Browser scenarios only: check `npx --offline --no -- @playwright/cli@0.1.17 --version`. Missing dependencies => report the error; install only with authorization, then recheck. Never add runner dependencies to application manifests.
## Test
-- Passing checks produce no message.
-- A missing dependency produces one choice and no unapproved installation.
-- Either installation path reruns every check before continuing.
-- No application dependency file changes.
+The runner executes successfully.
diff --git a/plugins/aidd-qa/skills/01-acceptance-qa/actions/03-prepare-run.md b/plugins/aidd-qa/skills/01-acceptance-qa/actions/03-prepare-run.md
index f2f9f495d..0d8514ee1 100644
--- a/plugins/aidd-qa/skills/01-acceptance-qa/actions/03-prepare-run.md
+++ b/plugins/aidd-qa/skills/01-acceptance-qa/actions/03-prepare-run.md
@@ -1,28 +1,20 @@
# 03 - Prepare Run
-Make every scenario executable before recording.
-
## Input
Verified prerequisites and the locked scope.
## Output
-Per scenario: fixture, initial URL, minimal steps, expected outcome, proven teardown, and isolated session id. Plus every scenario rejected here, with its reason.
+Executable scenarios or rejection reasons.
## Process
-1. **Reuse.** Resolve entry, auth, fixtures, and reset from the `Browser QA` section of `aidd_docs/memory/testing.md` when it exists, then a related browser test, then one targeted snapshot. Stop once executable.
- - Before starting the entry, prove the storage it mounts is test-only when its reset deletes data. Unproven: ask once, never guess.
-2. **Preflight.** Check the application and fixed `1280ร720` viewport.
-3. **Authenticate.** Establish the role before recording; never show login discovery or secrets in evidence.
-4. **Fixture.** Use deterministic data per setup; never pick a live record by guesswork.
-5. **Rehearse.** Only non-mutating steps and selectors; never the final state-changing action.
-6. **Reset.** Resolve a verified, executable teardown per state-changing scenario; without one, reject the scenario and carry it forward with its reason. If preparation changed state, run the teardown and verify the baseline now, not at a restart.
+1. **Resolve.** Entry, auth, deterministic fixtures and teardown come from `aidd_docs/memory/testing.md`, then a related test (`Browser QA` for browser scenarios). Never invent commands or guess live data.
+2. **Prove.** Any data-deleting reset, even unused: prove its storage test-only from memory, isolated fixture/config or user confirmation, never inference. Without proof => `blocked` before any start or snapshot.
+3. **Browser.** Read [the runner reference](../references/interface-browser-playwright-cli.md), start the entry and inspect missing setup with one snapshot. Failed/UI-less entry: one documented fallback, else `blocked`. Viewport/permissions: criterion, memory, then `1280ร720`/browser defaults; missing mobile dimensions need a decision.
+4. **Reset.** Reject mutations without documented, verified teardown. Undo preparation mutations and verify baseline. Rehearse only non-mutating steps; keep auth discovery and secrets out of reports.
## Test
-- An entry on storage not proven test-only is never started when its reset deletes data; it produces one question.
-- A state-changing scenario without a verified teardown is rejected with its reason.
-- No login discovery, secret, or guessed live record appears in evidence.
-- Preparation that changed state is torn down and verified before the run is ready.
+Each retained scenario is reproducible without risking live data.
diff --git a/plugins/aidd-qa/skills/01-acceptance-qa/actions/04-run-scenarios.md b/plugins/aidd-qa/skills/01-acceptance-qa/actions/04-run-scenarios.md
index 99867f18d..d547f679a 100644
--- a/plugins/aidd-qa/skills/01-acceptance-qa/actions/04-run-scenarios.md
+++ b/plugins/aidd-qa/skills/01-acceptance-qa/actions/04-run-scenarios.md
@@ -1,31 +1,20 @@
# 04 - Run Scenarios
-Record, verify, and report one clean take per scenario.
-
## Input
-The prepared run and its rejections; from load-scope, the source label, candidate, evidence folder, Out of interface, and Rejected.
+Prepared scenarios and scope.
## Output
-`/qa.md` and one final WebM per scenario that ran.
+`/qa.md`.
## Process
-1. **Group.** Steps 1-7 cover executable scenarios only. At most two read-only ones concurrently, in isolated sessions; state-changing ones sequentially.
-2. **Record.** Apply setup, then follow [interface-browser-playwright-cli.md](../references/interface-browser-playwright-cli.md).
-3. **Verdict.** From each step's `expected`, `actual`, `ok`: any `ok: false` => `fail`, else `pass`. Keep the take of every `fail`.
-4. **Recover.** A setup failure, throw, or non-zero exit is operational: discard the take, reset, retry once; a second one => `blocked`.
-5. **Reset.** After each state-changing take, run its verified teardown, verify the baseline, then close the session.
-6. **Normalize.** At most two independent raw files concurrently. Save only `qa/happy-path.webm` and `qa/edge-case-.webm` once `ffprobe` and frame inspection pass; record each final file's duration from `ffprobe`.
-7. **Clean.** Delete raw takes and validation frames only after every final file passes codec, dimension, duration, path, cut-point, and frame checks. Never retain screenshots or alternate media.
-8. **Report.** Fill [qa-report-template.md](../assets/qa-report-template.md), even when every scenario was rejected: one row per scenario (evidence `none` when `blocked`), Out of interface and Rejected (`none` when empty), Findings only for `fail` or `blocked`. Run verdict, in order: any `fail` => `fail`; any `blocked` or rejected => `blocked`; else `pass`. `skipped` belongs to load-scope only.
-9. **Return.** Output the verdict and evidence paths; when `qa/happy-path.webm` exists, ask `Open happy-path.webm in the browser for review?` and open it when confirmed.
+1. **Run.** Apply setup and compare observed behavior to criteria only. CLI: record stdout and exit status. A mismatch => `fail`, else `pass`.
+2. **Recover.** Setup failure, throw or unexpected non-zero exit: teardown, retry once; repeated failure => `blocked`.
+3. **Reset.** Teardown mutations, verify baseline, close browser sessions.
+4. **Report.** Record the observable condition and evidence per criterion, all rejections and limitations, even if nothing ran. Verdict: `fail` > `blocked` or rejected > `pass`; proof-weakening limitations => `blocked`. Findings only for `fail`/`blocked`. Return verdict and path.
## Test
-- Each row names its criteria, expected, actual, verdict (`pass`, `fail`, or `blocked`), duration, and evidence.
-- Run verdict: `fail` over `blocked` or rejected over `pass`; a `pass` is never reported without the Out of interface list.
-- A product mismatch yields `fail` from the returned results, never a thrown take; a second operational failure yields `blocked`.
-- Raw takes and frames survive until every final file passes; then only `qa/happy-path.webm` and `qa/edge-case-.webm` remain beside `qa.md`.
-- Every earlier rejection appears in the report with its reason, even when it empties the executable set, and the report still runs.
+Every scoped criterion has evidence or a blocker.
diff --git a/plugins/aidd-qa/skills/01-acceptance-qa/assets/qa-report-template.md b/plugins/aidd-qa/skills/01-acceptance-qa/assets/qa-report-template.md
index a36355ceb..455c32941 100644
--- a/plugins/aidd-qa/skills/01-acceptance-qa/assets/qa-report-template.md
+++ b/plugins/aidd-qa/skills/01-acceptance-qa/assets/qa-report-template.md
@@ -1,21 +1,25 @@
# Acceptance QA: {{feature}}
-- **Verdict**: {{pass | fail | blocked | skipped}}
-- **Source**: {{acceptance criteria path}}
-- **Candidate**: {{branch | commit | running URL}}
-- **Run**: {{yyyy_mm_dd}}
+- **Verdict**: {{verdict}}
+- **Source**: {{criteria source}}
+- **Candidate**: {{candidate}}
+- **Run**: {{date}}
## Out of interface
-{{criteria, or quoted parts, with no browser-observable outcome, or "none"}}
+{{quoted exclusions, or "none"}}
## Scenarios
-| Scenario | Criteria | Expected | Actual | Verdict | Duration | Evidence |
-| -------- | --------- | -------- | ------ | ------- | -------- | -------- |
+| Scenario | Criteria | Expected | Actual | Verdict |
+| -------- | -------- | -------- | ------ | ------- |
## Rejected
-{{scenarios rejected in load-scope or prepare-run, criterion โ reason, or "none"}}
+{{criterion, reason; or "none"}}
-{{findings-section-when-needed}}
+## Limitations
+
+{{limitations, or "none"}}
+
+{{findings}}
diff --git a/plugins/aidd-qa/skills/01-acceptance-qa/references/interface-browser-playwright-cli.md b/plugins/aidd-qa/skills/01-acceptance-qa/references/interface-browser-playwright-cli.md
index 0f4535309..e753c0fcc 100644
--- a/plugins/aidd-qa/skills/01-acceptance-qa/references/interface-browser-playwright-cli.md
+++ b/plugins/aidd-qa/skills/01-acceptance-qa/references/interface-browser-playwright-cli.md
@@ -1,100 +1,9 @@
-# Playwright CLI runner
-
-Playwright Agent CLI produces WebM evidence without becoming an application dependency.
-
-## Version and invocation
-
-Support `@playwright/cli >= 0.1.17`; execute the framework pin `@playwright/cli@0.1.17`. Upgrade it deliberately, never through `latest` during QA.
+# Browser runner
```bash
-npx --yes @playwright/cli@0.1.17 -s=qa-
+cd && npx --offline --no -- @playwright/cli@0.1.17 -s=qa-
```
-Run every command from a temporary directory outside the application repository.
-
-`run-code` takes one `page` argument: pass `async page => { ... }`, never bare statements. Never throw on a product mismatch: check each expected outcome with a bounded `waitFor`, and return `{ step, expected, actual, ok }` per step. Keep stdout, stderr, and exit status visible; no redirects, pipes, command substitutions, or `|| true` in runner commands. A throw, `SyntaxError`, or non-zero exit is a tooling failure and invalidates the take.
-
-## Recording
-
-- One named session and raw WebM per scenario; viewport and frame `1280ร720`.
-- Reach the initial state before `video-start`; put every recorded interaction in one `run-code`.
-- Never record separate click, fill, or scroll commands.
-- Hold the initial and verified final states for `1000 ms`.
-- Wait `300 ms` between actions and after each scroll.
-- Run `video-stop` only after `run-code` succeeds.
-
-```bash
-npx --yes @playwright/cli@0.1.17 -s=qa-- resize 1280 720
-npx --yes @playwright/cli@0.1.17 -s=qa-- video-start raw-.webm --size=1280x720
-# Replace the locators; omit the scroll block when irrelevant.
-npx --yes @playwright/cli@0.1.17 -s=qa-- run-code 'async page => {
- const pause = milliseconds => page.waitForTimeout(milliseconds);
- await pause(1000);
- await page.getByRole("button", { name: "first action" }).click();
- await pause(300);
- await page.mouse.wheel(0, 600);
- await pause(300);
- await page.getByRole("button", { name: "final action" }).click();
- const expected = "observable expected outcome";
- const ok = await page.getByText(expected).waitFor({ state: "visible", timeout: 5000 }).then(() => true, () => false);
- await pause(1000);
- return [{ step: "final action", expected, actual: ok ? expected : "not visible", ok }];
-}'
-npx --yes @playwright/cli@0.1.17 -s=qa-- video-stop
-# Run the teardown and verify the baseline before closing.
-npx --yes @playwright/cli@0.1.17 -s=qa-- close
-```
-
-`video-stop` writes the raw file to the current directory. Name raw files `raw-happy-path.webm` or `raw-edge-case-.webm`.
-
-## Duration
-
-Probe each raw take once. Maximum 12 seconds, never a target; do not pad or extend.
-
-```bash
-ffprobe -v error -select_streams v:0 \
- -show_entries stream=codec_name,width,height:format=duration \
- -of json
-```
-
-Without `ffmpeg`, only an already-short raw take passes; otherwise report `blocked: media-postprocess-unavailable`.
-
-Never trim the head or infer the first action from scene changes. Inspect both sides of a tail cut; reject one that removes the result or final one-second hold.
-
-```bash
-qa_cut_frames=$(mktemp -d)
-ffmpeg -v error -ss -i raw-.webm \
- -frames:v 1 "$qa_cut_frames/before.png"
-ffmpeg -v error -ss -i raw-.webm \
- -frames:v 1 "$qa_cut_frames/after.png"
-```
-
-## Normalization
-
-Re-encode without audio. Use only a visually verified tail cut; omit `-to` when unused.
-
-```bash
-ffmpeg -y -to -i raw-.webm \
- -an -vf "fps=12,scale=1280:-2:force_original_aspect_ratio=decrease" \
- -c:v libvpx-vp9 -crf 36 -b:v 0 .webm
-```
-
-Above 12 seconds, remove non-scenario actions and record again. Never accelerate. Block when the required journey cannot fit.
-
-## Validation
-
-Probe the final file, then inspect four frames per second chronologically.
-
-```bash
-qa_final_frames=$(mktemp -d)
-ffmpeg -v error -i .webm -vf "fps=4" \
- "$qa_final_frames/frame-%03d.png"
-```
-
-Require:
-
-- codec `vp9`, width `1280`, duration at most 12 seconds;
-- initial state, every action and transition, and final result visible;
-- initial and final holds spanning at least four sampled frames.
+Use the framework pin, never `latest`. One named session per scenario; apply viewport and permissions before initial state. Keep stdout, stderr and exit status visible, without redirects, pipes, substitutions or `|| true`.
-`ffprobe` alone is insufficient. Delete raw takes, cut-point frames, and validation frames only after every final WebM passes both checks.
+`run-code` takes `async page => { ... }`; `page` is the only binding, browser globals belong inside `page.evaluate`. Return observed facts, including expected-outcome timeouts; product mismatches must not throw. Bound waits, measure timed boundaries on both sides, and capture brief transitions inside one `run-code`, never infer them.
diff --git a/plugins/aidd-qa/skills/02-demo/SKILL.md b/plugins/aidd-qa/skills/02-demo/SKILL.md
new file mode 100644
index 000000000..ce7736bb5
--- /dev/null
+++ b/plugins/aidd-qa/skills/02-demo/SKILL.md
@@ -0,0 +1,16 @@
+---
+name: 02-demo
+description: Show a journey or bug through a browser video or CLI transcript. Use for reviewer evidence, not acceptance verification or fixes.
+argument-hint: journey | candidate
+---
+
+# Demo
+
+## Actions
+
+Run [record](actions/01-record.md).
+
+## Transversal rules
+
+- Run against the candidate; never patch it or delete data not proven test-only.
+- Called by another skill or agent: never ask; return `blocked` with the question.
diff --git a/plugins/aidd-qa/skills/02-demo/actions/01-record.md b/plugins/aidd-qa/skills/02-demo/actions/01-record.md
new file mode 100644
index 000000000..55bdbe792
--- /dev/null
+++ b/plugins/aidd-qa/skills/02-demo/actions/01-record.md
@@ -0,0 +1,24 @@
+# 01 - Record
+
+## Input
+
+Journey steps and expected outcome, candidate, optional destination.
+
+## Output
+
+One artifact named for each journey: browser WebM or CLI `.txt` transcript (commands, outputs, exit status). Label observed `pass`/`fail` or `blocked`; never stage or invent outcomes.
+
+Use the caller's folder, else `qa/` beside the journey source; never a temporary destination.
+
+## Process
+
+1. Missing dependency: ask once to install outside the application manifest; refusal/recheck failure => `blocked` with the error.
+2. Use documented deterministic test data and verified teardown; authenticate off-camera, exclude secrets.
+3. Browser: [recorder](../references/interface-browser-playwright-cli.md). CLI: capture verbatim, including failures.
+4. Tool failure: reset and retry once, then `blocked`.
+5. Restore and verify baseline before closing.
+6. Direct browser calls: offer to open the video; open on agreement.
+
+## Test
+
+Artifact readable/playable; evidence matches the observed journey.
diff --git a/plugins/aidd-qa/skills/02-demo/references/interface-browser-playwright-cli.md b/plugins/aidd-qa/skills/02-demo/references/interface-browser-playwright-cli.md
new file mode 100644
index 000000000..786a320fd
--- /dev/null
+++ b/plugins/aidd-qa/skills/02-demo/references/interface-browser-playwright-cli.md
@@ -0,0 +1,66 @@
+# Browser recorder
+
+## Invocation
+
+Requires `npx`, `ffmpeg`, `ffprobe`.
+
+```bash
+cd && npx --offline --no -- @playwright/cli@0.1.17 -s=demo--
+```
+
+Keep stdout, stderr and exit status visible.
+
+## Recording
+
+One session per journey.
+
+1. `resize 1280 720`; reach initial state, then `video-start raw-.webm --size=1280x720`.
+2. All interactions in one `run-code 'async page => { ... }'`. Hold initial/verified final states `1000 ms`; pause `300 ms` between actions and after scrolls; before every click, focus and hover its target for `300 ms`.
+3. Use bounded waits; log expected and observed results. Keep recording mismatches; execution errors invalidate the take.
+4. Successful `run-code` => `video-stop`.
+
+## Duration
+
+Probe raw and encoded video:
+
+```bash
+ffprobe -v error -select_streams v:0 \
+ -show_entries stream=codec_name,width,height:format=duration \
+ -of json
+```
+
+Maximum 12 seconds; no padding, acceleration or head cuts. If the journey cannot fit, return `blocked`.
+
+For tail cuts, read both cut-point frames; preserve result and final hold:
+
+```bash
+qa_cut_frames=$(mktemp -d)
+ffmpeg -v error -ss -i raw-.webm \
+ -frames:v 1 "$qa_cut_frames/before.png"
+ffmpeg -v error -ss -i raw-.webm \
+ -frames:v 1 "$qa_cut_frames/after.png"
+```
+
+## Normalization
+
+Use `-to` only for a verified tail cut:
+
+```bash
+ffmpeg -y -to -i raw-.webm \
+ -an -vf "fps=12,scale=1280:-2:force_original_aspect_ratio=decrease" \
+ -c:v libvpx-vp9 -crf 36 -b:v 0 /.webm
+```
+
+## Validation
+
+Read every generated sheet in filename order:
+
+```bash
+qa_final_frames=$(mktemp -d)
+ffmpeg -v error -i /.webm -vf "fps=4,tile=2x4" \
+ "$qa_final_frames/sheet-%03d.png"
+```
+
+Require `vp9`, width `1280`, โค12 seconds; initial state, every action/transition, final result visible; holds โฅfour frames.
+
+Any unread sheet or unchecked hold => `blocked`. Delete raw takes/cut-point/validation frames only after all finals pass probe and inspection.
diff --git a/plugins/aidd-vcs/skills/02-pull-request/actions/02-draft.md b/plugins/aidd-vcs/skills/02-pull-request/actions/02-draft.md
index 496dbd502..55caa66de 100644
--- a/plugins/aidd-vcs/skills/02-pull-request/actions/02-draft.md
+++ b/plugins/aidd-vcs/skills/02-pull-request/actions/02-draft.md
@@ -13,11 +13,11 @@ The proposed title, body, and base, approved by the user.
## Process
1. **Template.** Load the request template, the project's own when set, else the bundled [pull_request.md](../assets/pull_request.md).
-2. **Write.** Draft a concise title and a body following the template from the change summary. Link every changed `**/qa/*.webm` under the template's testing or verification section.
+2. **Write.** Draft a concise title and a body following the template from the change summary. Link caller-supplied evidence and every changed `**/qa/*.webm` under the template's testing or verification section.
- Give the fewest concrete verification steps needed for the changed behavior.
- State the expected result for each step.
- Omit generic CI summaries.
- - Label scenarios that were not run.
+ - Label failed, blocked or unrun scenarios.
3. **Confirm.** Show the title, body, and base, apply any overrides, and wait for approval.
## Test
diff --git a/scripts/__tests__/aidd-qa-sdlc-wiring.test.js b/scripts/__tests__/aidd-qa-sdlc-wiring.test.js
new file mode 100644
index 000000000..033b8ba13
--- /dev/null
+++ b/scripts/__tests__/aidd-qa-sdlc-wiring.test.js
@@ -0,0 +1,39 @@
+const assert = require("node:assert/strict");
+const fs = require("node:fs");
+const path = require("node:path");
+const test = require("node:test");
+
+const root = path.resolve(__dirname, "../..");
+const read = (file) => fs.readFileSync(path.join(root, file), "utf8");
+
+test("static permissions: the checker may invoke both QA skills", () => {
+ const permissions = read("plugins/aidd-dev/agents/checker.md").split("# Skills you may invoke")[1];
+ const skills = [...permissions.matchAll(/`(\/[^`]+)`/g)].map((match) => match[1]);
+ for (const skill of ["/aidd-qa:01-acceptance-qa", "/aidd-qa:02-demo"]) assert.ok(skills.includes(skill), skill);
+});
+
+test("static flow: SDLC routes the QA verdicts through the canonical skills", () => {
+ const diagram = read("plugins/aidd-orchestrator/skills/01-sdlc/references/03-check.md").split("```mermaid")[1];
+ const nodes = Object.fromEntries([...diagram.matchAll(/(\w+)\["([/$][^"\n]+)"\]/g)]
+ .map((match) => [match[1], match[2]]));
+ const edges = [...diagram.matchAll(/^\s*(\w+)\s+(?:--\s+("[^"]+"|[^\n]+?)\s+)?-->\s+(\w+)\s*$/gm)]
+ .map((match) => [nodes[match[1]] || match[1], (match[2] || "").replace(/^"|"$/g, ""), nodes[match[3]] || match[3]]);
+ const qa = "/aidd-qa:01-acceptance-qa";
+ const demo = "/aidd-qa:02-demo";
+ const challenge = "/aidd-refine:02-challenge";
+ for (const edge of [
+ ["/aidd-dev:05-review", "approve", qa],
+ [qa, "pass", "$qa.md"],
+ ["$qa.md", null, demo],
+ [demo, "", challenge],
+ [qa, "skipped", challenge],
+ [qa, "fail", "Deliver"],
+ [qa, "blocked: criteria", "Frame"],
+ [qa, "blocked: execution", "Stop"],
+ ]) assert.ok(edges.some((actual) => actual.every((value, i) => edge[i] === null || value === edge[i])), edge.join(" -> "));
+});
+
+test("static installation: aidd-qa is recommended", () => {
+ const plugins = JSON.parse(read(".claude-plugin/marketplace.json")).plugins;
+ assert.equal(plugins.find((plugin) => plugin.name === "aidd-qa").metadata.recommended, true);
+});
diff --git a/scripts/__tests__/aidd-qa-verify-and-demo-split.test.js b/scripts/__tests__/aidd-qa-verify-and-demo-split.test.js
new file mode 100644
index 000000000..873fd8178
--- /dev/null
+++ b/scripts/__tests__/aidd-qa-verify-and-demo-split.test.js
@@ -0,0 +1,30 @@
+const assert = require("node:assert/strict");
+const fs = require("node:fs");
+const path = require("node:path");
+const test = require("node:test");
+
+const qa = path.resolve(__dirname, "../../plugins/aidd-qa");
+const verify = path.join(qa, "skills/01-acceptance-qa");
+
+test("static contract: the plugin declares acceptance QA and demo", () => {
+ const manifest = JSON.parse(fs.readFileSync(path.join(qa, ".claude-plugin/plugin.json"), "utf8"));
+ assert.deepEqual(manifest.skills, ["./skills/01-acceptance-qa", "./skills/02-demo"]);
+});
+
+test("static ownership: acceptance QA contains no media tooling", () => {
+ const offenders = fs.readdirSync(verify, { recursive: true, withFileTypes: true })
+ .filter((entry) => entry.isFile())
+ .map((entry) => path.join(entry.parentPath, entry.name))
+ .filter((file) => /\b(?:ffmpeg|ffprobe|libvpx-vp9)\b|\.webm\b/.test(fs.readFileSync(file, "utf8")));
+ assert.deepEqual(offenders.map((file) => path.relative(qa, file)), []);
+});
+
+test("static report contract: criteria, candidate, observations and gaps remain traceable", () => {
+ const template = fs.readFileSync(path.join(verify, "assets/qa-report-template.md"), "utf8");
+ const fields = [...template.matchAll(/^- \*\*([^*]+)\*\*:/gm)].map((match) => match[1]);
+ for (const field of ["Verdict", "Source", "Candidate"]) assert.ok(fields.includes(field), field);
+ const columns = template.match(/^\| Scenario \|.*$/m)[0].split("|").map((cell) => cell.trim());
+ for (const column of ["Criteria", "Expected", "Actual", "Verdict"]) assert.ok(columns.includes(column), column);
+ const sections = [...template.matchAll(/^## (.+)$/gm)].map((match) => match[1]);
+ for (const section of ["Out of interface", "Rejected", "Limitations"]) assert.ok(sections.includes(section), section);
+});
diff --git a/scripts/__tests__/architecture-rules.property.test.js b/scripts/__tests__/architecture-rules.property.test.js
index ad930c915..b4c436a02 100644
--- a/scripts/__tests__/architecture-rules.property.test.js
+++ b/scripts/__tests__/architecture-rules.property.test.js
@@ -214,11 +214,13 @@ function realSkills() {
return skills;
}
-/** The skill's content with every table row citing `name` removed. */
+/** Remove each citation form the router checker accepts. */
function withoutCitationOf(content, name) {
const stem = stemOf(name).toLowerCase();
const noExt = name.replace(/\.md$/, "").toLowerCase();
return content
+ .replaceAll(`actions/${name}`, "")
+ .replaceAll(`${TICK}${name}${TICK}`, "")
.split("\n")
.filter((line) => {
if (!/^\s*\|/.test(line)) return true;
@@ -233,6 +235,20 @@ function withoutCitationOf(content, name) {
.join("\n");
}
+test("citation mutation removes links, fenced paths and file names", () => {
+ const name = "01-run.md";
+ for (const citation of [
+ `[run](actions/${name})`,
+ `\`\`\`\nactions/${name}\n\`\`\``,
+ `${TICK}${name}${TICK}`,
+ ]) {
+ const file = "plugins/aidd-dev/skills/01-plan/SKILL.md";
+ const content = `## Actions\n\n${citation}`;
+ assert.equal(checkRouterCoherence(file, content, [name]).length, 0);
+ assert.equal(checkRouterCoherence(file, withoutCitationOf(content, name), [name]).length, 1);
+ }
+});
+
test("every router in the tree cites every action it provides", () => {
const failures = [];
for (const { relPath, content, names } of realSkills()) {
@@ -243,14 +259,10 @@ test("every router in the tree cites every action it provides", () => {
});
test("removing a real router's citation is caught, for every action in the tree", () => {
- // `aidd-dev:10-todo` names its one action as a fenced path rather than a table row, so there
- // is no row to remove and nothing to catch. It is the one documented exception.
- const FENCED_PATH_SKILL = "plugins/aidd-dev/skills/10-todo/SKILL.md";
const missed = [];
let checked = 0;
for (const { relPath, content, names } of realSkills()) {
- if (relPath === FENCED_PATH_SKILL) continue;
for (const name of names) {
checked += 1;
const violations = checkRouterCoherence(relPath, withoutCitationOf(content, name), names);
diff --git a/scripts/__tests__/architecture-rules.test.js b/scripts/__tests__/architecture-rules.test.js
index 33f5666fb..d4cba7e32 100644
--- a/scripts/__tests__/architecture-rules.test.js
+++ b/scripts/__tests__/architecture-rules.test.js
@@ -229,7 +229,7 @@ test("sweeping the repository's own plugins/ tree yields zero violations", () =>
assert.deepEqual(violations, []);
// A sweep that never actually exercised a skill with action files would pass the same way โ
// this pins the sweep to the measured count so it cannot go vacuously green.
- assert.equal(skillsWithActions, 49);
+ assert.equal(skillsWithActions, 50);
});
test("a table that declares no action column cites nothing, whatever its cells read", () => {