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.
- package/CHANGELOG.md +16 -5
- package/README.md +75 -22
- package/cli/doctor.mjs +45 -3
- package/cli/epic-state.mjs +19 -2
- package/cli/errors.mjs +2 -0
- package/cli/manifest.mjs +23 -1
- package/cli/setup.mjs +109 -10
- package/docs/index.html +62 -11
- package/package.json +5 -3
- package/skills/sdlc/config.yaml +41 -4
- package/skills/sdlc/install.sh +1 -1
- package/skills/sdlc/module-help.csv +6 -1
- package/skills/yad-analysis/SKILL.md +10 -5
- package/skills/yad-connect-design/SKILL.md +1 -1
- package/skills/yad-connect-design/references/design-registry.md +4 -2
- package/skills/yad-connect-learning/SKILL.md +140 -0
- package/skills/yad-connect-learning/references/learning-context.md +79 -0
- package/skills/yad-connect-learning/references/learning-registry.md +60 -0
- package/skills/yad-connect-testing/SKILL.md +121 -0
- package/skills/yad-connect-testing/references/testing-context.md +67 -0
- package/skills/yad-connect-testing/references/testing-registry.md +55 -0
- package/skills/yad-epic/SKILL.md +10 -5
- package/skills/yad-epic/references/state-schema.md +42 -11
- package/skills/yad-hub-bridge/SKILL.md +2 -2
- package/skills/yad-learn/SKILL.md +146 -0
- package/skills/yad-learn/references/learning-state.md +75 -0
- package/skills/yad-review-gate/SKILL.md +14 -11
- package/skills/yad-review-gate/references/gating.md +1 -1
- package/skills/yad-run/references/run-loop.md +3 -3
- package/skills/yad-spec/SKILL.md +3 -1
- package/skills/yad-status/SKILL.md +35 -15
- package/skills/yad-stories/SKILL.md +3 -1
- package/skills/yad-test-cases/SKILL.md +173 -0
- 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.
|
package/skills/yad-epic/SKILL.md
CHANGED
|
@@ -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 **
|
|
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
|
|
109
|
-
Create `{project-root}/epics/EP-<slug>/.sdlc/state.json` describing the full **
|
|
110
|
-
sequence (no analysis), all steps defaulting to `automation: human_approve`, with the
|
|
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** (
|
|
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
|
|
32
|
-
starts `blocked`.
|
|
33
|
-
- **Without analysis** (
|
|
34
|
-
`epic → epic-review → … → stories-review`. Seeded `currentStep` is
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
`analysis-review`
|
|
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
|
|
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
|
|