yadflow 2.2.0 → 2.3.0

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.
@@ -20,12 +20,12 @@ output_folder: "{project-root}/_bmad-output"
20
20
  defaults:
21
21
  assistance: review # none | review | heavy
22
22
  automation: human_approve # human_approve | machine_advance
23
- # Front steps (analysis [optional], epic, architecture, ui-design, stories) are locked to
23
+ # Front steps (analysis [optional], epic, architecture, ui-design, stories, test-cases) are locked to
24
24
  # human_approve and may NOT be set to machine_advance in this version (build plan §1, §8.7).
25
25
  front_steps_locked: true
26
26
  # Each front authoring step opens its own branch at the start of the step (the <step> is the step id:
27
- # analysis | epic | architecture | ui-design | stories). Git/greenfield-safe; distinct from the
28
- # bridge's review branch (hub.artifact_branch). See yad-epic/references/state-schema.md.
27
+ # analysis | epic | architecture | ui-design | stories | test-cases). Git/greenfield-safe; distinct
28
+ # from the bridge's review branch (hub.artifact_branch). See yad-epic/references/state-schema.md.
29
29
  front_authoring_branch: "<step>/EP-<slug>"
30
30
 
31
31
  # Team review gate defaults (build plan §3 piece 2, §4).
@@ -134,6 +134,22 @@ design:
134
134
  # NO tokens in the registry (project_url/files are plain references, never credentials).
135
135
  auth: user
136
136
 
137
+ # Testing tool (yad-connect-testing) — the test-cases step (yad-test-cases) materializes the actual
138
+ # automation tests inside a connected testing tool, alongside test-cases.md. The tool is reached through
139
+ # its MCP (a harness MCP server, like the design tool — NOT a CLI), detected per provider and DEGRADING
140
+ # to artifacts-only when absent. Playwright-first but PLUGGABLE: a testing-tool adapter, like the
141
+ # design-tool adapter. Connection is one-per-project, recorded at setup; the per-epic case->test map
142
+ # (test-links.json) is written by yad-test-cases per epic.
143
+ testing:
144
+ registry: "{project-root}/.sdlc/testing.json" # project-wide testing connection (NOT per-epic)
145
+ tools: [playwright, cypress, pytest] # supported adapters; an unknown tool falls back to `primary`
146
+ primary: playwright # the default/named provider
147
+ degrade: artifacts-only # no tool / no MCP => yad-test-cases authors test-cases.md only
148
+ links: "{project-root}/epics/EP-<slug>/.sdlc/test-links.json" # per-epic case->test map (yad-test-cases)
149
+ # Auth: `yad-connect-testing` connects through the LOCAL user's own authenticated MCP session and stores
150
+ # NO tokens in the registry (project_url/suites are plain references, never credentials).
151
+ auth: user
152
+
137
153
  # Hub platform + front-half review bridge (yad-connect-repos `detect-hub`; yad-review-gate + yad-hub-bridge).
138
154
  # The product hub is itself a git repo on a platform. With the bridge enabled, the front-half review/
139
155
  # comment/approval cycle runs through a real PR/MR on the hub: a review PR is opened per artifact, reviewers
@@ -166,7 +182,7 @@ automation:
166
182
  # Hard lock — the dial-setter REFUSES machine_advance for these, regardless of trust evidence.
167
183
  # The front authoring steps (already locked:true in state.json; analysis is optional) + the human
168
184
  # merge gate.
169
- locked_steps: [analysis, epic, architecture, ui-design, stories, engineer-review]
185
+ locked_steps: [analysis, epic, architecture, ui-design, stories, test-cases, engineer-review]
170
186
  # Kill switch (phase-4-build-plan.md §Safety): true => every step forced to human_approve
171
187
  # system-wide, no per-step edits. One line, instantly reversible. Toggle via `yad-run action: kill`.
172
188
  kill_switch: false
@@ -11,7 +11,7 @@ set -euo pipefail
11
11
  ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
12
12
  cd "$ROOT"
13
13
 
14
- SKILLS=(yad-analysis yad-epic yad-architecture yad-ui yad-stories yad-connect-repos yad-connect-design yad-spec yad-implement yad-checks yad-pr-template yad-review-comments yad-hub-bridge yad-ship yad-backfill yad-run yad-review-gate yad-status)
14
+ SKILLS=(yad-analysis yad-epic yad-architecture yad-ui yad-stories yad-test-cases yad-connect-repos yad-connect-design yad-connect-testing yad-spec yad-implement yad-checks yad-pr-template yad-review-comments yad-hub-bridge yad-ship yad-backfill yad-run yad-review-gate yad-status)
15
15
 
16
16
  echo "Installing sdlc module from $ROOT/skills ..."
17
17
 
@@ -1,11 +1,13 @@
1
1
  module,skill,display-name,menu-code,description,action,args,phase,preceded-by,followed-by,required,output-location,outputs
2
2
  SDLC Workflow,yad-epic,Author Epic,AE,"Front state 1: shape an idea with analyst then pm into epic.md; assign EP-<slug> ID and seed .sdlc state. Never auto-advances.",,{idea: one-line feature idea},1-front,,yad-review-gate,true,epics/EP-<slug>/,epic.md state.json
3
- SDLC Workflow,yad-review-gate,Team Review Gate,RG,"Reusable review+approve gate for all four reviews. Shares an artifact for review, records comments and approvals as files, enforces owner + 1 reviewer (escalates on contract/auth/payments; per-repo routing for stories), advances state only when approved.",,{artifact: file under the epic} {action: open|comment|approve|advance},1-front,,,true,epics/EP-<slug>/reviews/,reviews/*.md approvals.json state.json
3
+ SDLC Workflow,yad-review-gate,Team Review Gate,RG,"Reusable review+approve gate for all five reviews. Shares an artifact for review, records comments and approvals as files, enforces owner + 1 reviewer (escalates on contract/auth/payments; per-repo routing for stories), advances state only when approved.",,{artifact: file under the epic} {action: open|comment|approve|advance},1-front,,,true,epics/EP-<slug>/reviews/,reviews/*.md approvals.json state.json
4
4
  SDLC Workflow,yad-architecture,Author Architecture,AA,"Front state 3: with the architect author architecture.md and the locked contract.md; hash-lock the contract surface. Never auto-advances.",,{epic: EP-<slug>},1-front,yad-review-gate,yad-review-gate,true,epics/EP-<slug>/,architecture.md contract.md contract-lock.json state.json
5
5
  SDLC Workflow,yad-ui,Author UI Design,AU,"Front state 5: with the ux-designer author ui-design.md and DESIGN.md, driving Impeccable slash-commands when installed. Never auto-advances.",,{epic: EP-<slug>},1-front,yad-review-gate,yad-review-gate,true,epics/EP-<slug>/,ui-design.md DESIGN.md state.json
6
6
  SDLC Workflow,yad-stories,Author Stories,AS,"Front state 7: with the pm break the epic into repo-tagged stories with stable EP-<slug>-S0N IDs, one file each under stories/. Never auto-advances.",,{epic: EP-<slug>},1-front,yad-review-gate,yad-review-gate,true,epics/EP-<slug>/stories/,stories/EP-<slug>-S0N.md state.json
7
+ SDLC Workflow,yad-test-cases,Author Test Cases,TC,"Front state 9 (PARALLEL, non-blocking): opens when the stories gate passes — the epic is already ready-for-build, so the build half can start at the same time the tester works here. With the test architect (Murat) author test-cases.md covering the approved stories, and — when a testing tool is connected (.sdlc/testing.json) — generate/link the actual automation tests in it, recording test-links.json; otherwise produce the test-case artifact only. Its review never moves currentStep off ready-for-build. Never auto-advances.",,{epic: EP-<slug>},1-front,yad-review-gate,yad-review-gate,true,epics/EP-<slug>/,test-cases.md test-links.json state.json
7
8
  SDLC Workflow,yad-connect-repos,Connect Code Repos,CR,"Setup/maintenance: connect code repos to the product hub so the front/brain phases are code-aware. Registers each repo (GitHub or GitLab, local-user auth, no stored tokens) in .sdlc/repos.json and caches a Repomix pack + a lightweight code-map (existing endpoints/events/data-models/modules, secret-scanned). Idempotent and refreshable; staleness tracked by HEAD sha. Run at setup or any time a new repo is added. Not a gated state — never touches epic state or approvals.",,{action: connect|refresh|list|disconnect} {repo: <name>} {path: <path-or-absolute>} {git_url: <ssh-or-https>} {domain_owner: <who>},0-setup,,yad-epic,false,.sdlc/,repos.json code-context/<repo>/pack.md code-context/<repo>/code-map.md
8
9
  SDLC Workflow,yad-connect-design,Connect Design Tool,CD,"Setup/maintenance: connect a design tool (Figma-first, pluggable) to the product hub so the UI design step can materialize the actual feature design (mobile screens / web pages) inside it, alongside ui-design.md + DESIGN.md. Records the tool + project/file references in .sdlc/design.json (local-user / MCP-session auth, no stored tokens), detecting whether a design-tool MCP is available and degrading to markdown-only when absent. Idempotent and refreshable; one connection per project. Not a gated state — never touches epic state or approvals.",,{action: connect|refresh|list|disconnect} {tool: figma|pencil|none} {project_url: <team/project/file url>} {files: {web,mobile}},0-setup,,yad-ui,false,.sdlc/,design.json
10
+ SDLC Workflow,yad-connect-testing,Connect Testing Tool,CT,"Setup/maintenance: connect a testing tool (Playwright-first, pluggable) to the product hub so the test-cases step can implement the actual automation tests in it, alongside test-cases.md. Records the tool + project/suite references in .sdlc/testing.json (local-user / MCP-session auth, no stored tokens), detecting whether a testing-tool MCP is available and degrading to artifacts-only when absent. Idempotent and refreshable; one connection per project. Not a gated state — never touches epic state or approvals.",,{action: connect|refresh|list|disconnect} {tool: playwright|cypress|pytest|none} {project_url: <project/config reference>} {suites: {<repo>}},0-setup,,yad-test-cases,false,.sdlc/,testing.json
9
11
  SDLC Workflow,yad-spec,Author Spec,SP,"Build-half Step A: for one ready-for-build story and one of its repos, run the heavy Spec Kit ceremony once (specify→clarify→plan→analyze→checklist→tasks) inside that code repo, writing specs/<story-id>/ in Spec Kit's layout (drives /speckit.* when installed, else hand-authors and records speckit: not-installed). References the locked contract; never re-invents the surface. Writes link.md back to the story. Never auto-advances.",,{epic: EP-<slug>} {story: EP-<slug>-S0N} {repo: <one of story.repos>},3-build,yad-review-gate,,false,demo-repos/<repo>/specs/<story-id>/,spec.md research.md data-model.md contracts/ plan.md tasks.md link.md
10
12
  SDLC Workflow,yad-implement,Implement Task,IM,"Build-half Step B: with the dev lens, implement ONE atomic task from a story's tasks.md as a small diff (<=3 files) on its own branch feat/<story>-<task>-<slug> in the code repo. Diff stays inside the task's declared files (flag and STOP if it grows beyond them). Commit ends with a Task: <story>-<task> trailer; add Contract-Change: yes only when the locked contract surface is touched (routes back to the architecture gate). Never auto-advances.",,{epic: EP-<slug>} {story: EP-<slug>-S0N} {repo: <one of story.repos>} {task: T0N},3-build,yad-spec,,false,demo-repos/<repo>/,branch+commit per atomic task
11
13
  SDLC Workflow,yad-checks,Check Gates,CK,"Build-half Step C: wire and run the three production-safety CI gates on a code repo — spec-link (every change links a real story/spec via its Task trailer), contract-check (a contract-surface change without Contract-Change + an updated re-locked contract FAILS and routes back to the architecture gate), and build/test/lint. CI-agnostic bash invoked by GitHub Actions and GitLab CI. Blocking in CI; the human still owns the merge. Never auto-advances.",,{repo: <one of an epic's repos>} {action: wire|run} {base: target branch},3-build,yad-implement,,false,demo-repos/<repo>/,checks/*.sh .github/workflows/yad-checks.yml .gitlab-ci.yml
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: yad-analysis
3
- description: 'Optional front state 1 of the gated SDLC. With the analyst, pressure-test a feature idea and write the discovery brief into analysis.md. Assigns the EP-<slug> ID and seeds .sdlc/ state (the 10-step chain that puts analysis before epic). Never auto-advances — hands off to the team review gate. Optional: if skipped, the epic step does this shaping inline. Use when the user says "analyse the idea", "start with analysis", or "author the analysis".'
3
+ description: 'Optional front state 1 of the gated SDLC. With the analyst, pressure-test a feature idea and write the discovery brief into analysis.md. Assigns the EP-<slug> ID and seeds .sdlc/ state (the 12-step chain that puts analysis before epic). Never auto-advances — hands off to the team review gate. Optional: if skipped, the epic step does this shaping inline. Use when the user says "analyse the idea", "start with analysis", or "author the analysis".'
4
4
  ---
5
5
 
6
6
  # SDLC — Author Analysis (optional front state 1)
@@ -11,8 +11,8 @@ state machine in `.sdlc/`. This is a **front state**: human-authored with AI ass
11
11
  auto-advances**. When the analysis is drafted, control passes to `yad-review-gate`.
12
12
 
13
13
  This step is **optional**. When it runs, it is the entry point: it assigns the ID and seeds the
14
- **10-step** chain (`analysis` before `epic`). When it is **skipped**, `yad-epic` does the same
15
- analyst shaping inline and seeds the **8-step** chain — no behaviour change for teams that skip it.
14
+ **12-step** chain (`analysis` before `epic`). When it is **skipped**, `yad-epic` does the same
15
+ analyst shaping inline and seeds the **10-step** chain — no behaviour change for teams that skip it.
16
16
 
17
17
  This skill enforces the build plan's core rules: all state lives in files; IDs are generated by the
18
18
  engine (never typed by hand); front steps are locked to `human_approve`.
@@ -88,7 +88,7 @@ code-context: { repos: [], loaded: <YYYY-MM-DD or none> } # which code-maps in
88
88
  Fill the body with the user; leave `owner` for the user to set.
89
89
 
90
90
  ### Step 6 — Seed the state machine
91
- Create `{project-root}/epics/EP-<slug>/.sdlc/state.json` describing the full **10-step** front-state
91
+ Create `{project-root}/epics/EP-<slug>/.sdlc/state.json` describing the full **12-step** front-state
92
92
  sequence (analysis before epic), all steps defaulting to `automation: human_approve`, with every
93
93
  authoring step **locked**. Use this exact shape (see `references/state-schema.md`):
94
94
 
@@ -107,7 +107,9 @@ authoring step **locked**. Use this exact shape (see `references/state-schema.md
107
107
  { "id": "ui-design", "type": "author", "artifact": "ui-design.md", "assistance": "review", "automation": "human_approve", "locked": true, "status": "blocked", "risk_tags": [] },
108
108
  { "id": "ui-design-review", "type": "review+approve", "artifact": "ui-design.md", "assistance": "review", "automation": "human_approve", "locked": true, "status": "blocked", "risk_tags": [] },
109
109
  { "id": "stories", "type": "author", "artifact": "stories/", "assistance": "review", "automation": "human_approve", "locked": true, "status": "blocked", "risk_tags": [] },
110
- { "id": "stories-review", "type": "review+approve", "artifact": "stories/", "assistance": "review", "automation": "human_approve", "locked": true, "status": "blocked", "risk_tags": [] }
110
+ { "id": "stories-review", "type": "review+approve", "artifact": "stories/", "assistance": "review", "automation": "human_approve", "locked": true, "status": "blocked", "risk_tags": [] },
111
+ { "id": "test-cases", "type": "author", "artifact": "test-cases.md", "assistance": "review", "automation": "human_approve", "locked": true, "status": "blocked", "risk_tags": [] },
112
+ { "id": "test-cases-review", "type": "review+approve", "artifact": "test-cases.md", "assistance": "review", "automation": "human_approve", "locked": true, "status": "blocked", "risk_tags": [] }
111
113
  ]
112
114
  }
113
115
  ```
@@ -116,6 +118,9 @@ Notes:
116
118
  - `analysis-review` carries no `risk_tags` — it is the **base** rule (owner + 1 reviewer).
117
119
  - `architecture-review` carries `risk_tags: ["contract"]` so the gate escalates it by default
118
120
  (build plan §4): the contract review needs domain owners, not just owner + 1.
121
+ - `test-cases` / `test-cases-review` are a **parallel, non-blocking track**: they seed `blocked` and open
122
+ when `stories-review` passes — the epic is already `ready-for-build` by then, so the build half runs
123
+ alongside the tester (see `../yad-epic/references/state-schema.md`).
119
124
  - Also create an empty approvals ledger `{project-root}/epics/EP-<slug>/.sdlc/approvals.json`
120
125
  and an empty comments ledger `{project-root}/epics/EP-<slug>/.sdlc/comments.json`, each containing
121
126
  `[]`, and the `reviews/` directory.
@@ -20,7 +20,7 @@ markdown-only exactly as before.
20
20
 
21
21
  - `{project-root}` resolves from the project working directory (the **product hub**).
22
22
  - The integration is **Figma-first but pluggable** (`config.yaml` `design.tools`): a design-tool
23
- *adapter*, like the `github`/`gitlab` platform adapter. Figma is the primary provider; `pencil`
23
+ *adapter*, like the GitHub/GitLab platform adapter. Figma is the primary provider; `pencil`
24
24
  (the `.pen` web/mobile editor) is a second, write-capable provider; `none` → markdown-only.
25
25
  - **The design tool is reached through its MCP** (a harness MCP server), NOT a subprocess CLI — the same
26
26
  shape as Impeccable's slash-commands, not Repomix's `npx`. The skill detects the MCP and degrades when
@@ -27,8 +27,10 @@ root, not under any `epics/EP-<slug>/.sdlc/`.
27
27
 
28
28
  ## Rules
29
29
 
30
- - **`tool`** selects the adapter; it MUST be one of `config.yaml` `design.tools` (or `none`). An unknown
31
- tool falls back to `design.primary` with a warning never silently accepted.
30
+ - **`tool`** selects the adapter; it MUST be one of `config.yaml` `design.tools` (or `none`). At
31
+ **connect** time an unknown tool is normalized to `design.primary` with a warning (so the registry
32
+ never persists an unknown value); a registry hand-edited to an unknown or missing tool **fails
33
+ `doctor`** with `YAD-CFG-002` and must be fixed.
32
34
  - **Auth is never stored.** No Figma PAT, OAuth token, or any credential in the registry. `project_url`
33
35
  and `files` are plain references; `connect` reaches the tool through the user's authenticated MCP
34
36
  session.
@@ -0,0 +1,121 @@
1
+ ---
2
+ name: yad-connect-testing
3
+ description: 'Connects a testing tool (Playwright, or another tool — pluggable) to the product hub so the test-cases step can implement the actual automation tests, not just Markdown test cases. Registers the tool into the project-wide .sdlc/testing.json (local-user / MCP-session auth, no stored tokens), detecting whether a testing-tool MCP is available and degrading to artifacts-only when it is not. Run at setup or any time the testing tool changes. Reusable, idempotent, refreshable. Use when the user says "connect Playwright", "connect a testing tool", "refresh the testing connection", or "list the testing connection".'
4
+ ---
5
+
6
+ # SDLC — Connect a Testing Tool (make the test-cases step automation-aware)
7
+
8
+ **Goal:** Let the test-cases step (`yad-test-cases`) produce the **actual automation tests** — the
9
+ runnable specs in a connected code repo — alongside the Markdown artifact (`test-cases.md`). This skill
10
+ **connects** a testing tool such as **Playwright** to the product hub and records *how* to reach it (the
11
+ tool, the suite references, which MCP runs it) — never a credential.
12
+
13
+ This is **setup/maintenance**, not a gated front state — it never touches `.sdlc/state.json` or any
14
+ epic's approvals. It only writes the project-wide testing registry. `yad-test-cases` consumes it: when a
15
+ tool is connected and its MCP is available, the `test architect` lens **generates** automation tests
16
+ into the connected repo(s) (or **links** an existing suite and reads it back); when nothing is
17
+ connected, `yad-test-cases` runs artifacts-only exactly as before.
18
+
19
+ ## Conventions
20
+
21
+ - `{project-root}` resolves from the project working directory (the **product hub**).
22
+ - The integration is **Playwright-first but pluggable** (`config.yaml` `testing.tools`): a testing-tool
23
+ *adapter*, like the GitHub/GitLab platform adapter or the design-tool adapter. Playwright is the
24
+ primary provider; `cypress` and `pytest` are second providers; `none` → artifacts-only.
25
+ - **The testing tool is reached through its MCP** (a harness MCP server), NOT a subprocess CLI — the
26
+ same shape as the design tool's MCP, not Repomix's `npx`. The skill detects the MCP and degrades when
27
+ it is absent; it never installs an MCP server.
28
+ - Registry: `{project-root}/.sdlc/testing.json` (project-wide, shared across all epics — NOT per-epic),
29
+ the sibling of `.sdlc/repos.json`, `.sdlc/hub.json`, and `.sdlc/design.json`.
30
+ - Per-epic test→suite links are written later by `yad-test-cases`
31
+ (`epics/EP-<slug>/.sdlc/test-links.json`), not here.
32
+ - Speak in the configured `communication_language`; write documents in `document_output_language`.
33
+
34
+ ## Inputs
35
+
36
+ - `action` — `connect` (default) | `refresh` | `list` | `disconnect`.
37
+ - `tool` — `playwright` | `cypress` | `pytest` | another adapter id (`config.yaml` `testing.tools`).
38
+ `none` records a deliberate artifacts-only project.
39
+ - `project_url` — the testing tool's project/config reference (e.g. a `playwright.config.ts` path or a
40
+ test-runner project URL). Optional — a connection with no suite yet is valid; `yad-test-cases` can
41
+ create one on first generate.
42
+ - `suites` — optional default suite mapping per repo (`{ <repo>: <ref> }`).
43
+
44
+ ## On Activation
45
+
46
+ ### Step 1 — Resolve the tool and its MCP (the testing-tool adapter)
47
+ Determine which tool is being connected from `tool` (default `playwright`); reject a `tool` not in
48
+ `config.yaml` `testing.tools` (fall back to the configured `testing.primary` with a warning, the same
49
+ way `registerRepo` falls back on an unknown platform). Then **detect the tool's MCP** in this harness:
50
+
51
+ - **playwright** → a Playwright MCP server (drives a browser, generates/runs E2E + API specs).
52
+ - **cypress** → the Cypress MCP (generate/run Cypress specs).
53
+ - **pytest** → a pytest MCP (generate/run service-layer tests).
54
+ - another adapter → its named MCP.
55
+
56
+ Record `provider` (the concrete MCP, e.g. `playwright-mcp` | `cypress-mcp` | `pytest-mcp`) and whether
57
+ it is available. **Auth is the local user's own** — the user's authenticated MCP session. The skill
58
+ **stores no tokens**; `project_url`/`suites` are plain references, never credentials.
59
+
60
+ **Graceful degradation:** if no testing-tool MCP is available, record `source: "unavailable"` and report
61
+ that `yad-test-cases` will run **artifacts-only** until an MCP is connected (no error — the testing tool
62
+ is purely additive, exactly like the design tool being absent). Do **not** install an MCP server as part
63
+ of this step.
64
+
65
+ ### Step 2 — Record the connection in the registry
66
+ Upsert into `{project-root}/.sdlc/testing.json` (create the file + parent `.sdlc/` if absent):
67
+
68
+ ```json
69
+ {
70
+ "tool": "playwright",
71
+ "provider": "playwright-mcp",
72
+ "project_url": "tests/playwright.config.ts",
73
+ "auth": "user",
74
+ "suites": { "backend": null, "mobile": null },
75
+ "connectedAt": "<YYYY-MM-DD>",
76
+ "lastSyncedAt": "<YYYY-MM-DD>",
77
+ "source": "playwright-mcp"
78
+ }
79
+ ```
80
+
81
+ - `tool: "none"` records a deliberate artifacts-only project: `{ "tool": "none", "provider": null,
82
+ "source": "unavailable", ... }`.
83
+ - `connect` is **idempotent** — re-running it overwrites the single connection in place (a project has
84
+ one testing tool at a time; switching tools is just another `connect`).
85
+
86
+ ### Step 3 — Report (never auto-advance)
87
+ Report the connected `tool`, its `provider`, whether the MCP is available (or that `yad-test-cases` will
88
+ degrade to artifacts-only), the `project_url`, and that **`yad-test-cases` will now generate/link the
89
+ automation tests here**. Nothing auto-advances; this is setup.
90
+
91
+ ## Other actions
92
+
93
+ - **`refresh`** — re-detect the MCP and update `lastSyncedAt` (after the user authenticates a session or
94
+ changes tools). Same machinery as `connect`. Re-detection may flip `source` between an MCP id and
95
+ `unavailable` — report the change.
96
+ - **`list`** — print the current connection: `tool`, `provider`, `project_url`, the suite mapping, and a
97
+ **available/unavailable** flag for the MCP (best-effort, the user's own session). No testing tool
98
+ connected ⇒ "artifacts-only".
99
+ - **`disconnect`** — remove the registry file (or set `tool: "none"`). The testing tool's own
100
+ project/suites are **never touched** — only the hub's record of them.
101
+
102
+ ## Hard rules
103
+
104
+ - **Local-user / MCP-session auth only; store no tokens.** Connect through the user's authenticated MCP
105
+ session; never embed a token or any credential in the registry. `project_url`/`suites` are plain
106
+ references.
107
+ - **Degrade gracefully.** No testing tool / no MCP → `yad-test-cases` runs artifacts-only with no error.
108
+ The testing tool is additive, never a blocker — the same discipline as the design tool and Impeccable.
109
+ - **Setup, not a gate.** Never touch `.sdlc/state.json`, approvals, or the contract lock from here.
110
+ - **Idempotent + refreshable.** `connect`/`refresh` are safe to re-run; a project carries one testing
111
+ connection at a time.
112
+ - **Describe the connection; do not author tests here.** This skill records *how to reach* the tool. The
113
+ actual automation tests are generated/linked by `yad-test-cases`, per epic.
114
+
115
+ ## Reference
116
+ - Registry schema + freshness rule: `references/testing-registry.md`.
117
+ - MCP detection per provider, the generate-vs-link recipes, the degrade path, and the honest
118
+ write-vs-read-only MCP capability note: `references/testing-context.md`.
119
+ - The connect pattern this mirrors (design tool): `../yad-connect-design/SKILL.md`.
120
+ - The consumer — how `yad-test-cases` generates/links and writes `test-links.json`:
121
+ `../yad-test-cases/SKILL.md`.
@@ -0,0 +1,67 @@
1
+ # Testing context — MCP detection, generate vs link, and the degrade path
2
+
3
+ How a connected testing tool turns into the actual automation tests the test-cases step materializes.
4
+ The testing tool is reached through its **MCP** (a harness MCP server), the same shape as the design
5
+ tool — detect it, use it when present, degrade cleanly when absent. This is the testing-side analogue of
6
+ `yad-connect-design`'s design-context.
7
+
8
+ ## Provider detection
9
+
10
+ `connect`/`refresh` records `provider` (the concrete MCP) and `source` (the MCP id, or `unavailable`).
11
+ Detection is best-effort against the user's own authenticated MCP session:
12
+
13
+ | `tool` | MCP / provider | Capability |
14
+ |--------|----------------|------------|
15
+ | `playwright` | a Playwright MCP | **generate** — author + run E2E/API specs against the app |
16
+ | `cypress` | the Cypress MCP | **generate** — author + run Cypress specs |
17
+ | `pytest` | a pytest MCP | **generate** — author + run service-layer tests |
18
+ | any | a read-only runner MCP | **link** — reference an existing suite and read results back |
19
+ | other | the adapter's named MCP | per that adapter |
20
+
21
+ **Honest capability note:** not every testing-tool MCP can *write* tests. A read-only runner MCP
22
+ supports **link + read-back** only; *generate* needs a write-capable provider. `yad-test-cases` picks
23
+ the direction the connected provider actually supports and records which one it used
24
+ (`direction: generated | linked`). It never claims to have generated tests a read-only MCP cannot
25
+ produce.
26
+
27
+ ## Generate (write automation tests into the repo)
28
+
29
+ When the connected provider is write-capable, the `test architect` lens (Murat, `bmad-tea` +
30
+ `bmad-testarch-automate`) produces the epic's automation tests in the connected code repo(s), covering
31
+ the cases `test-cases.md` enumerates and the acceptance criteria the stories define:
32
+
33
+ - **Playwright** — the lens authors `*.spec.ts` E2E/API specs (reusing the repo's existing fixtures and
34
+ the code-maps from `yad-test-cases` Step 2b), one spec per high-priority (P0/P1) case, and runs them
35
+ via the MCP to confirm they execute.
36
+ - **Cypress / pytest** — the lens authors the equivalent specs in that framework's layout.
37
+
38
+ Reuse what already exists: load the connected code repos' code-maps (`yad-test-cases` Step 2b) so
39
+ generated tests target real endpoints/components, not invented ones, and prefer the lowest useful test
40
+ level (unit > integration > E2E) per Murat's principles.
41
+
42
+ ## Link (reference an existing suite)
43
+
44
+ When a suite already exists (or the provider is read-only), point `yad-test-cases` at it and **read the
45
+ suite back** so `test-cases.md` reflects the real tests: list each test as a case, capture its
46
+ path/name + URL, and map it to the story it covers.
47
+
48
+ ## Write back the linkage (done by `yad-test-cases`, per epic)
49
+
50
+ Either direction ends by writing `epics/EP-<slug>/.sdlc/test-links.json` — the machine-readable
51
+ case→test map — and a `## Automation (<tool>)` section in `test-cases.md` linking each case to its test.
52
+ The tests themselves live in the code repo; the hub keeps the *links* and the Markdown spec beside the
53
+ other epic artifacts.
54
+
55
+ ## Degrade path (no MCP / no tool)
56
+
57
+ If `testing.json` is absent, `tool: "none"`, or `source: "unavailable"`, `yad-test-cases` runs
58
+ **artifacts-only**: it authors `test-cases.md` exactly as before and records `testing: none` in the
59
+ frontmatter with a one-line note (mirroring the `design: none` degrade). No error — the testing tool is
60
+ purely additive.
61
+
62
+ ## Staleness / refresh
63
+
64
+ A re-generated or hand-edited suite is like a moved code repo: `yad-test-cases` **flags** a divergence
65
+ and lets a human decide (re-run the step, or `yad-connect-testing` action: refresh). It never silently
66
+ overwrites a hand-written suite — refreshing the automation is a human decision, the same discipline as
67
+ `code_context.refresh: human`.
@@ -0,0 +1,55 @@
1
+ # Testing registry — schema + freshness rule
2
+
3
+ The registry is the product hub's record of which testing tool is connected and how to reach it. It is
4
+ **project-wide** (one testing tool per project, shared across every epic), so it lives at the product
5
+ root, not under any `epics/EP-<slug>/.sdlc/`.
6
+
7
+ ## Location
8
+
9
+ `{project-root}/.sdlc/testing.json`
10
+
11
+ (`config.yaml` `testing.registry`.) Create the file and its parent `.sdlc/` on the first `connect`.
12
+
13
+ ## Schema
14
+
15
+ ```json
16
+ {
17
+ "tool": "playwright", // playwright | cypress | pytest | <adapter id> | none (artifacts-only)
18
+ "provider": "playwright-mcp", // the concrete MCP: playwright-mcp | cypress-mcp | pytest-mcp | null
19
+ "project_url": "tests/playwright.config.ts", // project/config reference; null if none yet
20
+ "auth": "user", // ALWAYS the user's own MCP session — never a token
21
+ "suites": { "backend": null, "mobile": null }, // optional default suite refs per repo
22
+ "connectedAt": "2026-06-13", // first connect (YYYY-MM-DD)
23
+ "lastSyncedAt": "2026-06-13", // last connect/refresh
24
+ "source": "playwright-mcp" // the MCP detected at connect | unavailable (degraded)
25
+ }
26
+ ```
27
+
28
+ ## Rules
29
+
30
+ - **`tool`** selects the adapter; it MUST be one of `config.yaml` `testing.tools` (or `none`). At
31
+ **connect** time an unknown tool is normalized to `testing.primary` with a warning (so the registry
32
+ never persists an unknown value); a registry hand-edited to an unknown or missing tool **fails
33
+ `doctor`** with `YAD-CFG-003` and must be fixed.
34
+ - **Auth is never stored.** No token, API key, or any credential in the registry. `project_url` and
35
+ `suites` are plain references; `connect` reaches the tool through the user's authenticated MCP session.
36
+ - **`connect` overwrites in place** — a project carries exactly one testing connection at a time;
37
+ switching tools is just another `connect`. There is no array (unlike `repos.json`).
38
+ - **`source`** is the authority for availability: an MCP id (`playwright-mcp` / `cypress-mcp` / …) means
39
+ `yad-test-cases` can generate/link; `unavailable` means `yad-test-cases` degrades to artifacts-only.
40
+ `refresh` re-detects and may flip it.
41
+ - **`tool: "none"`** is a valid, deliberate state: a project that has chosen artifacts-only.
42
+ `yad-test-cases` treats it exactly like an absent registry.
43
+ - **`disconnect`** removes the file (or sets `tool: "none"`). The testing tool's own project/suites are
44
+ never touched.
45
+
46
+ ## Git tracking
47
+
48
+ Commit the **registry** (`testing.json`) — it is small, reviewable, and holds no secrets (references
49
+ only). This mirrors how `repos.json`, `hub.json`, and `design.json` are committed.
50
+
51
+ ## Greenfield
52
+
53
+ A brand-new product hub has no `testing.json`. That is valid — `yad-test-cases` treats "no testing tool
54
+ connected" the same as `tool: "none"` and produces the Markdown test-case artifact only. The registry
55
+ appears the first time `connect` runs.
@@ -14,7 +14,7 @@ drafted, control passes to `yad-review-gate`.
14
14
  - **Analysis ran** — `.sdlc/state.json` already exists with `currentStep == "epic"`. The epic **reads
15
15
  `analysis.md`** as its shaped input and does not re-seed state.
16
16
  - **Analysis skipped** (the default) — no `state.json` yet. The epic is the entry point: it shapes the
17
- idea inline with the analyst, assigns `EP-<slug>`, and seeds the **8-step** chain.
17
+ idea inline with the analyst, assigns `EP-<slug>`, and seeds the **10-step** chain.
18
18
 
19
19
  This skill enforces the build plan's core rules: all state lives in files; IDs are generated by the
20
20
  engine (never typed by hand); front steps are locked to `human_approve`.
@@ -105,9 +105,9 @@ Fill the body with the user; leave `owner` / `technical_product_owner` for the u
105
105
  `repos` to the repos this epic will touch.
106
106
 
107
107
  ### Step 5 — Seed the state machine — analysis-skipped only
108
- *(Skip when analysis ran — `yad-analysis` already seeded the 10-step chain. Go to Step 5b.)*
109
- Create `{project-root}/epics/EP-<slug>/.sdlc/state.json` describing the full **8-step** front-state
110
- sequence (no analysis), all steps defaulting to `automation: human_approve`, with the four authoring
108
+ *(Skip when analysis ran — `yad-analysis` already seeded the 12-step chain. Go to Step 5b.)*
109
+ Create `{project-root}/epics/EP-<slug>/.sdlc/state.json` describing the full **10-step** front-state
110
+ sequence (no analysis), all steps defaulting to `automation: human_approve`, with the five authoring
111
111
  steps **locked**. Use this exact shape (see `references/state-schema.md`):
112
112
 
113
113
  ```json
@@ -123,7 +123,9 @@ steps **locked**. Use this exact shape (see `references/state-schema.md`):
123
123
  { "id": "ui-design", "type": "author", "artifact": "ui-design.md", "assistance": "review", "automation": "human_approve", "locked": true, "status": "blocked", "risk_tags": [] },
124
124
  { "id": "ui-design-review", "type": "review+approve", "artifact": "ui-design.md", "assistance": "review", "automation": "human_approve", "locked": true, "status": "blocked", "risk_tags": [] },
125
125
  { "id": "stories", "type": "author", "artifact": "stories/", "assistance": "review", "automation": "human_approve", "locked": true, "status": "blocked", "risk_tags": [] },
126
- { "id": "stories-review", "type": "review+approve", "artifact": "stories/", "assistance": "review", "automation": "human_approve", "locked": true, "status": "blocked", "risk_tags": [] }
126
+ { "id": "stories-review", "type": "review+approve", "artifact": "stories/", "assistance": "review", "automation": "human_approve", "locked": true, "status": "blocked", "risk_tags": [] },
127
+ { "id": "test-cases", "type": "author", "artifact": "test-cases.md", "assistance": "review", "automation": "human_approve", "locked": true, "status": "blocked", "risk_tags": [] },
128
+ { "id": "test-cases-review", "type": "review+approve", "artifact": "test-cases.md", "assistance": "review", "automation": "human_approve", "locked": true, "status": "blocked", "risk_tags": [] }
127
129
  ]
128
130
  }
129
131
  ```
@@ -131,6 +133,9 @@ steps **locked**. Use this exact shape (see `references/state-schema.md`):
131
133
  Notes:
132
134
  - `architecture-review` carries `risk_tags: ["contract"]` so the gate escalates it by default
133
135
  (build plan §4): the contract review needs domain owners, not just owner + 1.
136
+ - `test-cases` / `test-cases-review` are a **parallel, non-blocking track**: they seed `blocked` and open
137
+ when `stories-review` passes — at which point the epic is already `ready-for-build`, so the build half
138
+ runs alongside the tester. They never gate `ready-for-build` (see `references/state-schema.md`).
134
139
  - Also create an empty approvals ledger `{project-root}/epics/EP-<slug>/.sdlc/approvals.json`
135
140
  and an empty comments ledger `{project-root}/epics/EP-<slug>/.sdlc/comments.json`, each containing
136
141
  `[]`, and the `reviews/` directory. (`comments.json` is the machine-readable counterpart to the
@@ -17,7 +17,7 @@ Each `steps[]` entry:
17
17
 
18
18
  | Field | Values | Meaning |
19
19
  |-------|--------|---------|
20
- | `id` | `analysis`, `analysis-review`, `epic`, `epic-review`, `architecture`, `architecture-review`, `ui-design`, `ui-design-review`, `stories`, `stories-review` | Step identity. |
20
+ | `id` | `analysis`, `analysis-review`, `epic`, `epic-review`, `architecture`, `architecture-review`, `ui-design`, `ui-design-review`, `stories`, `stories-review`, `test-cases`, `test-cases-review` | Step identity. |
21
21
 
22
22
  ### Two valid chain shapes (analysis is optional)
23
23
 
@@ -26,20 +26,37 @@ ran `yad-analysis` before the epic. The entry-point skill (whichever runs first)
26
26
  that assigns `EP-<slug>` and seeds `state.json` + the empty ledgers; the other skill detects an
27
27
  existing `state.json` and does **not** re-seed.
28
28
 
29
- - **With analysis** (10 steps — `yad-analysis` seeded the chain):
29
+ - **With analysis** (12 steps — `yad-analysis` seeded the chain):
30
30
  `analysis → analysis-review → epic → epic-review → architecture → architecture-review → ui-design →
31
- ui-design-review → stories → stories-review`. Seeded `currentStep` is `analysis-review`; `epic`
32
- starts `blocked`.
33
- - **Without analysis** (8 steps — `yad-epic` is the entry point, the default):
34
- `epic → epic-review → … → stories-review`. Seeded `currentStep` is `epic-review`.
35
-
36
- After `stories-review` passes, `currentStep` becomes the `ready-for-build` sentinel either way.
37
- `analysis-review` carries no `risk_tags` (base rule: owner + 1 reviewer).
31
+ ui-design-review → stories → stories-review test-cases test-cases-review`. Seeded `currentStep`
32
+ is `analysis-review`; `epic` starts `blocked`.
33
+ - **Without analysis** (10 steps — `yad-epic` is the entry point, the default):
34
+ `epic → epic-review → … → stories-review → test-cases → test-cases-review`. Seeded `currentStep` is
35
+ `epic-review`.
36
+
37
+ `analysis-review`, `ui-design-review`, and `test-cases-review` carry no `risk_tags` (base rule:
38
+ owner + 1 reviewer).
39
+
40
+ ### `test-cases` is a parallel, non-blocking track
41
+
42
+ `test-cases` (and its `test-cases-review` gate) sit in `steps[]` after `stories-review`, but they are a
43
+ **parallel track that does not gate the build half**. When `stories-review` passes, `advanceState`:
44
+ - sets `currentStep` to the **`ready-for-build`** sentinel — so the build half (`yad-spec` → … keyed off
45
+ `currentStep == "ready-for-build"`) can start **immediately**, and
46
+ - opens `test-cases` (`blocked` → `in_progress`) so the tester can work **in parallel**.
47
+
48
+ The `test-cases` track is therefore driven by its own step `status`, **not** by `currentStep`:
49
+ `yad-test-cases` proceeds when `test-cases.status == "in_progress"`, and neither it nor the
50
+ `test-cases-review` gate (`advanceState` / `markInReview`) ever moves `currentStep` away from
51
+ `ready-for-build`. So implementation and test-case authoring run at the same time; the epic is
52
+ `ready-for-build` the moment the **stories** gate passes, whether or not test cases are done. (For an old
53
+ epic seeded before this step existed, `stories-review` → `ready-for-build` with no test-cases track —
54
+ unchanged.)
38
55
 
39
56
  ### Authoring branches
40
57
 
41
58
  Each front **authoring** step opens its own git branch at the start of the step, named
42
- `<step>/EP-<slug>` where `<step>` ∈ `analysis | epic | architecture | ui-design | stories`
59
+ `<step>/EP-<slug>` where `<step>` ∈ `analysis | epic | architecture | ui-design | stories | test-cases`
43
60
  (`config.yaml` `defaults.front_authoring_branch`). This is **distinct** from the review branch
44
61
  `review/EP-<slug>/<artifact-base>` that `yad-hub-bridge` opens later for the review PR/MR.
45
62
 
@@ -99,6 +116,20 @@ chain is unchanged — this is an *output enrichment*, mirrored by the `design:`
99
116
  "source": "<mcp id>" }
100
117
  ```
101
118
 
119
+ ## `test-links.json`
120
+ Present only when the `test-cases` step materialized automation in a connected testing tool
121
+ (`yad-connect-testing` → `.sdlc/testing.json`). Written by `yad-test-cases`, the machine-readable
122
+ case→test map (sibling of `contract-lock.json` / `design-links.json`; the locked `state.json` step shape
123
+ is untouched). The `test-cases` step chain is unchanged — this is an *output enrichment*, mirrored by the
124
+ `testing:` frontmatter block and the `## Automation (<tool>)` section in `test-cases.md`. Absent when the
125
+ step ran artifacts-only (`testing: none`).
126
+
127
+ ```json
128
+ { "tool": "playwright", "suite": "<url/path>", "generatedAt": "<YYYY-MM-DD>", "direction": "generated|linked",
129
+ "tests": [ { "case": "<id>", "story": "EP-<slug>-S0N", "repo": "<name>", "level": "unit|integration|e2e", "path": "<test path>", "url": "<url>" } ],
130
+ "source": "<mcp id>" }
131
+ ```
132
+
102
133
  ## `reviews/`
103
134
  Human-readable review records, one file per round:
104
135
  `reviews/<artifact-base>--<YYYY-MM-DD>--<status>.md` where `status` ∈ `comments` | `approved`
@@ -106,7 +137,7 @@ and `<artifact-base>` is the artifact without extension (e.g. `epic`, `architect
106
137
 
107
138
  ## Dial defaults & locks
108
139
  - Every step defaults to `automation: human_approve` (build plan §2).
109
- - The four authoring front steps and their reviews are `locked: true` — the engine refuses to set
140
+ - The five authoring front steps and their reviews are `locked: true` — the engine refuses to set
110
141
  them to `machine_advance` in this version (build plan §1, §8.7). Only back states (build pipeline,
111
142
  steps 9–14) may move toward machine-advance in a later iteration.
112
143
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: yad-hub-bridge
3
- description: 'The templated PR/MR bridge for the front-half review gate. When the product hub has a platform (.sdlc/hub.json), it opens a review PR/MR on the hub for an authored artifact (the optional analysis / epic / architecture+contract / ui-design / stories), sets the required reviewers/labels from the routing rule, and provides the read-only gh/glab recipes that yad-review-gate uses to pull platform comments + approvals back into the file ledger. Can also wire event-driven sync on the hub: a CI workflow that runs `yad gate ci` whenever a reviewer approves / requests changes / a human merges, committing the ledger to the default branch. Local-user auth only — no stored tokens. The file ledger stays the source of truth; degrades to the file-only gate when there is no platform / no CLI. Use when the user says "open the review PR", "route the review", "wire the gate sync", or it is invoked by yad-review-gate open/sync.'
3
+ description: 'The templated PR/MR bridge for the front-half review gate. When the product hub has a platform (.sdlc/hub.json), it opens a review PR/MR on the hub for an authored artifact (the optional analysis / epic / architecture+contract / ui-design / stories / test-cases), sets the required reviewers/labels from the routing rule, and provides the read-only gh/glab recipes that yad-review-gate uses to pull platform comments + approvals back into the file ledger. Can also wire event-driven sync on the hub: a CI workflow that runs `yad gate ci` whenever a reviewer approves / requests changes / a human merges, committing the ledger to the default branch. Local-user auth only — no stored tokens. The file ledger stays the source of truth; degrades to the file-only gate when there is no platform / no CLI. Use when the user says "open the review PR", "route the review", "wire the gate sync", or it is invoked by yad-review-gate open/sync.'
4
4
  ---
5
5
 
6
6
  # SDLC — Hub Review Bridge (the templated PR/MR bridge)
@@ -29,7 +29,7 @@ keeps platform mechanics out of the gate). `yad-review-gate` *calls* it; it neve
29
29
  ## Inputs
30
30
 
31
31
  - `epic` — the `EP-<slug>` under review.
32
- - `artifact` — the artifact file (`analysis.md` | `epic.md` | `architecture.md` | `ui-design.md` | `stories/`).
32
+ - `artifact` — the artifact file (`analysis.md` | `epic.md` | `architecture.md` | `ui-design.md` | `stories/` | `test-cases.md`).
33
33
  - `action` — `open` | `route` | `wire` (default `route`). (`sync`'s ledger writes live in
34
34
  `yad-review-gate`; this skill provides the read recipes `sync` calls — see `references/bridge.md`.)
35
35
 
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  name: yad-review-gate
3
- description: 'The reusable team review + approve gate for the SDLC. Shares an authored artifact for review, records reviewer comments and approvals as files, enforces the owner + 1 reviewer rule (escalating to domain owners on contract/auth/payments), and advances the epic state ONLY when approval is recorded. Use when the user says "review the analysis/epic/architecture/UI/stories", "comment", "approve", or "advance the gate".'
3
+ description: 'The reusable team review + approve gate for the SDLC. Shares an authored artifact for review, records reviewer comments and approvals as files, enforces the owner + 1 reviewer rule (escalating to domain owners on contract/auth/payments), and advances the epic state ONLY when approval is recorded. Use when the user says "review the analysis/epic/architecture/UI/stories/test-cases", "comment", "approve", or "advance the gate".'
4
4
  ---
5
5
 
6
6
  # SDLC — Team Review Gate (build plan §3 piece 2, §4, §5)
7
7
 
8
8
  **Goal:** One reusable step type that turns any authored artifact into a gated, human-approved
9
9
  review. Every `review+approve` step in the workflow (the optional analysis, epic, architecture+contract,
10
- UI, stories) uses this exact gate. **No step advances until its review is approved** and recorded as a
11
- file. The `analysis-review` and `epic`/`ui-design` reviews use the **base** rule (owner + 1 reviewer);
12
- escalation applies only where `risk_tags` or per-repo routing call for it.
10
+ UI, stories, test-cases) uses this exact gate. **No step advances until its review is approved** and
11
+ recorded as a file. The `analysis-review`, `epic`/`ui-design`, and `test-cases` reviews use the **base**
12
+ rule (owner + 1 reviewer); escalation applies only where `risk_tags` or per-repo routing call for it.
13
13
 
14
14
  This gate is **swappable and file-driven**: it talks only through files. A front step advances only on a
15
15
  human act — recording an approval and `advance`, or (with the bridge) **merging the approved,
@@ -87,7 +87,7 @@ counterpart to `approvals.json`):
87
87
  Then help the **owner address the comments** using the agent lens listed for this step
88
88
  (analysis → `analyst`; epic → `pm`; architecture → `architect`; ui-design → `ux-designer`;
89
89
  stories → `pm`, with `architect` for technical detail — there is **no `sm` agent**, Phase 0
90
- Deviation 1). Update the authored artifact in place.
90
+ Deviation 1; test-cases → `test architect` / Murat, `bmad-tea`). Update the authored artifact in place.
91
91
  Repeat comment→address rounds until reviewers are satisfied. **Commenting never advances the gate.**
92
92
 
93
93
  **`approve`** — Record an approval. Append to `.sdlc/approvals.json`:
@@ -153,13 +153,16 @@ If the predicate **fails**: report exactly which approvals are still missing and
153
153
 
154
154
  If the predicate **passes**:
155
155
  - Mark this review step `status: "done"`.
156
- - If there **is** a next step in `steps[]`: set it `status` from `blocked` to `in_progress`
157
- (authoring) or `in_review`, and set `currentStep` to that next step.
158
- - If this is the **last** step (`stories-review`, the final review): there is no further front step —
159
- set `currentStep: "ready-for-build"` (the Phase 3 handoff sentinel; it is intentionally not a
160
- `steps[]` entry). The front half is complete.
156
+ - **`stories-review`** is the end of the gating chain: set `currentStep: "ready-for-build"` (the Phase 3
157
+ handoff sentinel; intentionally not a `steps[]` entry) **and** open the parallel **`test-cases`** track
158
+ (set its step `blocked` `in_progress`). The build half can now start **and** the tester can work
159
+ `test-cases` at the same time.
160
+ - **`test-cases-review`** is the parallel track's gate: mark it `done` but **leave `currentStep` at
161
+ `ready-for-build`** — completing test cases must never pull the epic back from the build half.
162
+ - Any **other** review step: set the next step in `steps[]` from `blocked` to `in_progress` (authoring)
163
+ or `in_review`, and set `currentStep` to that next step.
161
164
  - Write `state.json`. Report the advance and what the next authored artifact is (or that the epic is
162
- now `ready-for-build`).
165
+ now `ready-for-build`, with `test-cases` running in parallel).
163
166
 
164
167
  ### PR-driven automation (the `yad gate` CLI)
165
168
  When the hub has a platform, the mechanical `open`/`sync`/`advance` is performed deterministically by the