yadflow 2.16.1 → 2.17.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 +3 -3
- package/README.md +27 -7
- package/cli/artifact-status.mjs +6 -1
- package/cli/epic-state.mjs +66 -4
- package/cli/gate.mjs +19 -2
- package/cli/manifest.mjs +2 -1
- package/cli/next.mjs +26 -11
- package/package.json +2 -2
- package/skills/sdlc/config.yaml +18 -4
- package/skills/sdlc/install.sh +1 -1
- package/skills/sdlc/module-help.csv +1 -0
- package/skills/yad-analysis/SKILL.md +12 -1
- package/skills/yad-discovery/SKILL.md +132 -0
- package/skills/yad-discovery/references/discovery-schema.md +106 -0
- package/skills/yad-docs-overview/references/pipeline-model.md +14 -2
- package/skills/yad-epic/SKILL.md +14 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
# [2.17.0](https://github.com/abdelrahmannasr/yadflow/compare/v2.16.1...v2.17.0) (2026-06-26)
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
###
|
|
4
|
+
### Features
|
|
5
5
|
|
|
6
|
-
* **
|
|
6
|
+
* **discovery:** add yad-discovery project front-zero phase ([#82](https://github.com/abdelrahmannasr/yadflow/issues/82)) ([4bb2a92](https://github.com/abdelrahmannasr/yadflow/commit/4bb2a928ea37cbd7d7b21f7c28a96a20492f527e))
|
|
7
7
|
|
|
8
8
|
# [2.2.0](https://github.com/abdelrahmannasr/yadflow/compare/v2.1.0...v2.2.0) (2026-06-14)
|
|
9
9
|
|
package/README.md
CHANGED
|
@@ -23,10 +23,11 @@ a scaffolded module that installs cleanly, and a working **team review gate** yo
|
|
|
23
23
|
|
|
24
24
|
## The workflow at a glance
|
|
25
25
|
|
|
26
|
-
The whole lifecycle, from an empty project to shipped code. Setup is one-time; the
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
The whole lifecycle, from an empty project to shipped code. Setup is one-time; the optional
|
|
27
|
+
**front-zero** (`yad-discovery`) frames the whole project once — market, feasibility, and a phased
|
|
28
|
+
roadmap; the **front half** is human-gated and runs once per epic in the product hub; the **build
|
|
29
|
+
half** runs once per story per code repo; **automation** is opt-in and earned. `yad-status` reads it
|
|
30
|
+
all; `yad-hub-bridge` mirrors front-half reviews to real PR/MRs.
|
|
30
31
|
|
|
31
32
|
<!-- Source: docs/diagrams/sdlc-overview.mmd — edit the .mmd and run `npm run diagrams` to regenerate -->
|
|
32
33
|

|
|
@@ -44,6 +45,7 @@ human**. Detailed walkthroughs for each phase follow below.
|
|
|
44
45
|
| `RESEARCH-NOTES.md` | Verified Phase 0 facts about BMAD, Spec Kit, Repomix, Impeccable + deviations. |
|
|
45
46
|
| `skills/sdlc/` | Module source of truth (`config.yaml`, `module-help.csv`, `install.sh`). Survives BMAD updates. |
|
|
46
47
|
| `bin/`, `cli/` | The `yad` setup/update CLI (published to npm as `yadflow`). |
|
|
48
|
+
| `skills/yad-discovery/` | Optional front-zero (once per project, greenfield + brownfield): market research, competitor study, feasibility, current-state, requirements (functional + non-functional) and a phased roadmap (MVP+) under the reserved `EP-discovery`. `roadmap.md` is the menu of features each epic reads. |
|
|
47
49
|
| `skills/yad-analysis/` | Optional front state 1: pressure-test the idea with the analyst into `analysis.md` (skippable). |
|
|
48
50
|
| `skills/yad-epic/` | Front state 1: author an epic with AI assist, assign its `EP-<slug>` ID, seed state. |
|
|
49
51
|
| `skills/yad-architecture/` | Front state 3: author `architecture.md` + the locked `contract.md`; hash-lock the contract surface. |
|
|
@@ -150,7 +152,7 @@ does / why / what to enter / what skipping means), and the step count adapts.
|
|
|
150
152
|
0. **Profile** — the three questions above, plus "configure optional tools now?". Pre-answer for
|
|
151
153
|
CI/scripts with `--solo`/`--team <n>`, `--greenfield`/`--brownfield`, `--monorepo`/`--separate`, `--tools`.
|
|
152
154
|
1. **Preflight** — confirm the hub is a git repo (offers `git init`); check `git`/`node`/`npx`.
|
|
153
|
-
2. **Install the module** — copy all
|
|
155
|
+
2. **Install the module** — copy all 31 `yad-*` skills into the IDE skill dirs you pick
|
|
154
156
|
(`.claude/`, `.agents/`, `.zencoder/`, `.opencode/`) and register `_bmad/sdlc/`.
|
|
155
157
|
3. **Hub platform & roster** — detect GitHub/GitLab from the remote; record reviewers → `.sdlc/hub.json`.
|
|
156
158
|
**Solo skips the roster** (you review by merging your own PR). Edit the roster any time with `yad roster`.
|
|
@@ -204,7 +206,7 @@ with a fix-it hint per finding. Failures carry stable, greppable codes, also pri
|
|
|
204
206
|
|
|
205
207
|
Filing a bug? Attach `yad doctor --json` — it contains no secrets (names, paths, and check results only).
|
|
206
208
|
|
|
207
|
-
## Agent skills (all
|
|
209
|
+
## Agent skills (all 31)
|
|
208
210
|
|
|
209
211
|
The CLI **installs and wires** the module; the skills below are the **agents you invoke by name** in your
|
|
210
212
|
AI IDE (e.g. *“run `yad-epic`”*) to actually do the work. State lives in files you can also edit
|
|
@@ -274,6 +276,17 @@ directly. Each skill stops at a gate and never auto-advances unless a step has *
|
|
|
274
276
|
never touches epic state, approvals, or the contract lock. *AI builds, the hand decides* — and now the
|
|
275
277
|
hand can also learn, on demand, what it is deciding about.
|
|
276
278
|
|
|
279
|
+
### Front-zero — frame the whole project (once per project, optional, human-gated)
|
|
280
|
+
|
|
281
|
+
- **`yad-discovery`** — *Optional* front-zero, for **greenfield and brownfield**. With the analyst
|
|
282
|
+
and pm, run market research, a **competitor study** (both modes), a feasibility study, and — in
|
|
283
|
+
brownfield — a code-aware current-state study, then distil a **functional + non-functional
|
|
284
|
+
requirements** list and a **phased roadmap** (an explicit **MVP** phase, then later phases) under the
|
|
285
|
+
reserved `EP-discovery` ("epic zero"). It is gated by the same review gate (base rule: owner + 1
|
|
286
|
+
reviewer); on approval it terminates at `discovery-done` (no build half). Its `roadmap.md` is the menu
|
|
287
|
+
of features — each `yad-epic` reads it for project context (reference-only; discovery never
|
|
288
|
+
auto-seeds epics).
|
|
289
|
+
|
|
277
290
|
### Front half — author the "thinking" (once per epic, human-gated)
|
|
278
291
|
|
|
279
292
|
- **`yad-analysis`** — *Optional* front state 1. With the analyst, pressure-test a feature idea
|
|
@@ -423,6 +436,10 @@ drive it deterministically with the **`yad gate`** CLI (`open → sync → …
|
|
|
423
436
|
the per-step PR/MR and the step **auto-advances on merge** once approvals are satisfied and all comment
|
|
424
437
|
threads are resolved. Details: **“Run the full front half by hand”** below.
|
|
425
438
|
|
|
439
|
+
0. *(optional, once per project)* `yad-discovery` → the discovery set (`market-research.md`,
|
|
440
|
+
`competitor-analysis.md`, `current-state.md`, `feasibility.md`, `requirements.md`, `roadmap.md`)
|
|
441
|
+
under the reserved `EP-discovery` → review (base rule) → `currentStep: discovery-done`. The whole
|
|
442
|
+
set is required to review; its `roadmap.md` then frames each epic below (read once it is approved).
|
|
426
443
|
6. `yad-epic` → `epic.md` (assigns `EP-<slug>`, seeds state) → review (base rule).
|
|
427
444
|
7. `yad-architecture` → `architecture.md` + locked `contract.md` → review (**escalated**: contract).
|
|
428
445
|
8. `yad-ui` → `ui-design.md` + `DESIGN.md` → review (base rule).
|
|
@@ -468,7 +485,10 @@ Details: **“Run the back half on the dial”** below.
|
|
|
468
485
|
|
|
469
486
|
## Run the full front half by hand
|
|
470
487
|
|
|
471
|
-
|
|
488
|
+
Optionally preceded once per project by the **front-zero** — **`yad-discovery` → review →
|
|
489
|
+
`discovery-done`** — which frames the whole product (market, competitor, feasibility, requirements,
|
|
490
|
+
roadmap) under the reserved `EP-discovery`; its approved `roadmap.md` then feeds each epic. The front
|
|
491
|
+
half itself walks **epic → review → architecture+contract → review → UI design → review → stories
|
|
472
492
|
→ review → `ready-for-build`**, then **test cases → review** runs as a **parallel, non-blocking track**
|
|
473
493
|
alongside the build half. It is all files under `epics/EP-<slug>/`. The skills below guide you, but you
|
|
474
494
|
can also edit the files directly — that's the point.
|
package/cli/artifact-status.mjs
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import fs from 'node:fs';
|
|
7
7
|
import path from 'node:path';
|
|
8
8
|
import { c, log, ok, info, readJSONStrict } from './lib.mjs';
|
|
9
|
-
import { epicRoot, artifactBase, artifactFromBase, findReviewStep } from './epic-state.mjs';
|
|
9
|
+
import { epicRoot, artifactBase, artifactFromBase, findReviewStep, DISCOVERY_FILES } from './epic-state.mjs';
|
|
10
10
|
import { epicFiles } from './manifest.mjs';
|
|
11
11
|
|
|
12
12
|
// The front-gate lifecycle this command manages. Forward-only: a status is only ever moved UP this
|
|
@@ -75,6 +75,11 @@ export async function syncStatuses(root, { epic, dryRun = false } = {}) {
|
|
|
75
75
|
files.push({ base: 'stories', file: path.join(storiesDir, f) });
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
|
+
// Discovery ("epic zero") set: the project-discovery files all key to the single discovery /
|
|
79
|
+
// discovery-review step pair, so they reconcile together (mirrors the stories/ set above).
|
|
80
|
+
for (const f of DISCOVERY_FILES) {
|
|
81
|
+
if (fs.existsSync(path.join(dir, f))) files.push({ base: 'discovery', file: path.join(dir, f) });
|
|
82
|
+
}
|
|
78
83
|
|
|
79
84
|
for (const { base, file } of files) {
|
|
80
85
|
if (!fs.existsSync(file)) continue;
|
package/cli/epic-state.mjs
CHANGED
|
@@ -38,6 +38,7 @@ export function parseReviewBranch(branch = '') {
|
|
|
38
38
|
// (storiesHash fingerprints the directory), so any story branch syncs the same review step.
|
|
39
39
|
export function artifactFromBase(base) {
|
|
40
40
|
if (base === 'stories' || /^stories-S\d+$/i.test(base)) return 'stories/';
|
|
41
|
+
if (base === 'discovery') return 'discovery/';
|
|
41
42
|
return `${base}.md`;
|
|
42
43
|
}
|
|
43
44
|
|
|
@@ -47,6 +48,7 @@ export function artifactFromBase(base) {
|
|
|
47
48
|
export function artifactPaths(base) {
|
|
48
49
|
if (base === 'architecture') return ['architecture.md', 'contract.md', '.sdlc/contract-lock.json'];
|
|
49
50
|
if (base === 'stories') return ['stories'];
|
|
51
|
+
if (base === 'discovery') return [...DISCOVERY_FILES];
|
|
50
52
|
return [`${base}.md`];
|
|
51
53
|
}
|
|
52
54
|
|
|
@@ -87,13 +89,41 @@ export function storiesHash(epicDir) {
|
|
|
87
89
|
return 'sha256:' + createHash('sha256').update(parts.join('\n')).digest('hex');
|
|
88
90
|
}
|
|
89
91
|
|
|
92
|
+
// The reserved id of the project front-zero ("epic zero"). yad-discovery seeds it; yad-epic /
|
|
93
|
+
// yad-analysis must never pick this slug for a feature.
|
|
94
|
+
export const DISCOVERY_EPIC = 'EP-discovery';
|
|
95
|
+
|
|
96
|
+
// The project-discovery artifact set (EP-discovery / "epic zero"). The `discovery-review` step binds
|
|
97
|
+
// to the whole set, mirroring how stories-review binds to the stories/ directory — editing any file
|
|
98
|
+
// revokes prior approvals. A fixed list (not a dir scan) because the files live in the epic root.
|
|
99
|
+
export const DISCOVERY_FILES = [
|
|
100
|
+
'market-research.md',
|
|
101
|
+
'competitor-analysis.md',
|
|
102
|
+
'current-state.md',
|
|
103
|
+
'feasibility.md',
|
|
104
|
+
'requirements.md',
|
|
105
|
+
'roadmap.md',
|
|
106
|
+
];
|
|
107
|
+
|
|
108
|
+
// Deterministic fingerprint of the discovery set: hash every file in the fixed DISCOVERY_FILES order,
|
|
109
|
+
// combine. The WHOLE set is the reviewable unit — if any required artifact is missing the discovery is
|
|
110
|
+
// incomplete and NON-REVIEWABLE, so this returns null (no hash to bind an approval to), the same
|
|
111
|
+
// "nothing to lock" signal storiesHash/contractSurfaceHash give for an absent/malformed surface. Once
|
|
112
|
+
// the full set exists, an edit (or deletion) of any file changes the hash and revokes prior approvals.
|
|
113
|
+
export function discoveryHash(epicDir) {
|
|
114
|
+
if (!DISCOVERY_FILES.every((f) => fs.existsSync(path.join(epicDir, f)))) return null;
|
|
115
|
+
const parts = DISCOVERY_FILES.map((f) => `${f}:${fileSha(path.join(epicDir, f))}`);
|
|
116
|
+
return 'sha256:' + createHash('sha256').update(parts.join('\n')).digest('hex');
|
|
117
|
+
}
|
|
118
|
+
|
|
90
119
|
// The content fingerprint an approval is bound to. For architecture the fingerprint is the locked
|
|
91
|
-
// contract surface (a re-lock => stale); for stories it is the whole stories/ set; for
|
|
92
|
-
// artifact it is the file's bytes.
|
|
120
|
+
// contract surface (a re-lock => stale); for stories it is the whole stories/ set; for discovery it is
|
|
121
|
+
// the whole discovery file set; for every other artifact it is the file's bytes.
|
|
93
122
|
export function artifactHash(epicDir, artifact) {
|
|
94
123
|
const b = artifactBase(artifact);
|
|
95
124
|
if (b === 'architecture') return contractSurfaceHash(epicDir);
|
|
96
125
|
if (b === 'stories') return storiesHash(epicDir);
|
|
126
|
+
if (b === 'discovery') return discoveryHash(epicDir);
|
|
97
127
|
return fileSha(path.join(epicDir, artifact.replace(/\/$/, '')));
|
|
98
128
|
}
|
|
99
129
|
|
|
@@ -221,6 +251,13 @@ export function advanceState(state, step) {
|
|
|
221
251
|
state.currentStep = 'ready-for-build';
|
|
222
252
|
return state;
|
|
223
253
|
}
|
|
254
|
+
// Discovery is the project front-zero ("epic zero"): it has no build half, so its review terminates
|
|
255
|
+
// at a `discovery-done` sentinel rather than `ready-for-build` (which would make `yad next` claim the
|
|
256
|
+
// build half can run). The roadmap it approved is the input the real feature epics read.
|
|
257
|
+
if (step.id === 'discovery-review') {
|
|
258
|
+
state.currentStep = 'discovery-done';
|
|
259
|
+
return state;
|
|
260
|
+
}
|
|
224
261
|
const next = state.steps[i + 1];
|
|
225
262
|
if (next) {
|
|
226
263
|
next.status = next.type === 'review+approve' ? 'in_review' : 'in_progress';
|
|
@@ -244,6 +281,7 @@ export function markInReview(state, step) {
|
|
|
244
281
|
// The front authoring step a `yad next` action maps to — the skill the user invokes for that step.
|
|
245
282
|
// Review (review+approve) steps are driven by the `yad gate` CLI, not a skill, so they are not here.
|
|
246
283
|
export const STEP_SKILL = {
|
|
284
|
+
discovery: 'yad-discovery',
|
|
247
285
|
analysis: 'yad-analysis',
|
|
248
286
|
epic: 'yad-epic',
|
|
249
287
|
architecture: 'yad-architecture',
|
|
@@ -258,8 +296,8 @@ export const STEP_SKILL = {
|
|
|
258
296
|
// (the Phase B rail) and by the driver. No FS / network.
|
|
259
297
|
export function preconditionsMet(state, stepId) {
|
|
260
298
|
if (!state || !Array.isArray(state.steps)) {
|
|
261
|
-
const ok = stepId === 'epic' || stepId === 'analysis';
|
|
262
|
-
return { ok, blockedBy: null, reason: ok ? 'entry step (no
|
|
299
|
+
const ok = stepId === 'epic' || stepId === 'analysis' || stepId === 'discovery';
|
|
300
|
+
return { ok, blockedBy: null, reason: ok ? 'entry step (no state seeded yet)' : `start with yad-epic — no epic state for '${stepId}'` };
|
|
263
301
|
}
|
|
264
302
|
const i = state.steps.findIndex((s) => s.id === stepId);
|
|
265
303
|
if (i === -1) return { ok: false, blockedBy: null, reason: `unknown step '${stepId}'` };
|
|
@@ -281,6 +319,30 @@ export function nextAction(ledger, { epic } = {}) {
|
|
|
281
319
|
const epicId = epic || state?.epicId || null;
|
|
282
320
|
if (!state) return { epicId, kind: 'new', skill: 'yad-epic', why: 'no epic state yet — seed it with yad-epic' };
|
|
283
321
|
|
|
322
|
+
// EP-discovery ("epic zero") is the project front-zero: a 2-step author→review chain with no build
|
|
323
|
+
// half and no parallel track. Resolve its action in isolation so the feature-epic logic below never
|
|
324
|
+
// applies to it.
|
|
325
|
+
if (state.kind === 'discovery') {
|
|
326
|
+
if (state.currentStep === 'discovery-done') {
|
|
327
|
+
return { epicId, kind: 'discovery-done', step: 'discovery-done', status: 'done',
|
|
328
|
+
why: 'discovery approved — seed feature epics with yad-epic (each reads roadmap.md)' };
|
|
329
|
+
}
|
|
330
|
+
const dstep = state.steps.find((s) => s.id === state.currentStep)
|
|
331
|
+
|| state.steps.find((s) => s.status !== 'done');
|
|
332
|
+
if (!dstep) return { epicId, kind: 'discovery-done', step: 'discovery-done', why: 'discovery is done' };
|
|
333
|
+
if (dstep.type === 'author') {
|
|
334
|
+
return { epicId, kind: 'author', step: dstep.id, status: dstep.status,
|
|
335
|
+
skill: STEP_SKILL[dstep.id] || null, artifact: dstep.artifact,
|
|
336
|
+
why: `${dstep.id} is ${dstep.status} — author ${dstep.artifact}` };
|
|
337
|
+
}
|
|
338
|
+
const dpr = (ledger.hubPrs || []).find((p) => artifactBase(p.artifact) === artifactBase(dstep.artifact));
|
|
339
|
+
const dverb = dpr ? 'sync' : 'open';
|
|
340
|
+
return { epicId, kind: dpr ? 'review-sync' : 'review-open', step: dstep.id, status: dstep.status,
|
|
341
|
+
artifact: dstep.artifact, pr: dpr ? dpr.number : null,
|
|
342
|
+
command: `yad gate ${dverb} ${epicId} ${dstep.artifact}`,
|
|
343
|
+
why: dpr ? `review PR #${dpr.number} is open — sync its state to advance` : `${dstep.id} is open — create the review PR/MR` };
|
|
344
|
+
}
|
|
345
|
+
|
|
284
346
|
// The parallel test-cases track stays workable even once the epic is ready-for-build.
|
|
285
347
|
const tc = state.steps.find((s) => s.id === 'test-cases');
|
|
286
348
|
const tcOpen = !!tc && tc.status !== 'done' && tc.status !== 'blocked';
|
package/cli/gate.mjs
CHANGED
|
@@ -11,7 +11,7 @@ import { PROJECT_FILES } from './manifest.mjs';
|
|
|
11
11
|
import {
|
|
12
12
|
epicRoot, loadLedger, findReviewStep, artifactBase, artifactHash, gatePredicate,
|
|
13
13
|
advanceState, markInReview, isEscalated, parseReviewBranch, artifactFromBase,
|
|
14
|
-
upsertHubPr,
|
|
14
|
+
upsertHubPr, DISCOVERY_FILES,
|
|
15
15
|
} from './epic-state.mjs';
|
|
16
16
|
import { readPr, mapApprovers, createPr, reviewersForScopes, resolveCommitterLogin } from './platform.mjs';
|
|
17
17
|
import { syncStatuses } from './artifact-status.mjs';
|
|
@@ -48,7 +48,12 @@ export function touchedDomains(epicDir, step) {
|
|
|
48
48
|
return frontmatter(path.join(epicDir, 'epic.md')).repos || [];
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
// The artifact owner shown in the review PR/MR body. Feature epics carry it in epic.md; the discovery
|
|
52
|
+
// front-zero (EP-discovery) has no epic.md, so fall back to roadmap.md's frontmatter owner.
|
|
53
|
+
const ownerOf = (epicDir) =>
|
|
54
|
+
frontmatter(path.join(epicDir, 'epic.md')).owner
|
|
55
|
+
|| frontmatter(path.join(epicDir, 'roadmap.md')).owner
|
|
56
|
+
|| '<owner>';
|
|
52
57
|
|
|
53
58
|
// A null architecture hash with a BEGIN marker present means the surface block is malformed
|
|
54
59
|
// (no END, or empty) — approvals would not be hash-bound, so make that visible.
|
|
@@ -61,6 +66,16 @@ function warnUnlockedContract(epicDir, artifact) {
|
|
|
61
66
|
}
|
|
62
67
|
}
|
|
63
68
|
|
|
69
|
+
// A null discovery hash means the discovery set is incomplete (a required artifact is missing), so the
|
|
70
|
+
// review is not yet reviewable and an approval would not be hash-bound. Name the missing files so the
|
|
71
|
+
// owner can complete the set before the gate is opened/advanced (mirrors warnUnlockedContract).
|
|
72
|
+
function warnIncompleteDiscovery(epicDir, artifact) {
|
|
73
|
+
if (artifactBase(artifact) !== 'discovery') return;
|
|
74
|
+
if (artifactHash(epicDir, artifact) !== null) return;
|
|
75
|
+
const missing = DISCOVERY_FILES.filter((f) => !fs.existsSync(path.join(epicDir, f)));
|
|
76
|
+
warn(`discovery set incomplete — missing ${missing.join(', ')}; review is not yet reviewable (approvals will not be hash-bound until the full set exists)`);
|
|
77
|
+
}
|
|
78
|
+
|
|
64
79
|
// Fail fast on a corrupt or wrong-shape hub config: a silently-defaulted hub.json would degrade
|
|
65
80
|
// every gate to file-only without anyone noticing, and a typo'd platform would read as "no bridge".
|
|
66
81
|
function loadHub(root) {
|
|
@@ -202,6 +217,7 @@ export async function gateSync(root, { epic, artifact, today, reader = readPr, l
|
|
|
202
217
|
|
|
203
218
|
const curHash = artifactHash(epicDir, pr.artifact);
|
|
204
219
|
warnUnlockedContract(epicDir, pr.artifact);
|
|
220
|
+
warnIncompleteDiscovery(epicDir, pr.artifact);
|
|
205
221
|
const recs = mapApprovers(pull.reviews, { roster, repos, touchedDomains: domains, headOid: pull.headOid });
|
|
206
222
|
approvals = upsertBridge(approvals, recs, { stepId: step.id, artifact: pr.artifact, curHash, today });
|
|
207
223
|
|
|
@@ -465,6 +481,7 @@ export async function gateOpen(root, { epic, artifact, head, creator = createPr
|
|
|
465
481
|
const branch = head || `review/${epic}/${b}`;
|
|
466
482
|
const domains = touchedDomains(epicDir, step);
|
|
467
483
|
warnUnlockedContract(epicDir, artifact);
|
|
484
|
+
warnIncompleteDiscovery(epicDir, artifact);
|
|
468
485
|
|
|
469
486
|
const bridge = isBridge(hub);
|
|
470
487
|
// Outside bridge mode (file-only, OR a platform with no gate-sync CI) there is no CI to write the
|
package/cli/manifest.mjs
CHANGED
|
@@ -10,8 +10,9 @@ 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
|
|
13
|
+
// The hand-authored yad-* skills (mirrors skills/sdlc/install.sh).
|
|
14
14
|
export const SKILLS = [
|
|
15
|
+
'yad-discovery',
|
|
15
16
|
'yad-analysis',
|
|
16
17
|
'yad-epic',
|
|
17
18
|
'yad-architecture',
|
package/cli/next.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// `yad next` — the unified next-step driver. Read-only: it never writes state or acts. It reads the
|
|
2
2
|
// file ledger and prints the ONE concrete, copy-pasteable next action (and a one-line why), so a user
|
|
3
|
-
// never has to remember which of the
|
|
3
|
+
// never has to remember which of the 31 skills / gate commands comes next. "Guide, don't act" — the
|
|
4
4
|
// front half still never auto-advances.
|
|
5
5
|
//
|
|
6
6
|
// yad next general orientation across the whole project
|
|
@@ -11,7 +11,7 @@ import fs from 'node:fs';
|
|
|
11
11
|
import path from 'node:path';
|
|
12
12
|
import { c, log, ok, info, warn, hand, fail, readJSON, exists } from './lib.mjs';
|
|
13
13
|
import { PROJECT_FILES } from './manifest.mjs';
|
|
14
|
-
import { epicRoot, loadLedger, nextAction, preconditionsMet, isValidEpicId } from './epic-state.mjs';
|
|
14
|
+
import { epicRoot, loadLedger, nextAction, preconditionsMet, isValidEpicId, DISCOVERY_EPIC } from './epic-state.mjs';
|
|
15
15
|
|
|
16
16
|
// Is solo mode on? Persisted in hub.json by setup (Phase C/D); default false. Read defensively so a
|
|
17
17
|
// missing/old hub.json never breaks the driver.
|
|
@@ -47,6 +47,8 @@ function actionLine(a, { solo } = {}) {
|
|
|
47
47
|
return `${c.bold(a.command)}${solo ? c.dim(' (solo: no approval needed — just merge your own PR)') : ''}`;
|
|
48
48
|
case 'build':
|
|
49
49
|
return `${c.bold('yad-run')} ${c.dim('(or per story: yad-spec → yad-implement → yad ship → yad-engineer-review)')}`;
|
|
50
|
+
case 'discovery-done':
|
|
51
|
+
return `invoke the ${c.bold('yad-epic')} skill ${c.dim('(seed a feature epic from roadmap.md)')}`;
|
|
50
52
|
default:
|
|
51
53
|
return c.dim('nothing to do');
|
|
52
54
|
}
|
|
@@ -67,23 +69,36 @@ function generalNext(root, { all } = {}) {
|
|
|
67
69
|
hand(`run ${c.bold('yad setup')} ${c.dim('(then come back to `yad next`)')}`);
|
|
68
70
|
return;
|
|
69
71
|
}
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
const solo = isSolo(root);
|
|
73
|
+
const brownfield = profileOf(root)?.codebase === 'brownfield';
|
|
74
|
+
// The project front-zero (EP-discovery / "epic zero") is not a feature epic — split it out so it is
|
|
75
|
+
// surfaced on its own line and never mixed into the feature-epic roll-up.
|
|
76
|
+
const allEpics = listEpics(root);
|
|
77
|
+
const hasDiscovery = allEpics.includes(DISCOVERY_EPIC);
|
|
78
|
+
const featureEpics = allEpics.filter((id) => id !== DISCOVERY_EPIC);
|
|
79
|
+
const discoveryAction = hasDiscovery
|
|
80
|
+
? nextAction(loadLedger(epicRoot(root, DISCOVERY_EPIC)), { epic: DISCOVERY_EPIC })
|
|
81
|
+
: null;
|
|
82
|
+
const discoveryOpen = !!discoveryAction && discoveryAction.kind !== 'discovery-done';
|
|
83
|
+
|
|
84
|
+
if (!featureEpics.length) {
|
|
85
|
+
if (discoveryOpen) { printAction(discoveryAction, { solo }); return; }
|
|
86
|
+
log(`\n ${c.bold('Set up — no feature epics yet.')}`);
|
|
74
87
|
if (brownfield) hand(`capture what already exists first: invoke the ${c.bold('yad-backfill')} skill`);
|
|
75
|
-
hand(`
|
|
88
|
+
if (!hasDiscovery) hand(`frame the whole project (market, feasibility, roadmap): invoke the ${c.bold('yad-discovery')} skill ${c.dim('(optional front-zero)')}`);
|
|
89
|
+
hand(`start your first epic: invoke the ${c.bold('yad-epic')} skill${hasDiscovery ? c.dim(' (it reads the approved roadmap.md)') : ''}`);
|
|
76
90
|
return;
|
|
77
91
|
}
|
|
78
|
-
const solo = isSolo(root);
|
|
79
|
-
const actions = epics.map((id) => nextAction(loadLedger(epicRoot(root, id)), { epic: id }));
|
|
80
92
|
|
|
81
|
-
|
|
93
|
+
const actions = featureEpics.map((id) => nextAction(loadLedger(epicRoot(root, id)), { epic: id }));
|
|
94
|
+
if (discoveryOpen) printAction(discoveryAction, { solo }); // an unfinished discovery comes first
|
|
95
|
+
|
|
96
|
+
if (featureEpics.length === 1 || all) {
|
|
82
97
|
for (const a of actions) printAction(a, { solo });
|
|
83
98
|
return;
|
|
84
99
|
}
|
|
85
100
|
// Several epics — list each with a one-liner, then point at the per-epic / --all views.
|
|
86
|
-
log(`\n ${c.bold(`${
|
|
101
|
+
log(`\n ${c.bold(`${featureEpics.length} epics`)} ${c.dim('— next action each:')}`);
|
|
87
102
|
for (const a of actions) log(` ${c.cyan(a.epicId)} ${actionLine(a, { solo })}`);
|
|
88
103
|
info(c.dim(`detail: ${c.bold('yad next <epic>')} • all at once: ${c.bold('yad next --all')}`));
|
|
89
104
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "yadflow",
|
|
3
|
-
"version": "2.
|
|
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, ship, repo). A BMAD module +
|
|
3
|
+
"version": "2.17.0",
|
|
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, ship, repo). A BMAD module + 31 yad-* skills.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "AbdelRahman Nasr",
|
|
7
7
|
"license": "MIT",
|
package/skills/sdlc/config.yaml
CHANGED
|
@@ -10,6 +10,19 @@ methodology: gated-team-multirepo-sdlc
|
|
|
10
10
|
product_root: "{project-root}"
|
|
11
11
|
epics_folder: "{project-root}/epics"
|
|
12
12
|
|
|
13
|
+
# Project discovery (yad-discovery) — the OPTIONAL front-zero, run once per project (greenfield AND
|
|
14
|
+
# brownfield). It is modelled as the reserved "epic zero" `EP-discovery` so the entire review gate +
|
|
15
|
+
# PR/MR bridge + CI sync + `yad next` operate on it unchanged. A 2-step author→review chain whose
|
|
16
|
+
# review binds to the whole artifact set; on approval it terminates at `discovery-done` (no build half).
|
|
17
|
+
# Output: a phased roadmap (incl. MVP) that each feature epic reads (yad-epic Step 2c) — reference-only,
|
|
18
|
+
# never auto-seeds epics.
|
|
19
|
+
discovery:
|
|
20
|
+
epic_id: "EP-discovery" # reserved id; yad-epic/yad-analysis never pick it
|
|
21
|
+
location: "{project-root}/epics/EP-discovery/" # discovery artifacts + ledger live here
|
|
22
|
+
optional: true # skip-able; a team that knows what to build starts at yad-epic
|
|
23
|
+
modes: [greenfield, brownfield] # current-state is code-aware in brownfield; competitor study in both
|
|
24
|
+
artifacts: [market-research.md, competitor-analysis.md, current-state.md, feasibility.md, requirements.md, roadmap.md]
|
|
25
|
+
|
|
13
26
|
# Core configuration values (inherited convention from _bmad/config.toml).
|
|
14
27
|
project_name: yadflow
|
|
15
28
|
communication_language: English
|
|
@@ -20,11 +33,12 @@ output_folder: "{project-root}/_bmad-output"
|
|
|
20
33
|
defaults:
|
|
21
34
|
assistance: review # none | review | heavy
|
|
22
35
|
automation: human_approve # human_approve | machine_advance
|
|
23
|
-
# Front steps (analysis [optional], epic, architecture, ui-design,
|
|
24
|
-
# human_approve and may NOT be set to machine_advance in this
|
|
36
|
+
# Front steps (discovery [optional front-zero], analysis [optional], epic, architecture, ui-design,
|
|
37
|
+
# stories, test-cases) are locked to human_approve and may NOT be set to machine_advance in this
|
|
38
|
+
# version (build plan §1, §8.7).
|
|
25
39
|
front_steps_locked: true
|
|
26
40
|
# Each front authoring step opens its own branch at the start of the step (the <step> is the step id:
|
|
27
|
-
# analysis | epic | architecture | ui-design | stories | test-cases). Git/greenfield-safe; distinct
|
|
41
|
+
# discovery | analysis | epic | architecture | ui-design | stories | test-cases). Git/greenfield-safe; distinct
|
|
28
42
|
# from the bridge's review branch (hub.artifact_branch). See yad-epic/references/state-schema.md.
|
|
29
43
|
front_authoring_branch: "<step>/EP-<slug>"
|
|
30
44
|
|
|
@@ -238,7 +252,7 @@ automation:
|
|
|
238
252
|
# Hard lock — the dial-setter REFUSES machine_advance for these, regardless of trust evidence.
|
|
239
253
|
# The front authoring steps (already locked:true in state.json; analysis is optional) + the human
|
|
240
254
|
# merge gate.
|
|
241
|
-
locked_steps: [analysis, epic, architecture, ui-design, stories, test-cases, engineer-review]
|
|
255
|
+
locked_steps: [discovery, analysis, epic, architecture, ui-design, stories, test-cases, engineer-review]
|
|
242
256
|
# Kill switch (phase-4-build-plan.md §Safety): true => every step forced to human_approve
|
|
243
257
|
# system-wide, no per-step edits. One line, instantly reversible. Toggle via `yad-run action: kill`.
|
|
244
258
|
kill_switch: false
|
package/skills/sdlc/install.sh
CHANGED
|
@@ -11,7 +11,7 @@ set -euo pipefail
|
|
|
11
11
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
12
12
|
cd "$ROOT"
|
|
13
13
|
|
|
14
|
-
SKILLS=(yad-analysis yad-epic yad-architecture yad-ui yad-stories yad-test-cases yad-connect-repos yad-sync-repos yad-connect-design yad-connect-testing yad-connect-learning yad-connect-docs yad-docs yad-docs-overview yad-docs-sync yad-learn yad-spec yad-implement yad-checks yad-pr-template yad-review-comments yad-hub-bridge yad-commit yad-open-pr yad-ship yad-engineer-review yad-backfill yad-run yad-review-gate yad-status)
|
|
14
|
+
SKILLS=(yad-discovery yad-analysis yad-epic yad-architecture yad-ui yad-stories yad-test-cases yad-connect-repos yad-sync-repos yad-connect-design yad-connect-testing yad-connect-learning yad-connect-docs yad-docs yad-docs-overview yad-docs-sync yad-learn yad-spec yad-implement yad-checks yad-pr-template yad-review-comments yad-hub-bridge yad-commit yad-open-pr yad-ship yad-engineer-review yad-backfill yad-run yad-review-gate yad-status)
|
|
15
15
|
|
|
16
16
|
echo "Installing sdlc module from $ROOT/skills ..."
|
|
17
17
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
module,skill,display-name,menu-code,description,action,args,phase,preceded-by,followed-by,required,output-location,outputs
|
|
2
|
+
SDLC Workflow,yad-discovery,Project Discovery,DI,"Optional front-zero (once per project, greenfield AND brownfield): with the analyst + pm run market research, a competitor study, a feasibility study, and (brownfield) a current-state study, then distil functional + non-functional requirements and a phased roadmap (MVP and beyond) under the reserved EP-discovery. roadmap.md becomes the menu of features each yad-epic reads. Never auto-advances.",,{idea: one-line product idea} {mode: greenfield|brownfield},0-front,,yad-review-gate,false,epics/EP-discovery/,market-research.md competitor-analysis.md current-state.md feasibility.md requirements.md roadmap.md state.json
|
|
2
3
|
SDLC Workflow,yad-analysis,Author Analysis,AN,"Optional front state: with the analyst pressure-test a feature idea and write the discovery brief into analysis.md. Assigns the EP-<slug> ID and seeds .sdlc state (the chain that puts analysis before epic). If skipped, the epic step does this shaping inline. Never auto-advances.",,{idea: one-line feature idea},1-front,,yad-review-gate,false,epics/EP-<slug>/,analysis.md state.json
|
|
3
4
|
SDLC Workflow,yad-epic,Author Epic,AE,"Front state 1: shape an idea with analyst then pm into epic.md; assign EP-<slug> ID and seed .sdlc state. Never auto-advances.",,{idea: one-line feature idea},1-front,,yad-review-gate,true,epics/EP-<slug>/,epic.md state.json
|
|
4
5
|
SDLC Workflow,yad-review-gate,Team Review Gate,RG,"Reusable review+approve gate for all five reviews. Shares an artifact for review, records comments and approvals as files, enforces owner + 1 reviewer (escalates on contract/auth/payments; per-repo routing for stories), advances state only when approved.",,{artifact: file under the epic} {action: open|comment|approve|advance},1-front,,,true,epics/EP-<slug>/reviews/,reviews/*.md approvals.json state.json
|
|
@@ -50,9 +50,20 @@ connected repo** (the epic's `repos` are not chosen yet), load the lightweight c
|
|
|
50
50
|
stamp `code-context: stale` in the frontmatter.
|
|
51
51
|
- **Traceability:** record which maps you loaded in the analysis frontmatter `code-context:` field.
|
|
52
52
|
|
|
53
|
+
### Step 2c — Read the project roadmap (project context, only once discovery is APPROVED)
|
|
54
|
+
Consume the project front-zero (`yad-discovery`) **only after its review gate has passed** — never a
|
|
55
|
+
draft or in-review roadmap (that would bypass `discovery-review`). Gate on the **state**, not file
|
|
56
|
+
existence: read `{project-root}/epics/EP-discovery/.sdlc/state.json` and proceed **only when
|
|
57
|
+
`currentStep == "discovery-done"`**. When it is, read its `roadmap.md` and sibling `requirements.md`
|
|
58
|
+
for the project framing: which phase (MVP / later) this feature belongs to and the requirements it
|
|
59
|
+
carries — so the analysis's **Problem / Options / Recommendation** stay consistent with the approved
|
|
60
|
+
roadmap. **Optional & non-blocking:** if there is no discovery, or it has not yet reached
|
|
61
|
+
`discovery-done`, proceed unchanged — do not consume an unapproved roadmap.
|
|
62
|
+
|
|
53
63
|
### Step 3 — Generate the Epic ID (engine-assigned, never by hand)
|
|
54
64
|
Derive `EP-<slug>` where `slug` is **2–4 lowercase words joined by hyphens**, drawn from the idea
|
|
55
|
-
(e.g. `EP-istifta-inquiries`). Lowercase except the fixed `EP` prefix.
|
|
65
|
+
(e.g. `EP-istifta-inquiries`). Lowercase except the fixed `EP` prefix. `EP-discovery` is **reserved**
|
|
66
|
+
for the project front-zero — never use it for a feature. **The ID is assigned once and
|
|
56
67
|
never renamed** — renaming breaks every downstream link (build plan §6b). Check
|
|
57
68
|
`{project-root}/epics/` for collisions; if the slug exists, append a distinguishing word.
|
|
58
69
|
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: yad-discovery
|
|
3
|
+
description: 'Optional front-zero of the gated SDLC — the once-per-project discovery phase. With the field-expert lenses (analyst + pm), run market research, a competitor study, a feasibility study, and (brownfield) a current-state study, then distil a functional + non-functional requirements list and a phased roadmap (MVP and beyond) into the reserved EP-discovery. Greenfield AND brownfield. Its roadmap.md becomes the menu of features each yad-epic reads. Seeds the EP-discovery state and hands off to the team review gate; never auto-advances. Use when the user says "start the project", "do discovery", "market research / feasibility / roadmap", or "what should we build first".'
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# SDLC — Project Discovery (optional front-zero, "epic zero")
|
|
7
|
+
|
|
8
|
+
**Goal:** Produce a human-authored, AI-assisted **project-level discovery set** — the field expert's
|
|
9
|
+
requirement-gathering for the whole product — under the reserved `EP-discovery` ("epic zero"), then
|
|
10
|
+
hand off to `yad-review-gate`. The output `roadmap.md` is the menu of features; each feature is later
|
|
11
|
+
taken into the normal `yad-epic` flow, which reads the roadmap for project context.
|
|
12
|
+
|
|
13
|
+
This is a **front state**: human-authored with AI assist and **never auto-advances**. It runs **once
|
|
14
|
+
per project** and is **optional** — a team that already knows what to build can skip it and start at
|
|
15
|
+
`yad-epic`. It supports **both greenfield and brownfield**, and produces a **competitor study in both**.
|
|
16
|
+
|
|
17
|
+
This skill enforces the build plan's core rules: all state lives in files; IDs are engine-assigned
|
|
18
|
+
(the reserved `EP-discovery`, never a typed feature slug); front steps are locked to `human_approve`.
|
|
19
|
+
|
|
20
|
+
## Conventions
|
|
21
|
+
|
|
22
|
+
- `{project-root}` resolves from the project working directory.
|
|
23
|
+
- Discovery artifacts live under `{project-root}/epics/EP-discovery/` (the reserved "epic zero").
|
|
24
|
+
- Speak in the configured `communication_language`; write documents in `document_output_language`.
|
|
25
|
+
|
|
26
|
+
## On Activation
|
|
27
|
+
|
|
28
|
+
### Step 1 — Entry guard (runs once per project)
|
|
29
|
+
The id is the reserved `EP-discovery` — never a feature slug. Discovery seeds its state exactly once:
|
|
30
|
+
if `{project-root}/epics/EP-discovery/.sdlc/state.json` already exists, **STOP** and point the user at
|
|
31
|
+
`yad next EP-discovery` (the phase is in review or done; edit the artifacts in place, don't re-seed).
|
|
32
|
+
When no `state.json` exists yet, proceed and seed state in Step 5.
|
|
33
|
+
|
|
34
|
+
Detect the project mode from `{project-root}/.sdlc/hub.json` `profile.codebase`
|
|
35
|
+
(`greenfield` | `brownfield`, set by `yad setup`). If absent, ask the user; default `greenfield`.
|
|
36
|
+
|
|
37
|
+
### Step 2 — Shape with the field-expert lenses (assist: analyst + pm)
|
|
38
|
+
Adopt the **analyst** lens (`bmad-agent-analyst`, Mary) and the **pm** lens (`bmad-agent-pm`) to gather
|
|
39
|
+
requirements as a domain expert would. Drive the existing BMAD research skills as the assist — they
|
|
40
|
+
already exist in this project:
|
|
41
|
+
- `bmad-market-research` — market size, segments, demand, trends, positioning.
|
|
42
|
+
- `bmad-domain-research` — the problem domain, regulations, and constraints of the field.
|
|
43
|
+
- `bmad-product-brief` — personas, value proposition, success metrics.
|
|
44
|
+
|
|
45
|
+
Pressure-test: who are the users, what problem, what is the market, **who are the competitors and how
|
|
46
|
+
do we differ** (required in BOTH modes), what is feasible, what is the smallest valuable slice (MVP),
|
|
47
|
+
and what sequences after it.
|
|
48
|
+
|
|
49
|
+
### Step 2b — Brownfield current-state (make discovery code-aware)
|
|
50
|
+
Read the registry `{project-root}/.sdlc/repos.json` (`config.yaml` `code_context`). For **every
|
|
51
|
+
connected repo**, load the lightweight code-map `{project-root}/.sdlc/code-context/<repo>/code-map.md`
|
|
52
|
+
and base `current-state.md` on **what already exists** — modules, endpoints, data, gaps — so the
|
|
53
|
+
roadmap extends the real system rather than re-proposing it.
|
|
54
|
+
|
|
55
|
+
- **Greenfield-safe:** if `repos.json` is absent/empty (greenfield), `current-state.md` is a short
|
|
56
|
+
"clean slate / assumptions & non-goals" note, and you proceed.
|
|
57
|
+
- **Staleness:** if a repo's current HEAD (`git -C <path> rev-parse HEAD`) ≠ its registry `syncedHead`,
|
|
58
|
+
warn and suggest `yad repo refresh <repo>` (a human decision — flag, never auto-refresh).
|
|
59
|
+
- **Backfill pointer:** for an existing codebase, point the user at `yad-backfill` to capture specs for
|
|
60
|
+
already-built features; discovery frames the *forward* roadmap, backfill captures the *current* one.
|
|
61
|
+
|
|
62
|
+
### Step 3 — Open the authoring branch
|
|
63
|
+
Open the discovery authoring branch `discovery/EP-discovery` per the shared procedure
|
|
64
|
+
(`../yad-epic/references/state-schema.md` → "Authoring branches"): git-safe (skip with a note if
|
|
65
|
+
`{project-root}` is not a git work tree), check out the branch if it exists, else create it from the
|
|
66
|
+
hub's default branch. Author and commit the discovery set on it. Distinct from the bridge's
|
|
67
|
+
`review/EP-discovery/discovery` branch.
|
|
68
|
+
|
|
69
|
+
### Step 4 — Write the discovery set
|
|
70
|
+
Write these files under `{project-root}/epics/EP-discovery/`. Each is a normal Markdown artifact; the
|
|
71
|
+
gate binds to the **whole set** (editing any one revokes approvals). `roadmap.md` summarises and links
|
|
72
|
+
the others and is the spine of the review.
|
|
73
|
+
|
|
74
|
+
- `market-research.md` — market, segments, demand, trends (assist: `bmad-market-research`).
|
|
75
|
+
- `competitor-analysis.md` — competitors, capabilities, gaps, our differentiation (**both modes**).
|
|
76
|
+
- `current-state.md` — brownfield: what exists today (Step 2b); greenfield: clean-slate assumptions.
|
|
77
|
+
- `feasibility.md` — technical/operational/economic feasibility, risks, viability, go/no-go.
|
|
78
|
+
- `requirements.md` — the consolidated requirements list, **functional AND non-functional**, as a
|
|
79
|
+
table (see `references/discovery-schema.md`). Functional rows are candidate features (registration,
|
|
80
|
+
login, …); non-functional rows are cross-cutting (performance, security, accessibility, i18n …).
|
|
81
|
+
- `roadmap.md` — the phased plan with an explicit **MVP** phase, then later phases. Each feature row
|
|
82
|
+
carries a proposed `EP-<slug>` id, its target phase, and a `status:` of `planned`
|
|
83
|
+
(see `references/discovery-schema.md` for the exact templates).
|
|
84
|
+
|
|
85
|
+
Leave `owner` for the user to set in each frontmatter. Fill the bodies with the user.
|
|
86
|
+
|
|
87
|
+
### Step 5 — Seed the state machine
|
|
88
|
+
Create `{project-root}/epics/EP-discovery/.sdlc/state.json` describing the **2-step** front-zero
|
|
89
|
+
sequence, both steps `automation: human_approve` and `locked`, with the `kind: "discovery"` marker the
|
|
90
|
+
engine keys off. Use this exact shape (see `references/discovery-schema.md`):
|
|
91
|
+
|
|
92
|
+
```json
|
|
93
|
+
{
|
|
94
|
+
"epicId": "EP-discovery",
|
|
95
|
+
"kind": "discovery",
|
|
96
|
+
"createdAt": "<YYYY-MM-DD>",
|
|
97
|
+
"currentStep": "discovery-review",
|
|
98
|
+
"steps": [
|
|
99
|
+
{ "id": "discovery", "type": "author", "artifact": "discovery/", "assistance": "review", "automation": "human_approve", "locked": true, "status": "done", "risk_tags": [] },
|
|
100
|
+
{ "id": "discovery-review", "type": "review+approve", "artifact": "discovery/", "assistance": "review", "automation": "human_approve", "locked": true, "status": "in_review", "risk_tags": [] }
|
|
101
|
+
]
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Notes:
|
|
106
|
+
- The review step's artifact is the virtual base `discovery/` — the gate fingerprints the whole
|
|
107
|
+
discovery file set (`market-research`, `competitor-analysis`, `current-state`, `feasibility`,
|
|
108
|
+
`requirements`, `roadmap`), so editing any of them revokes prior approvals (mirrors `stories/`).
|
|
109
|
+
**All six must exist to review:** if any is missing the set is incomplete and non-reviewable (the
|
|
110
|
+
hash is `null`) and `yad gate open`/`sync` warn — so write all six (Step 4) before the gate.
|
|
111
|
+
- `discovery-review` carries no `risk_tags` — it is the **base** rule (owner + 1 reviewer); discovery
|
|
112
|
+
never escalates to domain owners (no contract surface is touched yet).
|
|
113
|
+
- Also create an empty approvals ledger `.sdlc/approvals.json` and comments ledger
|
|
114
|
+
`.sdlc/comments.json`, each containing `[]`, and the `reviews/` directory.
|
|
115
|
+
|
|
116
|
+
### Step 6 — Stop at the gate (do NOT advance)
|
|
117
|
+
Report: the path to the discovery set, and that the next action is **review** via `yad-review-gate`
|
|
118
|
+
(base rule: owner + 1 reviewer) on the virtual artifact `discovery/`. **Never mark discovery-review
|
|
119
|
+
approved here** — only real reviewers do that through the gate. When the discovery gate passes, the
|
|
120
|
+
state moves to the `discovery-done` sentinel (not `ready-for-build` — discovery has no build half); the
|
|
121
|
+
roadmap is now the input that each `yad-epic` reads (its "Step 2c — read the roadmap"). When the hub
|
|
122
|
+
has a platform, the gate opens a review PR on the hub (via `yad-hub-bridge`) and
|
|
123
|
+
`yad-review-gate action: sync` pulls platform approvals/comments into the ledger; otherwise the review
|
|
124
|
+
is recorded file-only.
|
|
125
|
+
|
|
126
|
+
## Reference
|
|
127
|
+
- Discovery artifact templates + the 2-step state shape: `references/discovery-schema.md`.
|
|
128
|
+
- State schema, chain shapes, and the authoring-branch procedure:
|
|
129
|
+
`../yad-epic/references/state-schema.md`.
|
|
130
|
+
- The epic step that consumes `roadmap.md`: `../yad-epic/SKILL.md` (Step 2c).
|
|
131
|
+
- Capturing already-built features in a brownfield codebase: `../yad-backfill/SKILL.md`.
|
|
132
|
+
- Connecting code repos + the code-context the brain reads: `../yad-connect-repos/SKILL.md`.
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# Discovery schema — artifacts + the front-zero state shape
|
|
2
|
+
|
|
3
|
+
The project discovery phase ("epic zero") lives under `{project-root}/epics/EP-discovery/`. It reuses
|
|
4
|
+
the per-epic ledger files (`.sdlc/state.json`, `approvals.json`, `comments.json`, `reviews/`,
|
|
5
|
+
`hub-prs.json`) unchanged — `EP-discovery` is a valid epic id, so the existing gate, PR/MR bridge, CI
|
|
6
|
+
sync, and `yad next` all operate on it. What is special is the `kind: "discovery"` marker on the state
|
|
7
|
+
object and the 2-step chain.
|
|
8
|
+
|
|
9
|
+
## State (`.sdlc/state.json`)
|
|
10
|
+
|
|
11
|
+
```json
|
|
12
|
+
{
|
|
13
|
+
"epicId": "EP-discovery",
|
|
14
|
+
"kind": "discovery",
|
|
15
|
+
"createdAt": "<YYYY-MM-DD>",
|
|
16
|
+
"currentStep": "discovery-review",
|
|
17
|
+
"steps": [
|
|
18
|
+
{ "id": "discovery", "type": "author", "artifact": "discovery/", "assistance": "review", "automation": "human_approve", "locked": true, "status": "done", "risk_tags": [] },
|
|
19
|
+
{ "id": "discovery-review", "type": "review+approve", "artifact": "discovery/", "assistance": "review", "automation": "human_approve", "locked": true, "status": "in_review", "risk_tags": [] }
|
|
20
|
+
]
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
- `artifact: "discovery/"` is a **virtual** base: `artifactHash` fingerprints the whole discovery file
|
|
25
|
+
set (`discoveryHash` in `cli/epic-state.mjs`), so an edit to any discovery file revokes prior
|
|
26
|
+
approvals — exactly like `stories/` fingerprints the stories directory.
|
|
27
|
+
- The **full set is required to review**: if any of the six files is missing, `discoveryHash` returns
|
|
28
|
+
`null` — the discovery is **incomplete and non-reviewable** (no hash to bind an approval to), and
|
|
29
|
+
`yad gate open` / `yad gate sync` warn with the missing filenames. Write all six (in greenfield,
|
|
30
|
+
`current-state.md` is a short clean-slate note) before handing off to the gate.
|
|
31
|
+
- On approval the gate sets `currentStep: "discovery-done"` (a terminal sentinel — discovery has **no**
|
|
32
|
+
build half, so it never becomes `ready-for-build`).
|
|
33
|
+
- The discovery files (relative to the epic dir) the gate commits on the review branch and re-hashes at
|
|
34
|
+
merge are: `market-research.md`, `competitor-analysis.md`, `current-state.md`, `feasibility.md`,
|
|
35
|
+
`requirements.md`, `roadmap.md` (the `DISCOVERY_FILES` list).
|
|
36
|
+
|
|
37
|
+
## Artifact templates
|
|
38
|
+
|
|
39
|
+
### `requirements.md`
|
|
40
|
+
|
|
41
|
+
```markdown
|
|
42
|
+
---
|
|
43
|
+
id: EP-discovery
|
|
44
|
+
artifact: requirements
|
|
45
|
+
status: draft
|
|
46
|
+
owner:
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Functional requirements
|
|
50
|
+
<!-- candidate features — each becomes (or seeds) a feature epic later -->
|
|
51
|
+
|
|
52
|
+
| Ref | Requirement | Description | Priority | MVP? |
|
|
53
|
+
|-----|-------------|-------------|----------|------|
|
|
54
|
+
| F-01 | Registration | A new user can create an account | must | yes |
|
|
55
|
+
| F-02 | Login | A returning user can authenticate | must | yes |
|
|
56
|
+
|
|
57
|
+
## Non-functional requirements
|
|
58
|
+
<!-- cross-cutting qualities the whole product must hold -->
|
|
59
|
+
|
|
60
|
+
| Ref | Category | Requirement | Target / acceptance |
|
|
61
|
+
|-----|----------|-------------|---------------------|
|
|
62
|
+
| N-01 | Performance | p95 page load | < 2s on 4G |
|
|
63
|
+
| N-02 | Security | auth + data-at-rest | OWASP ASVS L1; encrypted at rest |
|
|
64
|
+
| N-03 | Accessibility | WCAG conformance | AA |
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### `roadmap.md` (the spine of the review)
|
|
68
|
+
|
|
69
|
+
```markdown
|
|
70
|
+
---
|
|
71
|
+
id: EP-discovery
|
|
72
|
+
artifact: roadmap
|
|
73
|
+
status: draft
|
|
74
|
+
owner:
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Summary
|
|
78
|
+
<!-- the product thesis in 2–3 lines; links to market-research, competitor-analysis,
|
|
79
|
+
current-state, feasibility, requirements -->
|
|
80
|
+
|
|
81
|
+
## Phase 1 — MVP
|
|
82
|
+
<!-- the smallest valuable slice; the features here are built first -->
|
|
83
|
+
|
|
84
|
+
| Feature | Proposed epic id | Requirements | Status |
|
|
85
|
+
|---------|------------------|--------------|--------|
|
|
86
|
+
| Registration | EP-registration | F-01 | planned |
|
|
87
|
+
| Login | EP-login | F-02 | planned |
|
|
88
|
+
|
|
89
|
+
## Phase 2 — <name>
|
|
90
|
+
| Feature | Proposed epic id | Requirements | Status |
|
|
91
|
+
|---------|------------------|--------------|--------|
|
|
92
|
+
| … | EP-… | F-… | planned |
|
|
93
|
+
|
|
94
|
+
## Later / parked
|
|
95
|
+
<!-- explicitly deferred, with why -->
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Per-feature `status:` lifecycle (set by hand): `planned` → `epic-started` (a feature epic has been
|
|
99
|
+
seeded with `yad-epic`) → `shipped`. The proposed `EP-<slug>` ids are suggestions for the eventual
|
|
100
|
+
`yad-epic` runs — `yad-epic` still assigns the id (and skips the reserved `EP-discovery`).
|
|
101
|
+
|
|
102
|
+
The other four artifacts (`market-research.md`, `competitor-analysis.md`, `current-state.md`,
|
|
103
|
+
`feasibility.md`) are free-form Markdown with a `--- id / artifact / status / owner ---` frontmatter
|
|
104
|
+
block matching the two above. `competitor-analysis.md` is required in BOTH greenfield and brownfield;
|
|
105
|
+
`current-state.md` is the substantive code-aware study in brownfield and a short clean-slate note in
|
|
106
|
+
greenfield.
|
|
@@ -32,6 +32,18 @@ and never gates.
|
|
|
32
32
|
| `yad-connect-learning` | `learning.json` |
|
|
33
33
|
| `yad-connect-docs` | `docs.json` |
|
|
34
34
|
|
|
35
|
+
### Path: Front-zero (`phase: 0-front`)
|
|
36
|
+
The OPTIONAL once-per-project discovery phase, modelled as the reserved "epic zero" `EP-discovery`.
|
|
37
|
+
Greenfield AND brownfield; a 2-step author→review chain whose review binds to the whole artifact set
|
|
38
|
+
and terminates at `discovery-done` (no build half).
|
|
39
|
+
|
|
40
|
+
| Step (skill) | Gate | Outputs / sideEffects |
|
|
41
|
+
|--------------|------|------------------------|
|
|
42
|
+
| `yad-discovery` *(optional)* | → `discovery-review` (base rule) | `market-research.md`, `competitor-analysis.md`, `current-state.md`, `feasibility.md`, `requirements.md`, `roadmap.md`; seeds `EP-discovery/.sdlc/state.json` |
|
|
43
|
+
|
|
44
|
+
`roadmap.md` is the menu of features each `yad-epic` reads (Step 2c) — reference-only, never
|
|
45
|
+
auto-seeds epics.
|
|
46
|
+
|
|
35
47
|
### Path: Front half (`phase: 1-front`)
|
|
36
48
|
The gated authoring chain + the reusable review gate (10 steps, or 12 with the optional analysis).
|
|
37
49
|
|
|
@@ -85,8 +97,8 @@ The eight yadflow lenses, each to its relevant phase sections + paths:
|
|
|
85
97
|
|
|
86
98
|
| Lens | Relevant phases / sections |
|
|
87
99
|
|------|----------------------------|
|
|
88
|
-
| analyst | Setup intent, analysis step, front-half discovery |
|
|
89
|
-
| pm | epic, stories
|
|
100
|
+
| analyst | Setup intent, project discovery (front-zero), analysis step, front-half discovery |
|
|
101
|
+
| pm | project discovery (market/feasibility/roadmap), epic, stories; the front gates |
|
|
90
102
|
| architect | architecture + the locked contract; escalation |
|
|
91
103
|
| ux | UI design, design tool connection, the design system |
|
|
92
104
|
| dev | build half: spec → implement, the per-repo loop |
|
package/skills/yad-epic/SKILL.md
CHANGED
|
@@ -68,10 +68,23 @@ and let it inform which `repos` the epic should touch.
|
|
|
68
68
|
- For depth on a specific area not in the map, do a live on-demand read (see `yad-connect-repos`
|
|
69
69
|
`references/code-context.md`) — do not block on it.
|
|
70
70
|
|
|
71
|
+
### Step 2c — Read the project roadmap (project context, only once discovery is APPROVED)
|
|
72
|
+
Consume the project front-zero (`yad-discovery`) **only after its review gate has passed** — never a
|
|
73
|
+
draft or in-review roadmap (that would bypass `discovery-review`). Gate on the **state**, not file
|
|
74
|
+
existence: read `{project-root}/epics/EP-discovery/.sdlc/state.json` and proceed **only when
|
|
75
|
+
`currentStep == "discovery-done"`**. When it is, read its `roadmap.md` and sibling `requirements.md`
|
|
76
|
+
for the project framing: which phase (MVP / later) this feature belongs to, the functional/non-functional
|
|
77
|
+
requirements it carries, and how it fits the wider plan — so the epic's **Goal / Scope / Acceptance
|
|
78
|
+
signals** stay consistent with the approved roadmap. **Optional & non-blocking:** if there is no
|
|
79
|
+
discovery, or it has not yet reached `discovery-done` (absent / still draft / in-review), proceed
|
|
80
|
+
unchanged — do not consume an unapproved roadmap. After seeding the epic, the matching roadmap row's
|
|
81
|
+
`status:` can be bumped `planned → epic-started` by hand (a human edit; discovery never auto-seeds epics).
|
|
82
|
+
|
|
71
83
|
### Step 3 — Generate the Epic ID (engine-assigned, never by hand) — analysis-skipped only
|
|
72
84
|
*(Skip when analysis ran — the ID was already assigned by `yad-analysis`.)*
|
|
73
85
|
Derive `EP-<slug>` where `slug` is **2–4 lowercase words joined by hyphens**, drawn from the idea
|
|
74
|
-
(e.g. `EP-istifta-inquiries`). Lowercase except the fixed `EP` prefix.
|
|
86
|
+
(e.g. `EP-istifta-inquiries`). Lowercase except the fixed `EP` prefix. `EP-discovery` is **reserved**
|
|
87
|
+
for the project front-zero — never use it for a feature. **The ID is assigned once and
|
|
75
88
|
never renamed** — renaming breaks every downstream link (build plan §6b).
|
|
76
89
|
Check `{project-root}/epics/` for collisions; if the slug exists, append a distinguishing word.
|
|
77
90
|
|