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.
package/CHANGELOG.md CHANGED
@@ -1,16 +1,9 @@
1
- # [2.2.0](https://github.com/abdelrahmannasr/yadflow/compare/v2.1.0...v2.2.0) (2026-06-13)
2
-
3
-
4
- ### Bug Fixes
5
-
6
- * address code review on the design-tool connection ([1275146](https://github.com/abdelrahmannasr/yadflow/commit/1275146e9e24251d481d90eb26894a729e894997))
1
+ # [2.3.0](https://github.com/abdelrahmannasr/yadflow/compare/v2.2.0...v2.3.0) (2026-06-13)
7
2
 
8
3
 
9
4
  ### Features
10
5
 
11
- * add yad-connect-design skill (Figma-first, pluggable) ([d1de46d](https://github.com/abdelrahmannasr/yadflow/commit/d1de46d2b08995db0228e60a58877649649b5cd0))
12
- * materialize the feature design in yad-ui (generate or link) ([e440571](https://github.com/abdelrahmannasr/yadflow/commit/e44057131e86fed45d9a9c4151cd762eb98f24e8))
13
- * wire the design-tool connection through the CLI ([1867ed4](https://github.com/abdelrahmannasr/yadflow/commit/1867ed433dd57025ae4143f5a236db32f1a99c41))
6
+ * add parallel test-cases step with pluggable testing-tool connection ([#45](https://github.com/abdelrahmannasr/yadflow/issues/45)) ([19c282f](https://github.com/abdelrahmannasr/yadflow/commit/19c282f6bd737364bca122179b05de8ea94493a9))
14
7
 
15
8
  # [1.1.0](https://github.com/abdelrahmannasr/sdlc-workflow/compare/v1.0.3...v1.1.0) (2026-06-09)
16
9
 
package/README.md CHANGED
@@ -49,8 +49,9 @@ human**. Detailed walkthroughs for each phase follow below.
49
49
  | `skills/yad-architecture/` | Front state 3: author `architecture.md` + the locked `contract.md`; hash-lock the contract surface. |
50
50
  | `skills/yad-ui/` | Front state 5: author `ui-design.md` + `DESIGN.md` (Impeccable slash-commands, or graceful fallback). |
51
51
  | `skills/yad-stories/` | Front state 7: break the epic into repo-tagged stories with stable `EP-<slug>-S0N` IDs. |
52
+ | `skills/yad-test-cases/` | Front state 9: with the test architect author `test-cases.md`; implement the automation in the connected testing tool, or produce artifacts only. |
52
53
  | `skills/yad-connect-repos/` | Connect code repos to the hub (GitHub/GitLab, local-user auth); cache a Repomix pack + **code-map** per repo so the front phases are code-aware. |
53
- | `skills/yad-review-gate/` | The reusable **team review + approve gate** (used for all four reviews). |
54
+ | `skills/yad-review-gate/` | The reusable **team review + approve gate** (used for all five reviews). |
54
55
  | `skills/yad-spec/` | Build Step A: run the Spec Kit ceremony once per story per repo → `specs/<story-id>/`. |
55
56
  | `skills/yad-implement/` | Build Step B: implement ONE atomic task as a small diff on its own branch. |
56
57
  | `skills/yad-checks/` | Build Step C: wire + run the CI gates (spec-link, contract-check, build/test/lint, verified-commits). |
@@ -122,18 +123,21 @@ a manual `yad gate sync` racing CI, or GitLab pipelines — two simultaneous syn
122
123
  *commits* via the rebase retry but each works from the state it read at start, so the rarer of two
123
124
  simultaneous advancements can be lost; the next event or scheduled sweep re-syncs and converges.
124
125
 
125
- ### What `setup` walks you through (8 steps)
126
+ ### What `setup` walks you through (9 steps)
126
127
 
127
128
  1. **Preflight** — confirm the hub is a git repo (offers `git init`); check `git`/`node`/`npx`.
128
- 2. **Install the module** — copy all 18 `yad-*` skills into the IDE skill dirs you pick
129
+ 2. **Install the module** — copy all 20 `yad-*` skills into the IDE skill dirs you pick
129
130
  (`.claude/`, `.agents/`, `.zencoder/`, `.opencode/`) and register `_bmad/sdlc/`.
130
131
  3. **Hub platform & roster** — detect GitHub/GitLab from the remote; record reviewers → `.sdlc/hub.json`.
131
132
  4. **Connect a design tool** — record the design tool (Figma / pencil / none) → `.sdlc/design.json` so
132
133
  the UI step can materialize the design; the MCP itself is confirmed later by `yad-connect-design`.
133
- 5. **Connect code repos** — register each repo into `.sdlc/repos.json` and cache a Repomix pack.
134
- 6. **Wire each repo** CI gates, PR/MR template, and review-comment scaffold.
135
- 7. **AI review** — optionally write `.coderabbit.yaml`.
136
- 8. **Done** — stamp `.sdlc/cli-version.json` and hand off the AI-only steps (code-maps; first epic).
134
+ 5. **Connect a testing tool** — record the testing tool (Playwright / cypress / pytest / none) →
135
+ `.sdlc/testing.json` so the test-cases step can implement the automation; the MCP itself is confirmed
136
+ later by `yad-connect-testing`.
137
+ 6. **Connect code repos** — register each repo into `.sdlc/repos.json` and cache a Repomix pack.
138
+ 7. **Wire each repo** — CI gates, PR/MR template, and review-comment scaffold.
139
+ 8. **AI review** — optionally write `.coderabbit.yaml`.
140
+ 9. **Done** — stamp `.sdlc/cli-version.json` and hand off the AI-only steps (code-maps; first epic).
137
141
 
138
142
  The deterministic file work runs automatically; the AI-only steps are handed to the Claude Code skills
139
143
  with a printed next-action. Re-run `… check --fix` any time the workflow updates — it never re-asks for
@@ -170,10 +174,11 @@ with a fix-it hint per finding. Failures carry stable, greppable codes, also pri
170
174
  | `YAD-STATE-003` | a registered repo path is missing or not a git repo | fix the path in `.sdlc/repos.json` or re-connect the repo |
171
175
  | `YAD-CFG-001` | `hub.json` names an unknown platform | expected `github`, `gitlab`, or `null` — fix it or re-run `yad setup` |
172
176
  | `YAD-CFG-002` | `design.json` names an unknown design tool | expected one of `config.yaml` `design.tools` (e.g. `figma`, `pencil`), or `none` — fix it or re-run `yad setup` |
177
+ | `YAD-CFG-003` | `testing.json` names an unknown testing tool | expected one of `config.yaml` `testing.tools` (e.g. `playwright`, `cypress`, `pytest`), or `none` — fix it or re-run `yad setup` |
173
178
 
174
179
  Filing a bug? Attach `yad doctor --json` — it contains no secrets (names, paths, and check results only).
175
180
 
176
- ## Agent skills (all 18)
181
+ ## Agent skills (all 20)
177
182
 
178
183
  The CLI **installs and wires** the module; the skills below are the **agents you invoke by name** in your
179
184
  AI IDE (e.g. *“run `yad-epic`”*) to actually do the work. State lives in files you can also edit
@@ -191,12 +196,17 @@ directly. Each skill stops at a gate and never auto-advances unless a step has *
191
196
  Records the tool + project/file references in `.sdlc/design.json` (local-user / MCP-session auth, no
192
197
  stored tokens), detecting the design-tool MCP and degrading to markdown-only when absent. Idempotent
193
198
  and refreshable; one connection per project.
199
+ - **`yad-connect-testing`** — Connects a testing tool (Playwright-first, pluggable) so the test-cases
200
+ step can implement the actual automation tests inside it, alongside the Markdown. Records the tool +
201
+ project/suite references in `.sdlc/testing.json` (local-user / MCP-session auth, no stored tokens),
202
+ detecting the testing-tool MCP and degrading to artifacts-only when absent. Idempotent and
203
+ refreshable; one connection per project.
194
204
 
195
205
  ### Front half — author the "thinking" (once per epic, human-gated)
196
206
 
197
207
  - **`yad-analysis`** — *Optional* front state 1. With the analyst, pressure-test a feature idea
198
208
  and write the discovery brief into `analysis.md`. Assigns the `EP-<slug>` ID and seeds `.sdlc/` state
199
- (the 10-step chain that puts analysis before epic). If skipped, the epic step does this shaping inline.
209
+ (the 12-step chain that puts analysis before epic). If skipped, the epic step does this shaping inline.
200
210
  - **`yad-epic`** — The epic front state. Shape the idea with the analyst (or read `analysis.md`
201
211
  when it already ran), then write the epic with the pm into `epic.md`. The entry point when analysis is
202
212
  skipped: assigns the `EP-<slug>` ID and seeds `.sdlc/` state.
@@ -211,6 +221,13 @@ directly. Each skill stops at a gate and never auto-advances unless a step has *
211
221
  - **`yad-stories`** — Front state 7. With the pm, break the approved epic into user stories, each
212
222
  tagged with the repos that must implement it. Assigns zero-padded `EP-<slug>-S0N` IDs, one file per
213
223
  story under `stories/`. Reads epic + architecture + contract + UI.
224
+ - **`yad-test-cases`** — Front state 9, a **parallel, non-blocking** track: it opens when the stories
225
+ gate passes (the epic is already `ready-for-build`, so the build half runs alongside it). With the
226
+ test architect (Murat), author `test-cases.md` covering the approved stories (risk-based P0–P3 cases +
227
+ story→case traceability). When a testing tool is connected (`yad-connect-testing`), also **implements
228
+ the automation tests** in the connected code repo(s) (generate or link), recording the case→test map in
229
+ `test-links.json`; degrades to artifacts-only otherwise. Reads epic + architecture + contract + UI +
230
+ stories.
214
231
 
215
232
  ### The review gate (cross-cutting — used by every review)
216
233
 
@@ -269,7 +286,7 @@ merging the approved, fully-resolved review PR — never on a machine.
269
286
 
270
287
  As of **Phase 4a** the `automation` dial is no longer inert: the orchestrator `yad-run` reads it and,
271
288
  for the safe **back** steps, advances on its own when a step is set to `machine_advance` (and has
272
- *earned* it — see "Run the back half on the dial" below). The engineer review and all four front
289
+ *earned* it — see "Run the back half on the dial" below). The engineer review and all five front
273
290
  states stay `human_approve` forever.
274
291
 
275
292
  ## Using the workflow end to end (all the steps, in order)
@@ -320,7 +337,10 @@ threads are resolved. Details: **“Run the full front half by hand”** below.
320
337
  7. `yad-architecture` → `architecture.md` + locked `contract.md` → review (**escalated**: contract).
321
338
  8. `yad-ui` → `ui-design.md` + `DESIGN.md` → review (base rule).
322
339
  9. `yad-stories` → repo-tagged `stories/EP-<slug>-S0N.md` → review (**per-repo**).
323
- → `state.json` reaches `currentStep: ready-for-build`.
340
+ → `state.json` reaches `currentStep: ready-for-build` — **the build half can start now.**
341
+ 10. `yad-test-cases` → `test-cases.md` (+ automation tests when a testing tool is connected) → review (base rule).
342
+ **Parallel, non-blocking:** opens when the stories gate passes and runs alongside the build half; its
343
+ review never moves `currentStep` off `ready-for-build`.
324
344
 
325
345
  ### B — Build half (per story, per repo)
326
346
  From a `ready-for-build` story, for **each** repo the story is tagged with. Details: **“Run the full
@@ -355,12 +375,13 @@ Details: **“Run the back half on the dial”** below.
355
375
  ## Run the full front half by hand
356
376
 
357
377
  The front half walks **epic → review → architecture+contract → review → UI design → review → stories
358
- → review → `ready-for-build`**. It is all files under `epics/EP-<slug>/`. The skills below guide you,
359
- but you can also edit the files directly that's the point.
378
+ → review → `ready-for-build`**, then **test cases review** runs as a **parallel, non-blocking track**
379
+ alongside the build half. It is all files under `epics/EP-<slug>/`. The skills below guide you, but you
380
+ can also edit the files directly — that's the point.
360
381
 
361
382
  Each authoring step is the same shape: an author skill produces an artifact, sets its step `done`,
362
383
  moves `currentStep` to the matching review, and **stops at the gate**. Then **`yad-review-gate`**
363
- (one gate, reused for all four reviews) takes `open → comment → approve → advance`. When the hub is on a
384
+ (one gate, reused for all five reviews) takes `open → comment → approve → advance`. When the hub is on a
364
385
  platform, the **`yad gate`** CLI runs that gate over a real PR/MR — `open` raises the review PR, `sync`
365
386
  pulls approvals + comment threads into the ledger, and the step **auto-advances when the approved,
366
387
  fully-resolved PR is merged** (the merge is the human approval act).
@@ -417,7 +438,7 @@ accumulate, and the step moves forward only when the rule is met. **File-only**
417
438
  in any story's `repos`**.
418
439
 
419
440
  ### Check status anytime
420
- Invoke **`yad-status`** (read-only) to see the full 8-step chain, every step's dials/status, the
441
+ Invoke **`yad-status`** (read-only) to see the full 10-step chain, every step's dials/status, the
421
442
  contract lock, story repo tags, and which approvals the active gate still needs.
422
443
 
423
444
  ## Worked example (already in this repo)
package/cli/doctor.mjs CHANGED
@@ -5,7 +5,7 @@
5
5
  import path from 'node:path';
6
6
  import fs from 'node:fs';
7
7
  import { c, log, ok, info, warn, fail, hand, run, has, exists, readJSON, readJSONStrict } from './lib.mjs';
8
- import { VERSION, PROJECT_FILES, DESIGN_TOOLS } from './manifest.mjs';
8
+ import { VERSION, PROJECT_FILES, DESIGN_TOOLS, TESTING_TOOLS } from './manifest.mjs';
9
9
  import { loadLedger, epicRoot } from './epic-state.mjs';
10
10
  import { gitHead } from './setup.mjs';
11
11
  import { cliFor } from './platform.mjs';
@@ -88,13 +88,33 @@ export function projectChecks(checks, root) {
88
88
  }
89
89
  if (designBroken) { /* reported above */ }
90
90
  else if (typeof design !== 'object' || Array.isArray(design) || design === null) check(checks, 'design', 'project', 'fail', `${PROJECT_FILES.designConfig} has the wrong shape [YAD-STATE-002]`, 'expected a JSON object');
91
- else if (![...DESIGN_TOOLS, 'none', null, undefined].includes(design.tool)) check(checks, 'design', 'project', 'fail', `${PROJECT_FILES.designConfig}: unknown design tool '${design.tool}' [YAD-CFG-002]`, `expected one of ${DESIGN_TOOLS.join(', ')}, or none`);
92
- else if (!design.tool || design.tool === 'none') check(checks, 'design', 'project', 'ok', 'design: markdown-only');
91
+ else if (design.tool === 'none') check(checks, 'design', 'project', 'ok', 'design: markdown-only');
92
+ else if (!DESIGN_TOOLS.includes(design.tool)) check(checks, 'design', 'project', 'fail', `${PROJECT_FILES.designConfig}: unknown or missing design tool '${design.tool}' [YAD-CFG-002]`, `expected one of ${DESIGN_TOOLS.join(', ')}, or none`);
93
93
  else if (design.source && design.source !== 'unavailable') check(checks, 'design', 'project', 'ok', `design: ${design.tool} (${design.source})`);
94
94
  else if (design.source === 'unavailable') check(checks, 'design', 'project', 'warn', `design: ${design.tool} MCP unavailable — yad-ui runs markdown-only`, 'connect the MCP, then run `yad-connect-design` (action: refresh)');
95
95
  else check(checks, 'design', 'project', 'warn', `design: ${design.tool} recorded but the MCP is not confirmed`, 'run `yad-connect-design` in Claude Code to detect the MCP');
96
96
  }
97
97
 
98
+ // testing.json: parse + shape + tool + MCP confirmation (absent is the normal artifacts-only default —
99
+ // pre-feature projects have none, so silence rather than warn when the file does not exist).
100
+ const testingPath = path.join(root, PROJECT_FILES.testingConfig);
101
+ if (exists(testingPath)) {
102
+ let testing = null, testingBroken = false;
103
+ try {
104
+ testing = readJSONStrict(testingPath, null);
105
+ } catch (e) {
106
+ testingBroken = true;
107
+ check(checks, 'testing', 'project', 'fail', `${PROJECT_FILES.testingConfig} does not parse [${e.code || 'YAD-STATE-001'}]`, e.hint || 'fix the JSON or restore it from git');
108
+ }
109
+ if (testingBroken) { /* reported above */ }
110
+ else if (typeof testing !== 'object' || Array.isArray(testing) || testing === null) check(checks, 'testing', 'project', 'fail', `${PROJECT_FILES.testingConfig} has the wrong shape [YAD-STATE-002]`, 'expected a JSON object');
111
+ else if (testing.tool === 'none') check(checks, 'testing', 'project', 'ok', 'testing: artifacts-only');
112
+ else if (!TESTING_TOOLS.includes(testing.tool)) check(checks, 'testing', 'project', 'fail', `${PROJECT_FILES.testingConfig}: unknown or missing testing tool '${testing.tool}' [YAD-CFG-003]`, `expected one of ${TESTING_TOOLS.join(', ')}, or none`);
113
+ else if (testing.source && testing.source !== 'unavailable') check(checks, 'testing', 'project', 'ok', `testing: ${testing.tool} (${testing.source})`);
114
+ else if (testing.source === 'unavailable') check(checks, 'testing', 'project', 'warn', `testing: ${testing.tool} MCP unavailable — yad-test-cases runs artifacts-only`, 'connect the MCP, then run `yad-connect-testing` (action: refresh)');
115
+ else check(checks, 'testing', 'project', 'warn', `testing: ${testing.tool} recorded but the MCP is not confirmed`, 'run `yad-connect-testing` in Claude Code to detect the MCP');
116
+ }
117
+
98
118
  // repos.json: parse + every entry is a live git repo; staleness vs syncedHead
99
119
  let registry = { repos: [] };
100
120
  let regBroken = false;
@@ -197,9 +197,24 @@ export function gatePredicate({
197
197
 
198
198
  // Advance the step in state.json once the predicate passes. Mirrors yad-review-gate Step 3:
199
199
  // mark this review step done, unblock the next step, or set `ready-for-build` for the last one.
200
+ //
201
+ // `test-cases` is a PARALLEL, non-blocking track so the build half can start while the tester works:
202
+ // approving `stories-review` makes the epic `ready-for-build` (the build half keys off this) AND opens
203
+ // `test-cases` for the tester; completing `test-cases-review` never pulls `currentStep` back from
204
+ // `ready-for-build`. Both rules degrade safely for an old chain that has no test-cases steps.
200
205
  export function advanceState(state, step) {
201
206
  const i = state.steps.findIndex((s) => s.id === step.id);
202
207
  state.steps[i] = { ...state.steps[i], status: 'done' };
208
+ if (step.id === 'stories-review') {
209
+ const tc = state.steps.find((s) => s.id === 'test-cases');
210
+ if (tc && tc.status === 'blocked') tc.status = 'in_progress';
211
+ state.currentStep = 'ready-for-build';
212
+ return state;
213
+ }
214
+ if (step.id === 'test-cases-review') {
215
+ state.currentStep = 'ready-for-build';
216
+ return state;
217
+ }
203
218
  const next = state.steps[i + 1];
204
219
  if (next) {
205
220
  next.status = next.type === 'review+approve' ? 'in_review' : 'in_progress';
@@ -210,11 +225,13 @@ export function advanceState(state, step) {
210
225
  return state;
211
226
  }
212
227
 
213
- // Mark a step in-review (idempotent) and point currentStep at it.
228
+ // Mark a step in-review (idempotent) and point currentStep at it — EXCEPT once the epic is
229
+ // `ready-for-build`: the parallel `test-cases` track must not pull currentStep back (the build half
230
+ // runs alongside the tester, and only the test-cases review is in flight at that point).
214
231
  export function markInReview(state, step) {
215
232
  const i = state.steps.findIndex((s) => s.id === step.id);
216
233
  if (state.steps[i].status !== 'done') state.steps[i].status = 'in_review';
217
- state.currentStep = step.id;
234
+ if (state.currentStep !== 'ready-for-build') state.currentStep = step.id;
218
235
  return state;
219
236
  }
220
237
 
package/cli/errors.mjs CHANGED
@@ -21,6 +21,7 @@ export const CODES = {
21
21
  'YAD-STATE-003': 'a registered repo path is missing or not a git repository',
22
22
  'YAD-CFG-001': 'hub.json names an unknown platform (expected github, gitlab, or null)',
23
23
  'YAD-CFG-002': 'design.json names an unknown design tool (expected one of config.yaml design.tools, or none)',
24
+ 'YAD-CFG-003': 'testing.json names an unknown testing tool (expected one of config.yaml testing.tools, or none)',
24
25
  };
25
26
 
26
27
  export const err = (code, message, hint) => new YadError(code, message, hint);
package/cli/manifest.mjs CHANGED
@@ -10,15 +10,17 @@ import { readFileSync } from 'node:fs';
10
10
  const { version } = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'));
11
11
  export const VERSION = version;
12
12
 
13
- // The 18 hand-authored yad-* skills (mirrors skills/sdlc/install.sh).
13
+ // The 20 hand-authored yad-* skills (mirrors skills/sdlc/install.sh).
14
14
  export const SKILLS = [
15
15
  'yad-analysis',
16
16
  'yad-epic',
17
17
  'yad-architecture',
18
18
  'yad-ui',
19
19
  'yad-stories',
20
+ 'yad-test-cases',
20
21
  'yad-connect-repos',
21
22
  'yad-connect-design',
23
+ 'yad-connect-testing',
22
24
  'yad-spec',
23
25
  'yad-implement',
24
26
  'yad-checks',
@@ -87,11 +89,19 @@ export const MODULE_FILES = ['config.yaml', 'module-help.csv'];
87
89
  export const DESIGN_TOOLS = ['figma', 'pencil'];
88
90
  export const DESIGN_PRIMARY = 'figma';
89
91
 
92
+ // Supported testing-tool adapters (mirrors skills/sdlc/config.yaml `testing.tools`); `TESTING_PRIMARY`
93
+ // is the fallback `registerTesting`/setup use when an unknown tool is named, and `none` is the explicit
94
+ // artifacts-only choice. (doctor does NOT fall back — an unknown tool there is a hard YAD-CFG-003 fail,
95
+ // mirroring the design-tool YAD-CFG-002.)
96
+ export const TESTING_TOOLS = ['playwright', 'cypress', 'pytest'];
97
+ export const TESTING_PRIMARY = 'playwright';
98
+
90
99
  // Project-level files setup produces (used by `check` to spot missing setup).
91
100
  export const PROJECT_FILES = {
92
101
  reposRegistry: '.sdlc/repos.json',
93
102
  hubConfig: '.sdlc/hub.json',
94
103
  designConfig: '.sdlc/design.json',
104
+ testingConfig: '.sdlc/testing.json',
95
105
  version: '.sdlc/cli-version.json',
96
106
  };
97
107
 
package/cli/setup.mjs CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  c, log, step, ok, info, warn, hand, fail, ask, askYesNo, run, has,
6
6
  exists, readJSON, writeJSON,
7
7
  } from './lib.mjs';
8
- import { VERSION, IDE_FOLDER_TARGETS, PROJECT_FILES, DESIGN_TOOLS, DESIGN_PRIMARY } from './manifest.mjs';
8
+ import { VERSION, IDE_FOLDER_TARGETS, PROJECT_FILES, DESIGN_TOOLS, DESIGN_PRIMARY, TESTING_TOOLS, TESTING_PRIMARY } from './manifest.mjs';
9
9
  import { moduleActions, repoActions, hubActions, authorsActions } from './plan.mjs';
10
10
 
11
11
  const ALL_IDES = [...IDE_FOLDER_TARGETS, '.opencode'];
@@ -87,6 +87,34 @@ export function registerDesign(root, { tool, project_url = null, files = null, t
87
87
  return design;
88
88
  }
89
89
 
90
+ // Record the project's testing-tool connection into .sdlc/testing.json (the deterministic half of the
91
+ // connect loop; MCP detection itself is an AI step, handed off to `yad-connect-testing`). An unknown
92
+ // tool falls back to the primary adapter rather than being rejected — mirrors registerDesign. `none` is
93
+ // the explicit artifacts-only choice.
94
+ export function registerTesting(root, { tool, project_url = null, suites = null, today = null } = {}) {
95
+ // Idempotent re-connect: carry the original first-connect date forward; only lastSyncedAt moves.
96
+ const testingPath = path.join(root, PROJECT_FILES.testingConfig);
97
+ const prev = readJSON(testingPath, null);
98
+ const connectedAt = prev && prev.connectedAt ? prev.connectedAt : today;
99
+ let t = (tool || '').toLowerCase();
100
+ if (t === 'none' || t === '') {
101
+ const off = { tool: 'none', provider: null, project_url: null, auth: 'user',
102
+ suites: {}, connectedAt, lastSyncedAt: today, source: 'unavailable' };
103
+ writeJSON(testingPath, off);
104
+ return off;
105
+ }
106
+ if (!TESTING_TOOLS.includes(t)) { warn(`unknown testing tool '${tool}' — using ${TESTING_PRIMARY}`); t = TESTING_PRIMARY; }
107
+ // source stays null until `yad-connect-testing` detects the MCP in the harness (AI step). doctor
108
+ // reports a recorded-but-unconfirmed connection as a warning pointing at that skill.
109
+ const testing = {
110
+ tool: t, provider: null, project_url: project_url || null, auth: 'user',
111
+ suites: suites || {},
112
+ connectedAt, lastSyncedAt: today, source: null,
113
+ };
114
+ writeJSON(testingPath, testing);
115
+ return testing;
116
+ }
117
+
90
118
  function applyActions(actions, { force = false } = {}) {
91
119
  let changed = 0;
92
120
  for (const a of actions) {
@@ -100,7 +128,7 @@ function applyActions(actions, { force = false } = {}) {
100
128
  }
101
129
 
102
130
  export async function runSetup(root, opts = {}) {
103
- const total = 8;
131
+ const total = 9;
104
132
  log(c.bold(`\nSDLC Workflow setup ${c.dim('v' + VERSION)}`));
105
133
  log(c.dim(`target: ${root}`));
106
134
 
@@ -178,8 +206,26 @@ export async function runSetup(root, opts = {}) {
178
206
  : `wrote ${PROJECT_FILES.designConfig} (${tool})`);
179
207
  }
180
208
 
181
- // 5. Connect code repos
182
- step(5, total, 'Connect code repos');
209
+ // 5. Connect a testing tool (Playwright-first, pluggable; the test-cases step implements automation here)
210
+ step(5, total, 'Connect a testing tool (playwright / cypress / pytest / none)');
211
+ const testingPath = path.join(root, PROJECT_FILES.testingConfig);
212
+ if (exists(testingPath) && !(await askYesNo('testing.json exists — reconfigure?', false))) {
213
+ info('keeping existing .sdlc/testing.json');
214
+ } else {
215
+ let tool = (await ask(`Testing tool (${TESTING_TOOLS.join('/')}/none)`, TESTING_PRIMARY)).toLowerCase();
216
+ if (![...TESTING_TOOLS, 'none'].includes(tool)) {
217
+ warn(`unknown testing tool '${tool}' — using ${TESTING_PRIMARY}`);
218
+ tool = TESTING_PRIMARY;
219
+ }
220
+ const project_url = tool === 'none' ? null : (await ask(' project/config reference (blank to set later)', '')) || null;
221
+ registerTesting(root, { tool, project_url, today: opts.today ?? null });
222
+ ok(tool === 'none'
223
+ ? `wrote ${PROJECT_FILES.testingConfig} (artifacts-only)`
224
+ : `wrote ${PROJECT_FILES.testingConfig} (${tool})`);
225
+ }
226
+
227
+ // 6. Connect code repos
228
+ step(6, total, 'Connect code repos');
183
229
  const regPath = path.join(root, PROJECT_FILES.reposRegistry);
184
230
  const registry = readJSON(regPath, { repos: [] });
185
231
  const known = new Set(registry.repos.map((r) => r.name));
@@ -202,8 +248,8 @@ export async function runSetup(root, opts = {}) {
202
248
  }
203
249
  }
204
250
 
205
- // 6. Wire each connected repo + the hub itself
206
- step(6, total, 'Wire connected repos + the hub (CI gates, PR template, comment scaffold, gate-sync)');
251
+ // 7. Wire each connected repo + the hub itself
252
+ step(7, total, 'Wire connected repos + the hub (CI gates, PR template, comment scaffold, gate-sync)');
207
253
  if (registry.repos.length === 0) info('no repos to wire');
208
254
  for (const repo of registry.repos) {
209
255
  log(` ${c.bold(repo.name)} ${c.dim(`(${repo.platform})`)}`);
@@ -218,8 +264,8 @@ export async function runSetup(root, opts = {}) {
218
264
  // author allowlists for the verified-commits gate (hub + every repo), from the roster emails
219
265
  applyActions(authorsActions(root, registry.repos), { force: true });
220
266
 
221
- // 7. Optional CodeRabbit
222
- step(7, total, 'AI review (CodeRabbit)');
267
+ // 8. Optional CodeRabbit
268
+ step(8, total, 'AI review (CodeRabbit)');
223
269
  for (const repo of registry.repos) {
224
270
  const cr = path.join(path.resolve(root, repo.path), '.coderabbit.yaml');
225
271
  if (exists(cr)) { info(`${repo.name}: .coderabbit.yaml present`); continue; }
@@ -229,8 +275,8 @@ export async function runSetup(root, opts = {}) {
229
275
  }
230
276
  }
231
277
 
232
- // 8. Summary + version stamp
233
- step(8, total, 'Done');
278
+ // 9. Summary + version stamp
279
+ step(9, total, 'Done');
234
280
  writeJSON(path.join(root, PROJECT_FILES.version), { version: VERSION, ideTargets, updatedAt: opts.today ?? null });
235
281
  ok(`stamped ${PROJECT_FILES.version} (v${VERSION})`);
236
282
  log('');
@@ -240,6 +286,10 @@ export async function runSetup(root, opts = {}) {
240
286
  if (design && design.tool && design.tool !== 'none') {
241
287
  hand(`confirm the design tool: run \`yad-connect-design\` to detect the ${design.tool} MCP (or it degrades to markdown-only)`);
242
288
  }
289
+ const testing = readJSON(testingPath, null);
290
+ if (testing && testing.tool && testing.tool !== 'none') {
291
+ hand(`confirm the testing tool: run \`yad-connect-testing\` to detect the ${testing.tool} MCP (or it degrades to artifacts-only)`);
292
+ }
243
293
  hand('author your first epic: run `yad-epic`');
244
294
  log('');
245
295
  log(c.dim('Re-run anytime: `yad check` (report) / `yad check --fix` (reconcile).'));
package/docs/index.html CHANGED
@@ -284,7 +284,7 @@
284
284
  <div class="lane">
285
285
  <div class="lane-title">0 · One-time setup (team lead, per project)</div>
286
286
  <div class="flow-v">
287
- <div class="node plain"><strong>Install the module</strong><small><code>npx yadflow setup</code> — copies the 18 skills into your IDE dirs</small></div>
287
+ <div class="node plain"><strong>Install the module</strong><small><code>npx yadflow setup</code> — copies the 20 skills into your IDE dirs</small></div>
288
288
  <div class="arrow-v">▼</div>
289
289
  <div class="node plain"><strong>Wire each repo</strong><small><code>yad-checks</code> · <code>yad-pr-template</code> · <code>yad-review-comments</code> (CI gates, PR template, comment scaffold)</small></div>
290
290
  <div class="arrow-v">▼</div>
@@ -292,6 +292,8 @@
292
292
  <div class="arrow-v">▼</div>
293
293
  <div class="node plain"><strong>Connect a design tool</strong><small><code>yad-connect-design</code> → <code>design.json</code> (Figma-first, pluggable) so <code>yad-ui</code> can materialize the screens <em>(optional — degrades to markdown-only)</em></small></div>
294
294
  <div class="arrow-v">▼</div>
295
+ <div class="node plain"><strong>Connect a testing tool</strong><small><code>yad-connect-testing</code> → <code>testing.json</code> (Playwright-first, pluggable) so <code>yad-test-cases</code> can implement the automation <em>(optional — degrades to artifacts-only)</em></small></div>
296
+ <div class="arrow-v">▼</div>
295
297
  <div class="node plain"><strong>Optional: hub on a platform</strong><small>detect GitHub/GitLab + reviewer roster, so reviews run on real PRs/MRs</small></div>
296
298
  </div>
297
299
  </div>
@@ -321,7 +323,9 @@
321
323
  <div class="arrow-v">▼</div>
322
324
  <div class="node gate">gate · stories review<small><strong>per-repo:</strong> base rule + the engineer of each touched repo</small></div>
323
325
  <div class="arrow-v">▼</div>
324
- <div class="node sentinel"><strong>ready-for-build</strong><small>the front half is done — building can start</small></div>
326
+ <div class="node sentinel"><strong>ready-for-build</strong><small>the gating front half is done — building can start (and <code>yad-test-cases</code> opens in parallel)</small></div>
327
+ <div class="arrow-v">▼</div>
328
+ <div class="node artifact"><strong>yad-test-cases</strong> <em>(parallel · non-blocking)</em><small>opens when the stories gate passes and runs <strong>alongside the build half</strong> — with the test architect → <code>test-cases.md</code> + the automation tests in the connected testing tool (<code>test-links.json</code>) → its own base-rule review. Never blocks <code>ready-for-build</code>.</small></div>
325
329
  </div>
326
330
  </div>
327
331
 
@@ -371,9 +375,10 @@
371
375
  contract.md the LOCKED shared surface (APIs, events, data model)
372
376
  ui-design.md + DESIGN.md (+ the screens in the connected design tool)
373
377
  stories/ one file per story (EP-&lt;slug&gt;-S01.md …)
378
+ test-cases.md the test cases (+ the automation tests when a testing tool is connected)
374
379
  reviews/ reviewer comments, per artifact
375
380
  .sdlc/ state.json · approvals.json · contract-lock.json
376
- design-links.json · build-state/ · trust-log.json · build-log.json
381
+ design-links.json · test-links.json · build-state/ · trust-log.json · build-log.json
377
382
 
378
383
  each code repo/
379
384
  specs/&lt;story-id&gt;/ spec, plan, tasks (+ link.md back to the story)
@@ -440,8 +445,8 @@ each code repo/
440
445
  <div class="term"><strong>State machine</strong><span class="badge b-core">core</span><br>The core design: the workflow is a fixed set of <em>states</em> (steps). Each state does its work and waits at a gate. The states never change — only the <em>trigger</em> (who moves work forward) changes. That is what lets the team start fully manual and automate gradually without rebuilding anything.</div>
441
446
  <div class="term"><strong>Product hub (product repo)</strong><span class="badge b-core">core</span><br>The git repo that holds the shared “thinking”: epics, architecture, the contract, UI design, stories, reviews and all state. The brain of the project. Code never lives here.</div>
442
447
  <div class="term"><strong>Code repo</strong><span class="badge b-core">core</span><br>A separate git repo holding real application code (e.g. backend, mobile, dashboard). Each story’s spec lives inside the code repo it belongs to, with a link back to the story in the hub.</div>
443
- <div class="term"><strong>Skills source (this repo)</strong><span class="badge b-core">core</span><br>The <code>yadflow</code> repo itself — where the 18 skills live and where you pull updates from. No real product work happens inside it.</div>
444
- <div class="term"><strong>Front half (“decide” / the brain)</strong><span class="badge b-core">core</span><br>The first half of the workflow, run once per epic in the product hub: analysis (optional) → epic → architecture + contract → UI design → stories — each followed by a human review gate. Permanently human-approved.</div>
448
+ <div class="term"><strong>Skills source (this repo)</strong><span class="badge b-core">core</span><br>The <code>yadflow</code> repo itself — where the 20 skills live and where you pull updates from. No real product work happens inside it.</div>
449
+ <div class="term"><strong>Front half (“decide” / the brain)</strong><span class="badge b-core">core</span><br>The first half of the workflow, run once per epic in the product hub: analysis (optional) → epic → architecture + contract → UI design → stories — each followed by a human review gate, ending at <code>ready-for-build</code>. <strong>Test cases</strong> is a parallel, non-blocking track that opens at the stories gate and runs alongside the build half. Permanently human-approved.</div>
445
450
  <div class="term"><strong>Back half / build half (“build”)</strong><span class="badge b-core">core</span><br>The second half, run once per story per code repo, inside that repo: spec → implement → checks → PR → ship. These mechanical steps may earn automation over time.</div>
446
451
  <div class="term"><strong>Gate</strong><span class="badge b-core">core</span><br>A stopping point after a step. The step writes its output and <em>waits</em>; a human must approve before the workflow moves on. “Every step stops at a gate” is the heart of the whole system.</div>
447
452
  <div class="term"><strong>Assistance dial</strong><span class="badge b-core">core</span><br>A per-step setting for how much AI helps with the work: <code>none</code>, <code>review</code> (AI checks human work), or <code>heavy</code> (AI does the drafting, human directs).</div>
@@ -520,7 +525,7 @@ each code repo/
520
525
  <div class="term"><strong>Trust threshold</strong><span class="badge b-auto">automation</span><br>The bar a step must clear to earn automation — by default at least 5 runs with ≥80% “approved-unchanged” (configurable in <code>config.yaml</code>).</div>
521
526
  <div class="term"><strong>Run verdicts</strong><span class="badge b-auto">automation</span><br>How each engineer review scores an automated run: <code>approved-unchanged</code> (merged exactly as authored — the strongest signal), <code>approved-with-edits</code> (a human fixed it first), <code>rejected</code> (failed).</div>
522
527
  <div class="term"><strong>Kill switch</strong><span class="badge b-auto">automation</span><br><code>yad-run action: kill</code> — one move that forces <em>every</em> step system-wide back to <code>human_approve</code> instantly, no code change needed. <code>unkill</code> restores earned automation. Automation is always reversible in one move.</div>
523
- <div class="term"><strong>Locked steps</strong><span class="badge b-auto">automation</span><br>The steps that may <em>never</em> be set to <code>machine_advance</code>: all four front authoring steps, their reviews, and the engineer review. A front state advances only on a human act.</div>
528
+ <div class="term"><strong>Locked steps</strong><span class="badge b-auto">automation</span><br>The steps that may <em>never</em> be set to <code>machine_advance</code>: all five front authoring steps, their reviews, and the engineer review. A front state advances only on a human act.</div>
524
529
  <div class="term"><strong>Fleet roll-up</strong><span class="badge b-auto">automation</span><br>The cross-epic summary <code>yad-status</code> prints: where every epic stands, which steps are “earned but still manual” (nudge cost), at a glance.</div>
525
530
 
526
531
  <h3>Tools the workflow builds on</h3>
@@ -531,7 +536,7 @@ each code repo/
531
536
  <div class="term"><strong>CodeRabbit</strong><span class="badge b-tool">tools</span><br>An AI code-review service used as the advisory first pass on PRs. Optional; never the merge authority.</div>
532
537
  <div class="term"><strong>Graceful degradation</strong><span class="badge b-tool">tools</span><br>The rule that every optional tool (Spec Kit, Impeccable, Repomix, CodeRabbit) can be absent: the workflow falls back to doing the work directly and <em>records</em> that the tool was missing. You can start with none of them.</div>
533
538
  <div class="term"><strong>Tool-agnostic rule</strong><span class="badge b-tool">tools</span><br>Talk to every tool through its commands and files, never through its internal code — so each tool stays swappable.</div>
534
- <div class="term"><strong>Skill</strong><span class="badge b-tool">tools</span><br>One of the 18 named agents (e.g. <code>yad-epic</code>) you invoke by name in your AI IDE. Each skill does one step’s work, writes files, and stops at its gate.</div>
539
+ <div class="term"><strong>Skill</strong><span class="badge b-tool">tools</span><br>One of the 20 named agents (e.g. <code>yad-epic</code>) you invoke by name in your AI IDE. Each skill does one step’s work, writes files, and stops at its gate.</div>
535
540
  <div class="term"><strong>The <code>yad</code> CLI</strong><span class="badge b-tool">tools</span><br>A zero-dependency command-line tool (npm: <code>yadflow</code>) that installs, wires and reconciles the workflow (<code>setup</code>, <code>check --fix</code>, <code>update</code>) and runs the deterministic pieces (<code>gate</code>, <code>commit</code>, <code>open-pr</code>, <code>repo</code>).</div>
536
541
 
537
542
  <h3>People &amp; AI roles</h3>
@@ -574,7 +579,7 @@ each code repo/
574
579
  <div class="flow-v">
575
580
  <div class="node plain"><strong>Setup</strong><small>install skills · create the hub repo · create empty code repos · wire each repo (CI gates + PR template) · <s>connect-repos</s> <em>skipped — nothing to map yet</em></small></div>
576
581
  <div class="arrow-v">▼</div>
577
- <div class="node artifact"><strong>Front half</strong><small>(analysis) → epic → architecture + contract → UI (<code>craft</code> → <code>extract</code>) → stories — all human-gated</small></div>
582
+ <div class="node artifact"><strong>Front half</strong><small>(analysis) → epic → architecture + contract → UI (<code>craft</code> → <code>extract</code>) → stories — all human-gated; then test cases runs in parallel with the build half</small></div>
578
583
  <div class="arrow-v">▼</div>
579
584
  <div class="node"><strong>Build half</strong><small>spec → implement → checks → PR → ship, per story per repo — specs exist for 100% of the code from day one</small></div>
580
585
  <div class="arrow-v">▼</div>
@@ -674,7 +679,7 @@ each code repo/
674
679
 
675
680
  <!-- ======================================================= -->
676
681
  <section id="skills">
677
- <h2>6. The 18 skills — what each does, how to use it, and when</h2>
682
+ <h2>6. The 20 skills — what each does, how to use it, and when</h2>
678
683
  <p>A <strong>skill</strong> is an agent you invoke <em>by name</em> in your AI IDE — you just ask in
679
684
  plain words, e.g. <em>“run <code>yad-epic</code>”</em>, adding any inputs the skill needs
680
685
  (<code>repo: backend</code>, <code>story: EP-…-S01</code>, <code>action: wire</code>, …).
@@ -685,11 +690,13 @@ each code repo/
685
690
  <tr><th>Skill</th><th>Half</th><th>One line</th></tr>
686
691
  <tr><td><a href="#sk-connect"><code>yad-connect-repos</code></a></td><td>Setup</td><td>Register a code repo with the hub + cache its code-map.</td></tr>
687
692
  <tr><td><a href="#sk-connect-design"><code>yad-connect-design</code></a></td><td>Setup</td><td>Connect a design tool (Figma-first) so <code>yad-ui</code> can materialize the screens.</td></tr>
693
+ <tr><td><a href="#sk-connect-testing"><code>yad-connect-testing</code></a></td><td>Setup</td><td>Connect a testing tool (Playwright-first) so <code>yad-test-cases</code> can implement the automation.</td></tr>
688
694
  <tr><td><a href="#sk-analysis"><code>yad-analysis</code></a></td><td>Front</td><td><em>(Optional)</em> pressure-test an idea into <code>analysis.md</code>.</td></tr>
689
695
  <tr><td><a href="#sk-epic"><code>yad-epic</code></a></td><td>Front</td><td>Start a feature: write <code>epic.md</code>, assign the ID.</td></tr>
690
696
  <tr><td><a href="#sk-arch"><code>yad-architecture</code></a></td><td>Front</td><td>Author <code>architecture.md</code> + the locked <code>contract.md</code>.</td></tr>
691
697
  <tr><td><a href="#sk-ui"><code>yad-ui</code></a></td><td>Front</td><td>Author <code>ui-design.md</code> + <code>DESIGN.md</code>; materialize the screens in the connected design tool.</td></tr>
692
698
  <tr><td><a href="#sk-stories"><code>yad-stories</code></a></td><td>Front</td><td>Break the epic into repo-tagged stories.</td></tr>
699
+ <tr><td><a href="#sk-test-cases"><code>yad-test-cases</code></a></td><td>Front</td><td>With the test architect, author the test cases; implement the automation when a testing tool is connected.</td></tr>
693
700
  <tr><td><a href="#sk-gate"><code>yad-review-gate</code></a></td><td>Cross-cutting</td><td>Review / comment / approve / advance <strong>any</strong> gate.</td></tr>
694
701
  <tr><td><a href="#sk-bridge"><code>yad-hub-bridge</code></a></td><td>Cross-cutting</td><td>Run front-half reviews over real PRs/MRs and sync them back.</td></tr>
695
702
  <tr><td><a href="#sk-comments"><code>yad-review-comments</code></a></td><td>Setup</td><td>Install the structured review-comment scaffolds.</td></tr>
@@ -729,6 +736,17 @@ yad-connect-design action: disconnect</code></pre>
729
736
  <div class="row"><span class="lbl lbl-when">When</span> During one-time setup (the wizard records it), and again any time you switch design tools. <strong>No design tool? Skip it</strong> — <code>yad-ui</code> simply authors the Markdown, exactly as before.</div>
730
737
  </div>
731
738
 
739
+ <div class="skillcard" id="sk-connect-testing">
740
+ <h4><code>yad-connect-testing</code></h4>
741
+ <div class="row"><span class="lbl lbl-what">What</span> Connects a <strong>testing tool</strong> to the product hub so the test-cases step can implement the <em>actual</em> automation tests inside it, alongside the Markdown. <strong>Playwright-first but pluggable</strong> (a testing-tool adapter; <code>cypress</code> and <code>pytest</code> are second providers). It records the tool + project/suite references in <code>testing.json</code> through your own authenticated MCP session — <strong>no tokens are ever stored</strong> — and degrades to artifacts-only when no testing-tool MCP is available.</div>
742
+ <div class="row"><span class="lbl lbl-how">How</span> Run it in the product hub, once per project:</div>
743
+ <pre><code>yad-connect-testing action: connect tool: playwright project_url: tests/playwright.config.ts
744
+ yad-connect-testing action: list # show the connection + whether the MCP is available
745
+ yad-connect-testing action: refresh # re-detect the MCP (after authenticating a session)
746
+ yad-connect-testing action: disconnect</code></pre>
747
+ <div class="row"><span class="lbl lbl-when">When</span> During one-time setup (the wizard records it), and again any time you switch testing tools. <strong>No testing tool? Skip it</strong> — <code>yad-test-cases</code> simply authors the test cases, exactly as before.</div>
748
+ </div>
749
+
732
750
  <h3>Front half — author the thinking (in the product hub)</h3>
733
751
 
734
752
  <div class="skillcard" id="sk-analysis">
@@ -768,7 +786,15 @@ yad-connect-design action: disconnect</code></pre>
768
786
  <div class="row"><span class="lbl lbl-what">What</span> With the pm AI, slices the approved epic into user stories small enough to build — one file each under <code>stories/</code>, with stable IDs (<code>EP-&lt;slug&gt;-S01</code>…) and a <code>repos:</code> tag naming which code repos must implement each one. Reads epic + architecture + contract + UI as input.</div>
769
787
  <div class="row"><span class="lbl lbl-how">How</span> In the product hub, after the UI gate passes:</div>
770
788
  <pre><code>run yad-stories — epic: EP-istifta-inquiries</code></pre>
771
- <div class="row"><span class="lbl lbl-when">When</span> The last front-half author step. Stops at the <strong>stories review gate</strong> (per-repo rule). When that gate passes, the epic is <code>ready-for-build</code>.</div>
789
+ <div class="row"><span class="lbl lbl-when">When</span> After the UI gate passes. Stops at the <strong>stories review gate</strong> (per-repo rule). When it passes the epic is <code>ready-for-build</code> — the build half can start, and <code>yad-test-cases</code> opens in parallel.</div>
790
+ </div>
791
+
792
+ <div class="skillcard" id="sk-test-cases">
793
+ <h4><code>yad-test-cases</code></h4>
794
+ <div class="row"><span class="lbl lbl-what">What</span> With the <strong>test architect</strong> AI (Murat), authors the risk-based test cases that cover the approved stories → <code>test-cases.md</code> (cases prioritised P0–P3, with story→case traceability). When a testing tool is connected (<code>yad-connect-testing</code>), it also <strong>implements the automation tests in that tool</strong> — generating them into the connected code repo(s) or linking an existing suite — and records the case→test map in <code>test-links.json</code>; otherwise it stays artifacts-only. Reads epic + architecture + contract + UI + stories as input.</div>
795
+ <div class="row"><span class="lbl lbl-how">How</span> In the product hub, after the stories gate passes:</div>
796
+ <pre><code>run yad-test-cases — epic: EP-istifta-inquiries</code></pre>
797
+ <div class="row"><span class="lbl lbl-when">When</span> A <strong>parallel, non-blocking</strong> track: it opens the moment the stories gate passes — the epic is already <code>ready-for-build</code>, so this runs <strong>alongside the build half</strong>, not before it. Stops at its own <strong>test-cases review gate</strong> (base rule); that review never blocks building.</div>
772
798
  </div>
773
799
 
774
800
  <h3>The review gate &amp; its plumbing (cross-cutting)</h3>
@@ -897,7 +923,7 @@ yad-status EP-istifta-inquiries # one epic in detail</code></pre>
897
923
 
898
924
  <div class="skillcard">
899
925
  <h4><code>npx yadflow setup</code></h4>
900
- <div class="row"><span class="lbl lbl-what">What</span> The guided first-run wizard. Eight steps: preflight (git/node check), install all 18 skills into the IDE dirs you pick, detect the hub’s platform + record the reviewer roster, connect a design tool (Figma-first; optional), connect your code repos, wire each one (CI gates, PR template, comment scaffold), optionally write the AI-review config, and stamp the installed version.</div>
926
+ <div class="row"><span class="lbl lbl-what">What</span> The guided first-run wizard. Nine steps: preflight (git/node check), install all 20 skills into the IDE dirs you pick, detect the hub’s platform + record the reviewer roster, connect a design tool (Figma-first; optional), connect a testing tool (Playwright-first; optional), connect your code repos, wire each one (CI gates, PR template, comment scaffold), optionally write the AI-review config, and stamp the installed version.</div>
901
927
  <div class="row"><span class="lbl lbl-how">How</span></div>
902
928
  <pre><code>cd &lt;product-hub-repo&gt;
903
929
  npx yadflow setup</code></pre>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yadflow",
3
- "version": "2.2.0",
3
+ "version": "2.3.0",
4
4
  "description": "Yadflow — the gated, team, multi-repo SDLC: author → review → build with a PR-driven review gate and a zero-dependency `yad` CLI (setup, gate, commit, open-pr, repo). A BMAD module + 17 yad-* skills.",
5
5
  "type": "module",
6
6
  "author": "AbdelRahman Nasr",