yadflow 2.2.0 → 2.4.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.
Files changed (34) hide show
  1. package/CHANGELOG.md +16 -5
  2. package/README.md +75 -22
  3. package/cli/doctor.mjs +45 -3
  4. package/cli/epic-state.mjs +19 -2
  5. package/cli/errors.mjs +2 -0
  6. package/cli/manifest.mjs +23 -1
  7. package/cli/setup.mjs +109 -10
  8. package/docs/index.html +62 -11
  9. package/package.json +5 -3
  10. package/skills/sdlc/config.yaml +41 -4
  11. package/skills/sdlc/install.sh +1 -1
  12. package/skills/sdlc/module-help.csv +6 -1
  13. package/skills/yad-analysis/SKILL.md +10 -5
  14. package/skills/yad-connect-design/SKILL.md +1 -1
  15. package/skills/yad-connect-design/references/design-registry.md +4 -2
  16. package/skills/yad-connect-learning/SKILL.md +140 -0
  17. package/skills/yad-connect-learning/references/learning-context.md +79 -0
  18. package/skills/yad-connect-learning/references/learning-registry.md +60 -0
  19. package/skills/yad-connect-testing/SKILL.md +121 -0
  20. package/skills/yad-connect-testing/references/testing-context.md +67 -0
  21. package/skills/yad-connect-testing/references/testing-registry.md +55 -0
  22. package/skills/yad-epic/SKILL.md +10 -5
  23. package/skills/yad-epic/references/state-schema.md +42 -11
  24. package/skills/yad-hub-bridge/SKILL.md +2 -2
  25. package/skills/yad-learn/SKILL.md +146 -0
  26. package/skills/yad-learn/references/learning-state.md +75 -0
  27. package/skills/yad-review-gate/SKILL.md +14 -11
  28. package/skills/yad-review-gate/references/gating.md +1 -1
  29. package/skills/yad-run/references/run-loop.md +3 -3
  30. package/skills/yad-spec/SKILL.md +3 -1
  31. package/skills/yad-status/SKILL.md +35 -15
  32. package/skills/yad-stories/SKILL.md +3 -1
  33. package/skills/yad-test-cases/SKILL.md +173 -0
  34. package/skills/yad-test-cases/references/test-cases-schema.md +70 -0
@@ -0,0 +1,79 @@
1
+ # Learning context — CLI detection, knowledge-base build, capability map, degrade path
2
+
3
+ How `yad-connect-learning` reaches DeepTutor, grounds it in the project, and how `yad-learn` consumes the
4
+ connection. DeepTutor is a **CLI subprocess** (Apache-2.0, `pip install -U deeptutor`) — it ships **no
5
+ MCP server**, so this adapter detects a **binary on PATH**, the same shape as Repomix's `npx` in
6
+ `yad-connect-repos`/`yad-backfill`, not the MCP shape of the design/testing tools.
7
+
8
+ ## CLI detection
9
+
10
+ Best-effort, never fatal:
11
+
12
+ ```
13
+ deeptutor --version # success => provider: "deeptutor-cli", capture the version
14
+ deeptutor config show # fallback probe if --version is unavailable
15
+ ```
16
+
17
+ - Binary found → `source: "deeptutor-cli"`.
18
+ - Binary absent → `source: "harness-native"` (no error). Report that `yad-learn` will tutor via the
19
+ harness model reading the project artifacts.
20
+
21
+ DeepTutor's own setup (`deeptutor init`, LLM provider keys under `data/user/settings/`) is the **user's**
22
+ responsibility and lives outside this repo. The skill never runs `deeptutor init` and never writes keys.
23
+
24
+ ## Knowledge-base build (grounding)
25
+
26
+ When `ground: true` and the CLI is present, build/refresh a project knowledge base so tutoring quotes
27
+ what is actually being built:
28
+
29
+ ```
30
+ deeptutor kb create <kb> # idempotent — reuse if it already exists
31
+ deeptutor kb add <kb> --doc <path> # once per source path below
32
+ deeptutor kb list # verify
33
+ ```
34
+
35
+ Ingest only **committed, secret-scanned** sources (never raw repos):
36
+
37
+ - Per epic under `epics/EP-<slug>/`: `epic.md`, `architecture.md`, `contract.md`, `ui-design.md`, and the
38
+ `stories/*.md` files.
39
+ - Per connected repo: `.sdlc/code-context/<repo>/code-map.md` (already secret-scanned by
40
+ `yad-connect-repos`). Do **not** add `pack.md` or the raw repo.
41
+
42
+ Record `kb` and `kb_sources` in the registry. If there are no artifacts yet (greenfield), skip and record
43
+ `kb: null` — `yad-learn` falls back to passing context inline.
44
+
45
+ ## Mode → capability map (consumed by `yad-learn`)
46
+
47
+ `config.yaml` `learning.capabilities` maps a `yad-learn` mode to a DeepTutor capability:
48
+
49
+ | `yad-learn` mode | DeepTutor capability | use |
50
+ |------------------|----------------------|-----|
51
+ | `explain` (default) | `chat` | a focused, grounded explanation of the concept |
52
+ | `deep` | `deep_research` | a deeper, multi-source dive |
53
+ | `quiz` | `deep_question` | generate questions to confirm comprehension (records a signal) |
54
+
55
+ `yad-learn` invokes:
56
+
57
+ ```
58
+ deeptutor run <capability> "<concept> — in the context of <scoped artifact/stage>" \
59
+ --kb <kb> --format json
60
+ ```
61
+
62
+ `--format json` streams **NDJSON** — one event per line with a `type` (`content` | `tool_call` |
63
+ `tool_result` | `done`) and a `session_id`. `yad-learn` concatenates `content` events into the tutorial
64
+ and captures `session_id` from the `done` event for the learning record.
65
+
66
+ ## Degrade path (harness-native)
67
+
68
+ When `source: "harness-native"` (no CLI, or `tool: "none"`), `yad-learn` does **not** fail. It tutors
69
+ using the **harness model itself**: it reads the scoped epic's `epic.md` / `architecture.md` /
70
+ `contract.md` / code-maps and explains the concept grounded in them. The local-only learning record is
71
+ written identically (with `"tool": "harness-native"`), so the learning layer always works and always
72
+ records — DeepTutor only adds knowledge-base grounding, deep research, and quizzes.
73
+
74
+ ## Freshness
75
+
76
+ Like the code-context cache, the knowledge base can drift from the artifacts. `refresh` rebuilds it from
77
+ the current committed artifacts and moves `lastSyncedAt`. Rebuilding is a human decision (run `connect`/
78
+ `refresh`); `yad-learn` never silently rebuilds the kb mid-tutorial — at most it notes the kb may be
79
+ stale and proceeds.
@@ -0,0 +1,60 @@
1
+ # Learning registry — schema + freshness rule
2
+
3
+ The registry is the product hub's record of which learning tool is connected and how to reach it. It is
4
+ **project-wide** (one learning 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/learning.json`
10
+
11
+ (`config.yaml` `learning.registry`.) Create the file and its parent `.sdlc/` on the first `connect`.
12
+
13
+ ## Schema
14
+
15
+ ```json
16
+ {
17
+ "tool": "deeptutor", // deeptutor | <adapter id> | none (harness-native)
18
+ "provider": "deeptutor-cli", // the concrete CLI: deeptutor-cli | null
19
+ "version": "1.4.5", // CLI version reported at detect time; null if absent
20
+ "kb": "yadflow-istifta", // grounded knowledge-base name; null if not built
21
+ "kb_sources": ["epic.md", "architecture.md", "contract.md", "ui-design.md", "stories/", "code-context/*/code-map.md"],
22
+ "auth": "user", // ALWAYS the user's own DeepTutor config / LLM keys — never a token
23
+ "connectedAt": "2026-06-14", // first connect (YYYY-MM-DD)
24
+ "lastSyncedAt": "2026-06-14", // last connect/refresh
25
+ "source": "deeptutor-cli" // deeptutor-cli (CLI on PATH) | harness-native (degraded)
26
+ }
27
+ ```
28
+
29
+ ## Rules
30
+
31
+ - **`tool`** selects the adapter; it MUST be one of `config.yaml` `learning.tools` (or `none`). At
32
+ **connect** time an unknown tool is normalized to `learning.primary` with a warning (so the registry
33
+ never persists an unknown value); a registry hand-edited to an unknown or missing tool **fails
34
+ `doctor`** with `YAD-CFG-004` and must be fixed.
35
+ - **Auth is never stored.** No LLM key, token, or any credential in the registry. `kb`/`kb_sources` are
36
+ plain references; DeepTutor is reached through the user's own `deeptutor` config.
37
+ - **`connect` overwrites in place** — a project carries exactly one learning connection at a time;
38
+ switching tools is just another `connect`. There is no array (unlike `repos.json`). The original
39
+ `connectedAt` is preserved across re-connects; only `lastSyncedAt` moves.
40
+ - **`source`** is the authority for availability: `deeptutor-cli` means `yad-learn` can drive the CLI
41
+ (grounded in `kb`); `harness-native` means `yad-learn` tutors via the harness model reading the
42
+ artifacts directly. `refresh` re-detects and may flip it.
43
+ - **`ground` is not persisted.** The `ground: true|false` input only governs whether the AI connect
44
+ step builds/refreshes the knowledge base at connect time; the registry records the *result* (`kb` +
45
+ `kb_sources`), never the flag itself.
46
+ - **`tool: "none"`** is a valid, deliberate state: a project that has chosen harness-native tutoring.
47
+ `yad-learn` treats it exactly like an absent registry — it still tutors, just without DeepTutor.
48
+ - **`disconnect`** removes the file (or sets `tool: "none"`). DeepTutor's own config and knowledge bases
49
+ are never touched.
50
+
51
+ ## Git tracking
52
+
53
+ Commit the **registry** (`learning.json`) — it is small, reviewable, and holds no secrets (references
54
+ only). This mirrors how `repos.json`, `hub.json`, `design.json`, and `testing.json` are committed.
55
+
56
+ ## Greenfield
57
+
58
+ A brand-new product hub has no `learning.json`. That is valid — `yad-learn` treats "no learning tool
59
+ connected" the same as `tool: "none"` and tutors harness-native. The registry appears the first time
60
+ `connect` runs.
@@ -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