yadflow 2.1.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,15 +1,16 @@
1
- # [2.1.0](https://github.com/abdelrahmannasr/yadflow/compare/v2.0.1...v2.1.0) (2026-06-13)
1
+ # [2.2.0](https://github.com/abdelrahmannasr/yadflow/compare/v2.1.0...v2.2.0) (2026-06-13)
2
2
 
3
3
 
4
4
  ### Bug Fixes
5
5
 
6
- * address CodeRabbit review on the hardening PR ([7dbe9e3](https://github.com/abdelrahmannasr/yadflow/commit/7dbe9e358e731d69ecead6ecac9faa8377c37023))
7
- * drop useless backtick escapes in a single-quoted doctor hint (lint) ([c0cf1a2](https://github.com/abdelrahmannasr/yadflow/commit/c0cf1a26c15fccc91df15013d6bac83892f2af25))
6
+ * address code review on the design-tool connection ([1275146](https://github.com/abdelrahmannasr/yadflow/commit/1275146e9e24251d481d90eb26894a729e894997))
8
7
 
9
8
 
10
9
  ### Features
11
10
 
12
- * yad doctor + structured YAD-* error codes with recovery hints ([94f9e9f](https://github.com/abdelrahmannasr/yadflow/commit/94f9e9f6ff6d6d3c83ed29f1cfcc97e32678615c))
11
+ * add yad-connect-design skill (Figma-first, pluggable) ([d1de46d](https://github.com/abdelrahmannasr/yadflow/commit/d1de46d2b08995db0228e60a58877649649b5cd0))
12
+ * materialize the feature design in yad-ui (generate or link) ([e440571](https://github.com/abdelrahmannasr/yadflow/commit/e44057131e86fed45d9a9c4151cd762eb98f24e8))
13
+ * wire the design-tool connection through the CLI ([1867ed4](https://github.com/abdelrahmannasr/yadflow/commit/1867ed433dd57025ae4143f5a236db32f1a99c41))
13
14
 
14
15
  # [1.1.0](https://github.com/abdelrahmannasr/sdlc-workflow/compare/v1.0.3...v1.1.0) (2026-06-09)
15
16
 
package/README.md CHANGED
@@ -122,16 +122,18 @@ a manual `yad gate sync` racing CI, or GitLab pipelines — two simultaneous syn
122
122
  *commits* via the rebase retry but each works from the state it read at start, so the rarer of two
123
123
  simultaneous advancements can be lost; the next event or scheduled sweep re-syncs and converges.
124
124
 
125
- ### What `setup` walks you through (7 steps)
125
+ ### What `setup` walks you through (8 steps)
126
126
 
127
127
  1. **Preflight** — confirm the hub is a git repo (offers `git init`); check `git`/`node`/`npx`.
128
- 2. **Install the module** — copy all 17 `yad-*` skills into the IDE skill dirs you pick
128
+ 2. **Install the module** — copy all 18 `yad-*` skills into the IDE skill dirs you pick
129
129
  (`.claude/`, `.agents/`, `.zencoder/`, `.opencode/`) and register `_bmad/sdlc/`.
130
130
  3. **Hub platform & roster** — detect GitHub/GitLab from the remote; record reviewers → `.sdlc/hub.json`.
131
- 4. **Connect code repos** — register each repo into `.sdlc/repos.json` and cache a Repomix pack.
132
- 5. **Wire each repo** CI gates, PR/MR template, and review-comment scaffold.
133
- 6. **AI review** — optionally write `.coderabbit.yaml`.
134
- 7. **Done** — stamp `.sdlc/cli-version.json` and hand off the AI-only steps (code-maps; first epic).
131
+ 4. **Connect a design tool** — record the design tool (Figma / pencil / none) → `.sdlc/design.json` so
132
+ the UI step can materialize the design; the MCP itself is confirmed later by `yad-connect-design`.
133
+ 5. **Connect code repos** — register each repo into `.sdlc/repos.json` and cache a Repomix pack.
134
+ 6. **Wire each repo** — CI gates, PR/MR template, and review-comment scaffold.
135
+ 7. **AI review** — optionally write `.coderabbit.yaml`.
136
+ 8. **Done** — stamp `.sdlc/cli-version.json` and hand off the AI-only steps (code-maps; first epic).
135
137
 
136
138
  The deterministic file work runs automatically; the AI-only steps are handed to the Claude Code skills
137
139
  with a printed next-action. Re-run `… check --fix` any time the workflow updates — it never re-asks for
@@ -167,10 +169,11 @@ with a fix-it hint per finding. Failures carry stable, greppable codes, also pri
167
169
  | `YAD-STATE-002` | a ledger/config file parses but has the wrong shape | fix the file or restore from git (the message names the field) |
168
170
  | `YAD-STATE-003` | a registered repo path is missing or not a git repo | fix the path in `.sdlc/repos.json` or re-connect the repo |
169
171
  | `YAD-CFG-001` | `hub.json` names an unknown platform | expected `github`, `gitlab`, or `null` — fix it or re-run `yad setup` |
172
+ | `YAD-CFG-002` | `design.json` names an unknown design tool | expected one of `config.yaml` `design.tools` (e.g. `figma`, `pencil`), or `none` — fix it or re-run `yad setup` |
170
173
 
171
174
  Filing a bug? Attach `yad doctor --json` — it contains no secrets (names, paths, and check results only).
172
175
 
173
- ## Agent skills (all 17)
176
+ ## Agent skills (all 18)
174
177
 
175
178
  The CLI **installs and wires** the module; the skills below are the **agents you invoke by name** in your
176
179
  AI IDE (e.g. *“run `yad-epic`”*) to actually do the work. State lives in files you can also edit
@@ -183,6 +186,11 @@ directly. Each skill stops at a gate and never auto-advances unless a step has *
183
186
  `.sdlc/repos.json`, then caches an AI-readable picture of each — a compressed Repomix pack and a
184
187
  lightweight code-map (existing endpoints/events/data-models/modules), secret-scanned. Idempotent and
185
188
  refreshable; staleness tracked by HEAD sha.
189
+ - **`yad-connect-design`** — Connects a design tool (Figma-first, pluggable) so the UI step can
190
+ materialize the actual feature design (mobile screens / web pages) inside it, alongside the Markdown.
191
+ Records the tool + project/file references in `.sdlc/design.json` (local-user / MCP-session auth, no
192
+ stored tokens), detecting the design-tool MCP and degrading to markdown-only when absent. Idempotent
193
+ and refreshable; one connection per project.
186
194
 
187
195
  ### Front half — author the "thinking" (once per epic, human-gated)
188
196
 
@@ -197,7 +205,9 @@ directly. Each skill stops at a gate and never auto-advances unless a step has *
197
205
  `.sdlc/contract-lock.json`. Reads `epic.md`; escalates on the contract risk tag.
198
206
  - **`yad-ui`** — Front state 5. With the ux-designer, author `ui-design.md` and `DESIGN.md`,
199
207
  driving Impeccable as harness slash-commands (document/extract/craft) when installed, or authoring
200
- directly when not. Reads epic + architecture.
208
+ directly when not. When a design tool is connected (`yad-connect-design`), also **materializes the
209
+ feature design** — mobile screens / web pages — in the tool (generate or link), recording the
210
+ screen→frame map in `design-links.json`; degrades to markdown-only otherwise. Reads epic + architecture.
201
211
  - **`yad-stories`** — Front state 7. With the pm, break the approved epic into user stories, each
202
212
  tagged with the repos that must implement it. Assigns zero-padded `EP-<slug>-S0N` IDs, one file per
203
213
  story under `stories/`. Reads epic + architecture + contract + UI.
package/cli/doctor.mjs CHANGED
@@ -5,7 +5,7 @@
5
5
  import path from 'node:path';
6
6
  import fs from 'node:fs';
7
7
  import { c, log, ok, info, warn, fail, hand, run, has, exists, readJSON, readJSONStrict } from './lib.mjs';
8
- import { VERSION, PROJECT_FILES } from './manifest.mjs';
8
+ import { VERSION, PROJECT_FILES, DESIGN_TOOLS } from './manifest.mjs';
9
9
  import { loadLedger, epicRoot } from './epic-state.mjs';
10
10
  import { gitHead } from './setup.mjs';
11
11
  import { cliFor } from './platform.mjs';
@@ -75,6 +75,26 @@ export function projectChecks(checks, root) {
75
75
  }
76
76
  }
77
77
 
78
+ // design.json: parse + shape + tool + MCP confirmation (absent is the normal markdown-only default —
79
+ // pre-feature projects have none, so silence rather than warn when the file does not exist).
80
+ const designPath = path.join(root, PROJECT_FILES.designConfig);
81
+ if (exists(designPath)) {
82
+ let design = null, designBroken = false;
83
+ try {
84
+ design = readJSONStrict(designPath, null);
85
+ } catch (e) {
86
+ designBroken = true;
87
+ check(checks, 'design', 'project', 'fail', `${PROJECT_FILES.designConfig} does not parse [${e.code || 'YAD-STATE-001'}]`, e.hint || 'fix the JSON or restore it from git');
88
+ }
89
+ if (designBroken) { /* reported above */ }
90
+ else if (typeof design !== 'object' || Array.isArray(design) || design === null) check(checks, 'design', 'project', 'fail', `${PROJECT_FILES.designConfig} has the wrong shape [YAD-STATE-002]`, 'expected a JSON object');
91
+ else if (![...DESIGN_TOOLS, 'none', null, undefined].includes(design.tool)) check(checks, 'design', 'project', 'fail', `${PROJECT_FILES.designConfig}: unknown design tool '${design.tool}' [YAD-CFG-002]`, `expected one of ${DESIGN_TOOLS.join(', ')}, or none`);
92
+ else if (!design.tool || design.tool === 'none') check(checks, 'design', 'project', 'ok', 'design: markdown-only');
93
+ else if (design.source && design.source !== 'unavailable') check(checks, 'design', 'project', 'ok', `design: ${design.tool} (${design.source})`);
94
+ else if (design.source === 'unavailable') check(checks, 'design', 'project', 'warn', `design: ${design.tool} MCP unavailable — yad-ui runs markdown-only`, 'connect the MCP, then run `yad-connect-design` (action: refresh)');
95
+ else check(checks, 'design', 'project', 'warn', `design: ${design.tool} recorded but the MCP is not confirmed`, 'run `yad-connect-design` in Claude Code to detect the MCP');
96
+ }
97
+
78
98
  // repos.json: parse + every entry is a live git repo; staleness vs syncedHead
79
99
  let registry = { repos: [] };
80
100
  let regBroken = false;
package/cli/errors.mjs CHANGED
@@ -20,6 +20,7 @@ export const CODES = {
20
20
  'YAD-STATE-002': 'a ledger/config JSON file parses but has the wrong shape',
21
21
  'YAD-STATE-003': 'a registered repo path is missing or not a git repository',
22
22
  'YAD-CFG-001': 'hub.json names an unknown platform (expected github, gitlab, or null)',
23
+ 'YAD-CFG-002': 'design.json names an unknown design tool (expected one of config.yaml design.tools, or none)',
23
24
  };
24
25
 
25
26
  export const err = (code, message, hint) => new YadError(code, message, hint);
package/cli/manifest.mjs CHANGED
@@ -10,7 +10,7 @@ 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 17 hand-authored yad-* skills (mirrors skills/sdlc/install.sh).
13
+ // The 18 hand-authored yad-* skills (mirrors skills/sdlc/install.sh).
14
14
  export const SKILLS = [
15
15
  'yad-analysis',
16
16
  'yad-epic',
@@ -18,6 +18,7 @@ export const SKILLS = [
18
18
  'yad-ui',
19
19
  'yad-stories',
20
20
  'yad-connect-repos',
21
+ 'yad-connect-design',
21
22
  'yad-spec',
22
23
  'yad-implement',
23
24
  'yad-checks',
@@ -79,10 +80,18 @@ export const IDE_OPENCODE_DIR = '.opencode/commands'; // <skill>.md (flat SKILL.
79
80
  // Module registration files copied from skills/sdlc/ into _bmad/sdlc/.
80
81
  export const MODULE_FILES = ['config.yaml', 'module-help.csv'];
81
82
 
83
+ // Supported design-tool adapters (mirrors skills/sdlc/config.yaml `design.tools`); `DESIGN_PRIMARY` is
84
+ // the fallback `registerDesign`/setup use when an unknown tool is named, and `none` is the explicit
85
+ // markdown-only choice. (doctor does NOT fall back — an unknown tool there is a hard YAD-CFG-002 fail,
86
+ // mirroring how registerRepo falls back on platform while doctor fails on an unknown hub platform.)
87
+ export const DESIGN_TOOLS = ['figma', 'pencil'];
88
+ export const DESIGN_PRIMARY = 'figma';
89
+
82
90
  // Project-level files setup produces (used by `check` to spot missing setup).
83
91
  export const PROJECT_FILES = {
84
92
  reposRegistry: '.sdlc/repos.json',
85
93
  hubConfig: '.sdlc/hub.json',
94
+ designConfig: '.sdlc/design.json',
86
95
  version: '.sdlc/cli-version.json',
87
96
  };
88
97
 
package/cli/setup.mjs CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  c, log, step, ok, info, warn, hand, fail, ask, askYesNo, run, has,
6
6
  exists, readJSON, writeJSON,
7
7
  } from './lib.mjs';
8
- import { VERSION, IDE_FOLDER_TARGETS, PROJECT_FILES } from './manifest.mjs';
8
+ import { VERSION, IDE_FOLDER_TARGETS, PROJECT_FILES, DESIGN_TOOLS, DESIGN_PRIMARY } from './manifest.mjs';
9
9
  import { moduleActions, repoActions, hubActions, authorsActions } from './plan.mjs';
10
10
 
11
11
  const ALL_IDES = [...IDE_FOLDER_TARGETS, '.opencode'];
@@ -58,6 +58,35 @@ export function registerRepo(root, registry, { name, rpath, platform, domain_own
58
58
  return repo;
59
59
  }
60
60
 
61
+ // Record the project's design-tool connection into .sdlc/design.json (the deterministic half of the
62
+ // connect loop; MCP detection itself is an AI step, handed off to `yad-connect-design`). An unknown tool
63
+ // falls back to the primary adapter rather than being rejected — mirrors registerRepo's platform
64
+ // fallback and the hub step. `none` is the explicit markdown-only choice.
65
+ export function registerDesign(root, { tool, project_url = null, files = null, today = null } = {}) {
66
+ // Idempotent re-connect: carry the original first-connect date forward (the schema defines
67
+ // connectedAt as "first connect"); only lastSyncedAt moves. Mirrors repo.mjs refresh.
68
+ const designPath = path.join(root, PROJECT_FILES.designConfig);
69
+ const prev = readJSON(designPath, null);
70
+ const connectedAt = prev && prev.connectedAt ? prev.connectedAt : today;
71
+ let t = (tool || '').toLowerCase();
72
+ if (t === 'none' || t === '') {
73
+ const off = { tool: 'none', provider: null, project_url: null, auth: 'user',
74
+ files: { web: null, mobile: null }, connectedAt, lastSyncedAt: today, source: 'unavailable' };
75
+ writeJSON(designPath, off);
76
+ return off;
77
+ }
78
+ if (!DESIGN_TOOLS.includes(t)) { warn(`unknown design tool '${tool}' — using ${DESIGN_PRIMARY}`); t = DESIGN_PRIMARY; }
79
+ // source stays null until `yad-connect-design` detects the MCP in the harness (AI step). doctor reports
80
+ // a recorded-but-unconfirmed connection as a warning pointing at that skill.
81
+ const design = {
82
+ tool: t, provider: null, project_url: project_url || null, auth: 'user',
83
+ files: files || { web: null, mobile: null },
84
+ connectedAt, lastSyncedAt: today, source: null,
85
+ };
86
+ writeJSON(designPath, design);
87
+ return design;
88
+ }
89
+
61
90
  function applyActions(actions, { force = false } = {}) {
62
91
  let changed = 0;
63
92
  for (const a of actions) {
@@ -71,7 +100,7 @@ function applyActions(actions, { force = false } = {}) {
71
100
  }
72
101
 
73
102
  export async function runSetup(root, opts = {}) {
74
- const total = 7;
103
+ const total = 8;
75
104
  log(c.bold(`\nSDLC Workflow setup ${c.dim('v' + VERSION)}`));
76
105
  log(c.dim(`target: ${root}`));
77
106
 
@@ -131,8 +160,26 @@ export async function runSetup(root, opts = {}) {
131
160
  ok(`wrote ${PROJECT_FILES.hubConfig} (${roster.length} reviewer(s))`);
132
161
  }
133
162
 
134
- // 4. Connect code repos
135
- step(4, total, 'Connect code repos');
163
+ // 4. Connect a design tool (Figma-first, pluggable; the UI step materializes the design here)
164
+ step(4, total, 'Connect a design tool (Figma / pencil / none)');
165
+ const designPath = path.join(root, PROJECT_FILES.designConfig);
166
+ if (exists(designPath) && !(await askYesNo('design.json exists — reconfigure?', false))) {
167
+ info('keeping existing .sdlc/design.json');
168
+ } else {
169
+ let tool = (await ask(`Design tool (${DESIGN_TOOLS.join('/')}/none)`, DESIGN_PRIMARY)).toLowerCase();
170
+ if (![...DESIGN_TOOLS, 'none'].includes(tool)) {
171
+ warn(`unknown design tool '${tool}' — using ${DESIGN_PRIMARY}`);
172
+ tool = DESIGN_PRIMARY;
173
+ }
174
+ const project_url = tool === 'none' ? null : (await ask(' project/file URL (blank to set later)', '')) || null;
175
+ registerDesign(root, { tool, project_url, today: opts.today ?? null });
176
+ ok(tool === 'none'
177
+ ? `wrote ${PROJECT_FILES.designConfig} (markdown-only)`
178
+ : `wrote ${PROJECT_FILES.designConfig} (${tool})`);
179
+ }
180
+
181
+ // 5. Connect code repos
182
+ step(5, total, 'Connect code repos');
136
183
  const regPath = path.join(root, PROJECT_FILES.reposRegistry);
137
184
  const registry = readJSON(regPath, { repos: [] });
138
185
  const known = new Set(registry.repos.map((r) => r.name));
@@ -155,8 +202,8 @@ export async function runSetup(root, opts = {}) {
155
202
  }
156
203
  }
157
204
 
158
- // 5. Wire each connected repo + the hub itself
159
- step(5, total, 'Wire connected repos + the hub (CI gates, PR template, comment scaffold, gate-sync)');
205
+ // 6. Wire each connected repo + the hub itself
206
+ step(6, total, 'Wire connected repos + the hub (CI gates, PR template, comment scaffold, gate-sync)');
160
207
  if (registry.repos.length === 0) info('no repos to wire');
161
208
  for (const repo of registry.repos) {
162
209
  log(` ${c.bold(repo.name)} ${c.dim(`(${repo.platform})`)}`);
@@ -171,8 +218,8 @@ export async function runSetup(root, opts = {}) {
171
218
  // author allowlists for the verified-commits gate (hub + every repo), from the roster emails
172
219
  applyActions(authorsActions(root, registry.repos), { force: true });
173
220
 
174
- // 6. Optional CodeRabbit
175
- step(6, total, 'AI review (CodeRabbit)');
221
+ // 7. Optional CodeRabbit
222
+ step(7, total, 'AI review (CodeRabbit)');
176
223
  for (const repo of registry.repos) {
177
224
  const cr = path.join(path.resolve(root, repo.path), '.coderabbit.yaml');
178
225
  if (exists(cr)) { info(`${repo.name}: .coderabbit.yaml present`); continue; }
@@ -182,13 +229,17 @@ export async function runSetup(root, opts = {}) {
182
229
  }
183
230
  }
184
231
 
185
- // 7. Summary + version stamp
186
- step(7, total, 'Done');
232
+ // 8. Summary + version stamp
233
+ step(8, total, 'Done');
187
234
  writeJSON(path.join(root, PROJECT_FILES.version), { version: VERSION, ideTargets, updatedAt: opts.today ?? null });
188
235
  ok(`stamped ${PROJECT_FILES.version} (v${VERSION})`);
189
236
  log('');
190
237
  log(c.bold('Next — AI-only steps (run in Claude Code):'));
191
238
  hand('generate code-maps: run `yad-connect-repos` for each connected repo');
239
+ const design = readJSON(designPath, null);
240
+ if (design && design.tool && design.tool !== 'none') {
241
+ hand(`confirm the design tool: run \`yad-connect-design\` to detect the ${design.tool} MCP (or it degrades to markdown-only)`);
242
+ }
192
243
  hand('author your first epic: run `yad-epic`');
193
244
  log('');
194
245
  log(c.dim('Re-run anytime: `yad check` (report) / `yad check --fix` (reconcile).'));
package/docs/index.html CHANGED
@@ -284,12 +284,14 @@
284
284
  <div class="lane">
285
285
  <div class="lane-title">0 · One-time setup (team lead, per project)</div>
286
286
  <div class="flow-v">
287
- <div class="node plain"><strong>Install the module</strong><small><code>npx yadflow setup</code> — copies the 17 skills into your IDE dirs</small></div>
287
+ <div class="node plain"><strong>Install the module</strong><small><code>npx yadflow setup</code> — copies the 18 skills into your IDE dirs</small></div>
288
288
  <div class="arrow-v">▼</div>
289
289
  <div class="node plain"><strong>Wire each repo</strong><small><code>yad-checks</code> · <code>yad-pr-template</code> · <code>yad-review-comments</code> (CI gates, PR template, comment scaffold)</small></div>
290
290
  <div class="arrow-v">▼</div>
291
291
  <div class="node plain"><strong>Connect code repos</strong><small><code>yad-connect-repos</code> → <code>repos.json</code> + a cached code-map per repo <em>(skip if greenfield)</em></small></div>
292
292
  <div class="arrow-v">▼</div>
293
+ <div class="node plain"><strong>Connect a design tool</strong><small><code>yad-connect-design</code> → <code>design.json</code> (Figma-first, pluggable) so <code>yad-ui</code> can materialize the screens <em>(optional — degrades to markdown-only)</em></small></div>
294
+ <div class="arrow-v">▼</div>
293
295
  <div class="node plain"><strong>Optional: hub on a platform</strong><small>detect GitHub/GitLab + reviewer roster, so reviews run on real PRs/MRs</small></div>
294
296
  </div>
295
297
  </div>
@@ -311,7 +313,7 @@
311
313
  <div class="arrow-v">▼</div>
312
314
  <div class="node gate">gate · architecture review<small><strong>escalated:</strong> base rule + a domain owner for every repo in the epic</small></div>
313
315
  <div class="arrow-v">▼</div>
314
- <div class="node artifact"><strong>yad-ui</strong><small><code>ui-design.md</code> + <code>DESIGN.md</code></small></div>
316
+ <div class="node artifact"><strong>yad-ui</strong><small><code>ui-design.md</code> + <code>DESIGN.md</code> + the screens in the connected design tool (<code>design-links.json</code>)</small></div>
315
317
  <div class="arrow-v">▼</div>
316
318
  <div class="node gate">gate · UI review<small>base rule</small></div>
317
319
  <div class="arrow-v">▼</div>
@@ -367,11 +369,11 @@
367
369
  epic.md the feature, agreed
368
370
  architecture.md how it will be built
369
371
  contract.md the LOCKED shared surface (APIs, events, data model)
370
- ui-design.md + DESIGN.md
372
+ ui-design.md + DESIGN.md (+ the screens in the connected design tool)
371
373
  stories/ one file per story (EP-&lt;slug&gt;-S01.md …)
372
374
  reviews/ reviewer comments, per artifact
373
375
  .sdlc/ state.json · approvals.json · contract-lock.json
374
- build-state/ · trust-log.json · build-log.json
376
+ design-links.json · build-state/ · trust-log.json · build-log.json
375
377
 
376
378
  each code repo/
377
379
  specs/&lt;story-id&gt;/ spec, plan, tasks (+ link.md back to the story)
@@ -438,7 +440,7 @@ each code repo/
438
440
  <div class="term"><strong>State machine</strong><span class="badge b-core">core</span><br>The core design: the workflow is a fixed set of <em>states</em> (steps). Each state does its work and waits at a gate. The states never change — only the <em>trigger</em> (who moves work forward) changes. That is what lets the team start fully manual and automate gradually without rebuilding anything.</div>
439
441
  <div class="term"><strong>Product hub (product repo)</strong><span class="badge b-core">core</span><br>The git repo that holds the shared “thinking”: epics, architecture, the contract, UI design, stories, reviews and all state. The brain of the project. Code never lives here.</div>
440
442
  <div class="term"><strong>Code repo</strong><span class="badge b-core">core</span><br>A separate git repo holding real application code (e.g. backend, mobile, dashboard). Each story’s spec lives inside the code repo it belongs to, with a link back to the story in the hub.</div>
441
- <div class="term"><strong>Skills source (this repo)</strong><span class="badge b-core">core</span><br>The <code>yadflow</code> repo itself — where the 17 skills live and where you pull updates from. No real product work happens inside it.</div>
443
+ <div class="term"><strong>Skills source (this repo)</strong><span class="badge b-core">core</span><br>The <code>yadflow</code> repo itself — where the 18 skills live and where you pull updates from. No real product work happens inside it.</div>
442
444
  <div class="term"><strong>Front half (“decide” / the brain)</strong><span class="badge b-core">core</span><br>The first half of the workflow, run once per epic in the product hub: analysis (optional) → epic → architecture + contract → UI design → stories — each followed by a human review gate. Permanently human-approved.</div>
443
445
  <div class="term"><strong>Back half / build half (“build”)</strong><span class="badge b-core">core</span><br>The second half, run once per story per code repo, inside that repo: spec → implement → checks → PR → ship. These mechanical steps may earn automation over time.</div>
444
446
  <div class="term"><strong>Gate</strong><span class="badge b-core">core</span><br>A stopping point after a step. The step writes its output and <em>waits</em>; a human must approve before the workflow moves on. “Every step stops at a gate” is the heart of the whole system.</div>
@@ -529,7 +531,7 @@ each code repo/
529
531
  <div class="term"><strong>CodeRabbit</strong><span class="badge b-tool">tools</span><br>An AI code-review service used as the advisory first pass on PRs. Optional; never the merge authority.</div>
530
532
  <div class="term"><strong>Graceful degradation</strong><span class="badge b-tool">tools</span><br>The rule that every optional tool (Spec Kit, Impeccable, Repomix, CodeRabbit) can be absent: the workflow falls back to doing the work directly and <em>records</em> that the tool was missing. You can start with none of them.</div>
531
533
  <div class="term"><strong>Tool-agnostic rule</strong><span class="badge b-tool">tools</span><br>Talk to every tool through its commands and files, never through its internal code — so each tool stays swappable.</div>
532
- <div class="term"><strong>Skill</strong><span class="badge b-tool">tools</span><br>One of the 17 named agents (e.g. <code>yad-epic</code>) you invoke by name in your AI IDE. Each skill does one step’s work, writes files, and stops at its gate.</div>
534
+ <div class="term"><strong>Skill</strong><span class="badge b-tool">tools</span><br>One of the 18 named agents (e.g. <code>yad-epic</code>) you invoke by name in your AI IDE. Each skill does one step’s work, writes files, and stops at its gate.</div>
533
535
  <div class="term"><strong>The <code>yad</code> CLI</strong><span class="badge b-tool">tools</span><br>A zero-dependency command-line tool (npm: <code>yadflow</code>) that installs, wires and reconciles the workflow (<code>setup</code>, <code>check --fix</code>, <code>update</code>) and runs the deterministic pieces (<code>gate</code>, <code>commit</code>, <code>open-pr</code>, <code>repo</code>).</div>
534
536
 
535
537
  <h3>People &amp; AI roles</h3>
@@ -672,7 +674,7 @@ each code repo/
672
674
 
673
675
  <!-- ======================================================= -->
674
676
  <section id="skills">
675
- <h2>6. The 17 skills — what each does, how to use it, and when</h2>
677
+ <h2>6. The 18 skills — what each does, how to use it, and when</h2>
676
678
  <p>A <strong>skill</strong> is an agent you invoke <em>by name</em> in your AI IDE — you just ask in
677
679
  plain words, e.g. <em>“run <code>yad-epic</code>”</em>, adding any inputs the skill needs
678
680
  (<code>repo: backend</code>, <code>story: EP-…-S01</code>, <code>action: wire</code>, …).
@@ -682,10 +684,11 @@ each code repo/
682
684
  <table>
683
685
  <tr><th>Skill</th><th>Half</th><th>One line</th></tr>
684
686
  <tr><td><a href="#sk-connect"><code>yad-connect-repos</code></a></td><td>Setup</td><td>Register a code repo with the hub + cache its code-map.</td></tr>
687
+ <tr><td><a href="#sk-connect-design"><code>yad-connect-design</code></a></td><td>Setup</td><td>Connect a design tool (Figma-first) so <code>yad-ui</code> can materialize the screens.</td></tr>
685
688
  <tr><td><a href="#sk-analysis"><code>yad-analysis</code></a></td><td>Front</td><td><em>(Optional)</em> pressure-test an idea into <code>analysis.md</code>.</td></tr>
686
689
  <tr><td><a href="#sk-epic"><code>yad-epic</code></a></td><td>Front</td><td>Start a feature: write <code>epic.md</code>, assign the ID.</td></tr>
687
690
  <tr><td><a href="#sk-arch"><code>yad-architecture</code></a></td><td>Front</td><td>Author <code>architecture.md</code> + the locked <code>contract.md</code>.</td></tr>
688
- <tr><td><a href="#sk-ui"><code>yad-ui</code></a></td><td>Front</td><td>Author <code>ui-design.md</code> + <code>DESIGN.md</code>.</td></tr>
691
+ <tr><td><a href="#sk-ui"><code>yad-ui</code></a></td><td>Front</td><td>Author <code>ui-design.md</code> + <code>DESIGN.md</code>; materialize the screens in the connected design tool.</td></tr>
689
692
  <tr><td><a href="#sk-stories"><code>yad-stories</code></a></td><td>Front</td><td>Break the epic into repo-tagged stories.</td></tr>
690
693
  <tr><td><a href="#sk-gate"><code>yad-review-gate</code></a></td><td>Cross-cutting</td><td>Review / comment / approve / advance <strong>any</strong> gate.</td></tr>
691
694
  <tr><td><a href="#sk-bridge"><code>yad-hub-bridge</code></a></td><td>Cross-cutting</td><td>Run front-half reviews over real PRs/MRs and sync them back.</td></tr>
@@ -715,6 +718,17 @@ yad-connect-repos action: roster login: alice-gh name: alice role: owner # onc
715
718
  <div class="row"><span class="lbl lbl-when">When</span> During one-time setup, and again any time you add a new code repo. Re-run <code>refresh</code> when a skill warns a repo is stale. <strong>Greenfield with no code yet? Skip it entirely</strong> — the brain proceeds without it.</div>
716
719
  </div>
717
720
 
721
+ <div class="skillcard" id="sk-connect-design">
722
+ <h4><code>yad-connect-design</code></h4>
723
+ <div class="row"><span class="lbl lbl-what">What</span> Connects a <strong>design tool</strong> to the product hub so the UI step can materialize the <em>actual</em> feature design — mobile screens / web pages — inside it, alongside the Markdown. <strong>Figma-first but pluggable</strong> (a design-tool adapter; <code>pencil</code> is a second provider). It records the tool + project/file references in <code>design.json</code> through your own authenticated MCP session — <strong>no tokens are ever stored</strong> — and degrades to markdown-only when no design-tool MCP is available.</div>
724
+ <div class="row"><span class="lbl lbl-how">How</span> Run it in the product hub, once per project:</div>
725
+ <pre><code>yad-connect-design action: connect tool: figma project_url: https://figma.com/files/project/…
726
+ yad-connect-design action: list # show the connection + whether the MCP is available
727
+ yad-connect-design action: refresh # re-detect the MCP (after authenticating a session)
728
+ yad-connect-design action: disconnect</code></pre>
729
+ <div class="row"><span class="lbl lbl-when">When</span> During one-time setup (the wizard records it), and again any time you switch design tools. <strong>No design tool? Skip it</strong> — <code>yad-ui</code> simply authors the Markdown, exactly as before.</div>
730
+ </div>
731
+
718
732
  <h3>Front half — author the thinking (in the product hub)</h3>
719
733
 
720
734
  <div class="skillcard" id="sk-analysis">
@@ -743,7 +757,7 @@ yad-connect-repos action: roster login: alice-gh name: alice role: owner # onc
743
757
 
744
758
  <div class="skillcard" id="sk-ui">
745
759
  <h4><code>yad-ui</code></h4>
746
- <div class="row"><span class="lbl lbl-what">What</span> With the ux-designer AI, authors the UI design for the epic: <code>ui-design.md</code> (this feature’s screens and flows) + <code>DESIGN.md</code> (the reusable design language). Drives Impeccable’s <code>document / extract / craft</code> commands when installed; writes the files directly when not (and records that).</div>
760
+ <div class="row"><span class="lbl lbl-what">What</span> With the ux-designer AI, authors the UI design for the epic: <code>ui-design.md</code> (this feature’s screens and flows) + <code>DESIGN.md</code> (the reusable design language). Drives Impeccable’s <code>document / extract / craft</code> commands when installed; writes the files directly when not (and records that). When a design tool is connected (<code>yad-connect-design</code>), it also <strong>materializes the screens in that tool</strong> — generating them from the spec or linking an existing design — and records the screen→frame map in <code>design-links.json</code>; otherwise it stays markdown-only.</div>
747
761
  <div class="row"><span class="lbl lbl-how">How</span> In the product hub, after the architecture gate passes:</div>
748
762
  <pre><code>run yad-ui — epic: EP-istifta-inquiries</code></pre>
749
763
  <div class="row"><span class="lbl lbl-when">When</span> After architecture, before stories. Skip-able only if the epic genuinely has no UI. Stops at the <strong>UI review gate</strong> (base rule).</div>
@@ -883,7 +897,7 @@ yad-status EP-istifta-inquiries # one epic in detail</code></pre>
883
897
 
884
898
  <div class="skillcard">
885
899
  <h4><code>npx yadflow setup</code></h4>
886
- <div class="row"><span class="lbl lbl-what">What</span> The guided first-run wizard. Seven steps: preflight (git/node check), install all 17 skills into the IDE dirs you pick, detect the hub’s platform + record the reviewer roster, connect your code repos, wire each one (CI gates, PR template, comment scaffold), optionally write the AI-review config, and stamp the installed version.</div>
900
+ <div class="row"><span class="lbl lbl-what">What</span> The guided first-run wizard. Eight steps: preflight (git/node check), install all 18 skills into the IDE dirs you pick, detect the hub’s platform + record the reviewer roster, connect a design tool (Figma-first; optional), connect your code repos, wire each one (CI gates, PR template, comment scaffold), optionally write the AI-review config, and stamp the installed version.</div>
887
901
  <div class="row"><span class="lbl lbl-how">How</span></div>
888
902
  <pre><code>cd &lt;product-hub-repo&gt;
889
903
  npx yadflow setup</code></pre>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yadflow",
3
- "version": "2.1.0",
3
+ "version": "2.2.0",
4
4
  "description": "Yadflow — the gated, team, multi-repo SDLC: author → review → build with a PR-driven review gate and a zero-dependency `yad` CLI (setup, gate, commit, open-pr, repo). A BMAD module + 17 yad-* skills.",
5
5
  "type": "module",
6
6
  "author": "AbdelRahman Nasr",
@@ -118,6 +118,22 @@ code_context:
118
118
  # works for both github and gitlab (and self-hosted), and stores NO tokens in the registry.
119
119
  platforms: [github, gitlab]
120
120
 
121
+ # Design tool (yad-connect-design) — the UI design step (yad-ui) materializes the actual feature design
122
+ # (mobile screens / web pages) inside a connected design tool, alongside ui-design.md + DESIGN.md. The
123
+ # tool is reached through its MCP (a harness MCP server, like Impeccable's slash-commands — NOT a CLI),
124
+ # detected per provider and DEGRADING to markdown-only when absent. Figma-first but PLUGGABLE: a
125
+ # design-tool adapter, like the github/gitlab platform adapter. Connection is one-per-project, recorded
126
+ # at setup; the per-epic screen->frame map (design-links.json) is written by yad-ui per epic.
127
+ design:
128
+ registry: "{project-root}/.sdlc/design.json" # project-wide design connection (NOT per-epic)
129
+ tools: [figma, pencil] # supported adapters; an unknown tool falls back to `primary`
130
+ primary: figma # the default/named provider
131
+ degrade: markdown-only # no tool / no MCP => yad-ui authors ui-design.md + DESIGN.md only
132
+ links: "{project-root}/epics/EP-<slug>/.sdlc/design-links.json" # per-epic screen->frame map (yad-ui)
133
+ # Auth: `yad-connect-design` connects through the LOCAL user's own authenticated MCP session and stores
134
+ # NO tokens in the registry (project_url/files are plain references, never credentials).
135
+ auth: user
136
+
121
137
  # Hub platform + front-half review bridge (yad-connect-repos `detect-hub`; yad-review-gate + yad-hub-bridge).
122
138
  # The product hub is itself a git repo on a platform. With the bridge enabled, the front-half review/
123
139
  # comment/approval cycle runs through a real PR/MR on the hub: a review PR is opened per artifact, reviewers
@@ -11,7 +11,7 @@ set -euo pipefail
11
11
  ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
12
12
  cd "$ROOT"
13
13
 
14
- SKILLS=(yad-analysis yad-epic yad-architecture yad-ui yad-stories yad-connect-repos yad-spec yad-implement yad-checks yad-pr-template yad-review-comments yad-hub-bridge yad-ship yad-backfill yad-run yad-review-gate yad-status)
14
+ SKILLS=(yad-analysis yad-epic yad-architecture yad-ui yad-stories yad-connect-repos yad-connect-design yad-spec yad-implement yad-checks yad-pr-template yad-review-comments yad-hub-bridge yad-ship yad-backfill yad-run yad-review-gate yad-status)
15
15
 
16
16
  echo "Installing sdlc module from $ROOT/skills ..."
17
17
 
@@ -5,6 +5,7 @@ SDLC Workflow,yad-architecture,Author Architecture,AA,"Front state 3: with the a
5
5
  SDLC Workflow,yad-ui,Author UI Design,AU,"Front state 5: with the ux-designer author ui-design.md and DESIGN.md, driving Impeccable slash-commands when installed. Never auto-advances.",,{epic: EP-<slug>},1-front,yad-review-gate,yad-review-gate,true,epics/EP-<slug>/,ui-design.md DESIGN.md state.json
6
6
  SDLC Workflow,yad-stories,Author Stories,AS,"Front state 7: with the pm break the epic into repo-tagged stories with stable EP-<slug>-S0N IDs, one file each under stories/. Never auto-advances.",,{epic: EP-<slug>},1-front,yad-review-gate,yad-review-gate,true,epics/EP-<slug>/stories/,stories/EP-<slug>-S0N.md state.json
7
7
  SDLC Workflow,yad-connect-repos,Connect Code Repos,CR,"Setup/maintenance: connect code repos to the product hub so the front/brain phases are code-aware. Registers each repo (GitHub or GitLab, local-user auth, no stored tokens) in .sdlc/repos.json and caches a Repomix pack + a lightweight code-map (existing endpoints/events/data-models/modules, secret-scanned). Idempotent and refreshable; staleness tracked by HEAD sha. Run at setup or any time a new repo is added. Not a gated state — never touches epic state or approvals.",,{action: connect|refresh|list|disconnect} {repo: <name>} {path: <path-or-absolute>} {git_url: <ssh-or-https>} {domain_owner: <who>},0-setup,,yad-epic,false,.sdlc/,repos.json code-context/<repo>/pack.md code-context/<repo>/code-map.md
8
+ SDLC Workflow,yad-connect-design,Connect Design Tool,CD,"Setup/maintenance: connect a design tool (Figma-first, pluggable) to the product hub so the UI design step can materialize the actual feature design (mobile screens / web pages) inside it, alongside ui-design.md + DESIGN.md. Records the tool + project/file references in .sdlc/design.json (local-user / MCP-session auth, no stored tokens), detecting whether a design-tool MCP is available and degrading to markdown-only when absent. Idempotent and refreshable; one connection per project. Not a gated state — never touches epic state or approvals.",,{action: connect|refresh|list|disconnect} {tool: figma|pencil|none} {project_url: <team/project/file url>} {files: {web,mobile}},0-setup,,yad-ui,false,.sdlc/,design.json
8
9
  SDLC Workflow,yad-spec,Author Spec,SP,"Build-half Step A: for one ready-for-build story and one of its repos, run the heavy Spec Kit ceremony once (specify→clarify→plan→analyze→checklist→tasks) inside that code repo, writing specs/<story-id>/ in Spec Kit's layout (drives /speckit.* when installed, else hand-authors and records speckit: not-installed). References the locked contract; never re-invents the surface. Writes link.md back to the story. Never auto-advances.",,{epic: EP-<slug>} {story: EP-<slug>-S0N} {repo: <one of story.repos>},3-build,yad-review-gate,,false,demo-repos/<repo>/specs/<story-id>/,spec.md research.md data-model.md contracts/ plan.md tasks.md link.md
9
10
  SDLC Workflow,yad-implement,Implement Task,IM,"Build-half Step B: with the dev lens, implement ONE atomic task from a story's tasks.md as a small diff (<=3 files) on its own branch feat/<story>-<task>-<slug> in the code repo. Diff stays inside the task's declared files (flag and STOP if it grows beyond them). Commit ends with a Task: <story>-<task> trailer; add Contract-Change: yes only when the locked contract surface is touched (routes back to the architecture gate). Never auto-advances.",,{epic: EP-<slug>} {story: EP-<slug>-S0N} {repo: <one of story.repos>} {task: T0N},3-build,yad-spec,,false,demo-repos/<repo>/,branch+commit per atomic task
10
11
  SDLC Workflow,yad-checks,Check Gates,CK,"Build-half Step C: wire and run the three production-safety CI gates on a code repo — spec-link (every change links a real story/spec via its Task trailer), contract-check (a contract-surface change without Contract-Change + an updated re-locked contract FAILS and routes back to the architecture gate), and build/test/lint. CI-agnostic bash invoked by GitHub Actions and GitLab CI. Blocking in CI; the human still owns the merge. Never auto-advances.",,{repo: <one of an epic's repos>} {action: wire|run} {base: target branch},3-build,yad-implement,,false,demo-repos/<repo>/,checks/*.sh .github/workflows/yad-checks.yml .gitlab-ci.yml
@@ -0,0 +1,117 @@
1
+ ---
2
+ name: yad-connect-design
3
+ description: 'Connects a design tool (Figma, or another tool — pluggable) to the product hub so the UI design step can materialize the full feature design (mobile screens / web pages) inside it, not just Markdown. Registers the tool into the project-wide .sdlc/design.json (local-user / MCP-session auth, no stored tokens), detecting whether a design-tool MCP is available and degrading to markdown-only when it is not. Run at setup or any time the design tool changes. Reusable, idempotent, refreshable. Use when the user says "connect Figma", "connect a design tool", "refresh the design connection", or "list the design connection".'
4
+ ---
5
+
6
+ # SDLC — Connect a Design Tool (make the UI step design-tool aware)
7
+
8
+ **Goal:** Let the UI design step (`yad-ui`) produce the **actual feature design** — the mobile screens
9
+ and/or web pages — inside a design tool such as **Figma**, alongside the Markdown artifacts
10
+ (`ui-design.md` / `DESIGN.md`). This skill **connects** a design tool to the product hub and records
11
+ *how* to reach it (the tool, the project/file references, which MCP renders 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 design registry. `yad-ui` consumes it: when a tool is
15
+ connected and its MCP is available, the `ux-designer` lens **generates** screens into the tool (or
16
+ **links** an existing human-made design and reads it back); when nothing is connected, `yad-ui` runs
17
+ markdown-only exactly as before.
18
+
19
+ ## Conventions
20
+
21
+ - `{project-root}` resolves from the project working directory (the **product hub**).
22
+ - The integration is **Figma-first but pluggable** (`config.yaml` `design.tools`): a design-tool
23
+ *adapter*, like the `github`/`gitlab` platform adapter. Figma is the primary provider; `pencil`
24
+ (the `.pen` web/mobile editor) is a second, write-capable provider; `none` → markdown-only.
25
+ - **The design tool is reached through its MCP** (a harness MCP server), NOT a subprocess CLI — the same
26
+ shape as Impeccable's slash-commands, 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/design.json` (project-wide, shared across all epics — NOT per-epic),
29
+ the sibling of `.sdlc/repos.json` and `.sdlc/hub.json`.
30
+ - Per-epic screen→frame links are written later by `yad-ui` (`epics/EP-<slug>/.sdlc/design-links.json`),
31
+ 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` — `figma` | `pencil` | another adapter id (`config.yaml` `design.tools`). `none` records a
38
+ deliberate markdown-only project.
39
+ - `project_url` — the design tool's team/project/file reference (e.g. a Figma project or file URL).
40
+ Optional — a connection with no file yet is valid; `yad-ui` can create one on first generate.
41
+ - `files` — optional default file mapping per platform (`{ web: <ref>, mobile: <ref> }`).
42
+
43
+ ## On Activation
44
+
45
+ ### Step 1 — Resolve the tool and its MCP (the design-tool adapter)
46
+ Determine which tool is being connected from `tool` (default `figma`); reject a `tool` not in
47
+ `config.yaml` `design.tools` (fall back to the configured `design.primary` with a warning, the same way
48
+ `registerRepo` falls back on an unknown platform). Then **detect the tool's MCP** in this harness:
49
+
50
+ - **figma** → a Figma MCP server (Dev Mode MCP for read/link; html.to.design for HTML→Figma *generate*).
51
+ - **pencil** → the `pencil` MCP (`batch_design` writes `.pen` web/mobile screens — generate-capable).
52
+ - another adapter → its named MCP.
53
+
54
+ Record `provider` (the concrete MCP, e.g. `figma-mcp` | `html-to-design` | `pencil-mcp`) and whether it
55
+ is available. **Auth is the local user's own** — the user's authenticated MCP session. The skill
56
+ **stores no tokens**; `project_url`/`files` are plain references, never credentials.
57
+
58
+ **Graceful degradation:** if no design-tool MCP is available, record `source: "unavailable"` and report
59
+ that `yad-ui` will run **markdown-only** until an MCP is connected (no error — the design tool is purely
60
+ additive, exactly like Impeccable being absent). Do **not** install an MCP server as part of this step.
61
+
62
+ ### Step 2 — Record the connection in the registry
63
+ Upsert into `{project-root}/.sdlc/design.json` (create the file + parent `.sdlc/` if absent):
64
+
65
+ ```json
66
+ {
67
+ "tool": "figma",
68
+ "provider": "figma-mcp",
69
+ "project_url": "https://www.figma.com/files/project/<id>/<name>",
70
+ "auth": "user",
71
+ "files": { "web": null, "mobile": null },
72
+ "connectedAt": "<YYYY-MM-DD>",
73
+ "lastSyncedAt": "<YYYY-MM-DD>",
74
+ "source": "figma-mcp"
75
+ }
76
+ ```
77
+
78
+ - `tool: "none"` records a deliberate markdown-only project: `{ "tool": "none", "provider": null,
79
+ "source": "unavailable", ... }`.
80
+ - `connect` is **idempotent** — re-running it overwrites the single connection in place (a project has
81
+ one design tool at a time; switching tools is just another `connect`).
82
+
83
+ ### Step 3 — Report (never auto-advance)
84
+ Report the connected `tool`, its `provider`, whether the MCP is available (or that `yad-ui` will degrade
85
+ to markdown-only), the `project_url`, and that **`yad-ui` will now generate/link the design here**.
86
+ Nothing auto-advances; this is setup.
87
+
88
+ ## Other actions
89
+
90
+ - **`refresh`** — re-detect the MCP and update `lastSyncedAt` (after the user authenticates a session or
91
+ changes tools). Same machinery as `connect`. Re-detection may flip `source` between an MCP id and
92
+ `unavailable` — report the change.
93
+ - **`list`** — print the current connection: `tool`, `provider`, `project_url`, the file mapping, and a
94
+ **available/unavailable** flag for the MCP (best-effort, the user's own session). No design tool
95
+ connected ⇒ "markdown-only".
96
+ - **`disconnect`** — remove the registry file (or set `tool: "none"`). The design tool's own
97
+ project/files are **never touched** — only the hub's record of them.
98
+
99
+ ## Hard rules
100
+
101
+ - **Local-user / MCP-session auth only; store no tokens.** Connect through the user's authenticated MCP
102
+ session; never embed a Figma PAT or any credential in the registry. `project_url`/`files` are plain
103
+ references.
104
+ - **Degrade gracefully.** No design tool / no MCP → `yad-ui` runs markdown-only with no error. The design
105
+ tool is additive, never a blocker — the same discipline as Impeccable and the `gh`/`glab` bridge.
106
+ - **Setup, not a gate.** Never touch `.sdlc/state.json`, approvals, or the contract lock from here.
107
+ - **Idempotent + refreshable.** `connect`/`refresh` are safe to re-run; a project carries one design
108
+ connection at a time.
109
+ - **Describe the connection; do not design here.** This skill records *how to reach* the tool. The actual
110
+ screens are generated/linked by `yad-ui`, per epic.
111
+
112
+ ## Reference
113
+ - Registry schema + freshness rule: `references/design-registry.md`.
114
+ - MCP detection per provider, the generate-vs-link recipes, the degrade path, and the honest
115
+ write-vs-read-only MCP capability note: `references/design-context.md`.
116
+ - The connect pattern this mirrors (code repos): `../yad-connect-repos/SKILL.md`.
117
+ - The consumer — how `yad-ui` generates/links and writes `design-links.json`: `../yad-ui/SKILL.md`.
@@ -0,0 +1,64 @@
1
+ # Design context — MCP detection, generate vs link, and the degrade path
2
+
3
+ How a connected design tool turns into the actual feature design the UI step materializes. The design
4
+ tool is reached through its **MCP** (a harness MCP server), the same shape as Impeccable's
5
+ slash-commands — detect it, use it when present, degrade cleanly when absent. This is the design-side
6
+ analogue of `yad-connect-repos`'s code-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
+ | `figma` | a Figma Dev Mode MCP | **read/link** — reference a file, read frames back into `ui-design.md` |
16
+ | `figma` | html.to.design MCP (`import-html`) | **generate** — render HTML/CSS screens into a Figma file |
17
+ | `pencil` | the `pencil` MCP (`batch_design`) | **generate** — author `.pen` web/mobile screens directly |
18
+ | other | the adapter's named MCP | per that adapter |
19
+
20
+ **Honest capability note:** not every design-tool MCP can *write*. A read-only Figma Dev Mode MCP
21
+ supports **link + read-back** only; *generate* needs a write-capable provider (html.to.design for Figma,
22
+ or `pencil`). `yad-ui` picks the direction the connected provider actually supports and records which one
23
+ it used (`direction: generated | linked`). It never claims to have generated screens a read-only MCP
24
+ cannot produce.
25
+
26
+ ## Generate (push screens into the tool)
27
+
28
+ When the connected provider is write-capable, the `ux-designer` lens produces the epic's screens in the
29
+ tool, covering the screens/states `ui-design.md` enumerates and the user flows the architecture defines:
30
+
31
+ - **Figma via html.to.design** — the lens drafts each screen as HTML/CSS (reusing `DESIGN.md` tokens),
32
+ then imports it into the Figma file via the MCP's `import-html`, one frame per screen.
33
+ - **pencil** — the lens calls `batch_design` to author the screens as `.pen` frames directly (mobile
34
+ and/or web per the epic).
35
+
36
+ Reuse what already exists: load the connected code repos' code-maps (`yad-ui` Step 2b) and any
37
+ Impeccable `DESIGN.md` tokens so generated screens match built components, not invented ones.
38
+
39
+ ## Link (reference a human-made design)
40
+
41
+ When a designer has already built the screens (or the provider is read-only), point `yad-ui` at the
42
+ existing file and **read the frames back** so `ui-design.md` reflects the real design: list each frame as
43
+ a screen, capture its node id + URL, and map components/tokens into `DESIGN.md`.
44
+
45
+ ## Write back the linkage (done by `yad-ui`, per epic)
46
+
47
+ Either direction ends by writing `epics/EP-<slug>/.sdlc/design-links.json` — the machine-readable
48
+ screen→frame map — and a `## Design (<tool>)` section in `ui-design.md` linking each screen to its frame
49
+ URL. The design itself lives in the tool; the hub keeps the *links* and the Markdown spec beside the
50
+ other epic artifacts.
51
+
52
+ ## Degrade path (no MCP / no tool)
53
+
54
+ If `design.json` is absent, `tool: "none"`, or `source: "unavailable"`, `yad-ui` runs **markdown-only**:
55
+ it authors `ui-design.md` / `DESIGN.md` exactly as before and records `design: none` in the frontmatter
56
+ with a one-line note (mirroring the `impeccable: not-installed` degrade). No error — the design tool is
57
+ purely additive.
58
+
59
+ ## Staleness / refresh
60
+
61
+ A re-generated or designer-edited file is like a moved code repo: `yad-ui` **flags** a divergence and
62
+ lets a human decide (re-run the step, or `yad-connect-design` action: refresh). It never silently
63
+ overwrites a designer's frames — refreshing the design is a human decision, the same discipline as
64
+ `code_context.refresh: human`.
@@ -0,0 +1,54 @@
1
+ # Design registry — schema + freshness rule
2
+
3
+ The registry is the product hub's record of which design tool is connected and how to reach it. It is
4
+ **project-wide** (one design 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/design.json`
10
+
11
+ (`config.yaml` `design.registry`.) Create the file and its parent `.sdlc/` on the first `connect`.
12
+
13
+ ## Schema
14
+
15
+ ```json
16
+ {
17
+ "tool": "figma", // figma | pencil | <adapter id> | none (markdown-only)
18
+ "provider": "figma-mcp", // the concrete MCP: figma-mcp | html-to-design | pencil-mcp | null
19
+ "project_url": "https://www.figma.com/files/project/123/feature", // team/project/file reference; null if none yet
20
+ "auth": "user", // ALWAYS the user's own MCP session — never a token
21
+ "files": { "web": null, "mobile": null }, // optional default file refs per platform
22
+ "connectedAt": "2026-06-13", // first connect (YYYY-MM-DD)
23
+ "lastSyncedAt": "2026-06-13", // last connect/refresh
24
+ "source": "figma-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` `design.tools` (or `none`). An unknown
31
+ tool falls back to `design.primary` with a warning — never silently accepted.
32
+ - **Auth is never stored.** No Figma PAT, OAuth token, or any credential in the registry. `project_url`
33
+ and `files` are plain references; `connect` reaches the tool through the user's authenticated MCP
34
+ session.
35
+ - **`connect` overwrites in place** — a project carries exactly one design connection at a time;
36
+ switching tools is just another `connect`. There is no array (unlike `repos.json`).
37
+ - **`source`** is the authority for availability: an MCP id (`figma-mcp` / `pencil-mcp` / …) means
38
+ `yad-ui` can generate/link; `unavailable` means `yad-ui` degrades to markdown-only. `refresh`
39
+ re-detects and may flip it.
40
+ - **`tool: "none"`** is a valid, deliberate state: a project that has chosen markdown-only. `yad-ui`
41
+ treats it exactly like an absent registry.
42
+ - **`disconnect`** removes the file (or sets `tool: "none"`). The design tool's own project/files are
43
+ never touched.
44
+
45
+ ## Git tracking
46
+
47
+ Commit the **registry** (`design.json`) — it is small, reviewable, and holds no secrets (references
48
+ only). This mirrors how `repos.json` and `hub.json` are committed.
49
+
50
+ ## Greenfield
51
+
52
+ A brand-new product hub has no `design.json`. That is valid — `yad-ui` treats "no design tool connected"
53
+ the same as `tool: "none"` and produces the Markdown artifacts only. The registry appears the first time
54
+ `connect` runs.
@@ -86,6 +86,19 @@ PR/MR opened on the hub (sibling of `approvals.json`, so the locked `state.json`
86
86
  { "step": "<review step id>", "artifact": "<artifact>", "platform": "github|gitlab", "number": <n>, "url": "<pr/mr url>", "branch": "review/EP-<slug>/<artifact-base>", "lastSyncedAt": "<YYYY-MM-DD or null>" }
87
87
  ```
88
88
 
89
+ ## `design-links.json`
90
+ Present only when the `ui-design` step materialized the design in a connected design tool
91
+ (`yad-connect-design` → `.sdlc/design.json`). Written by `yad-ui`, the machine-readable screen→frame map
92
+ (sibling of `contract-lock.json`; the locked `state.json` step shape is untouched). The `ui-design` step
93
+ chain is unchanged — this is an *output enrichment*, mirrored by the `design:` frontmatter block and the
94
+ `## Design (<tool>)` section in `ui-design.md`. Absent when the step ran markdown-only (`design: none`).
95
+
96
+ ```json
97
+ { "tool": "figma", "fileUrl": "<url>", "generatedAt": "<YYYY-MM-DD>", "direction": "generated|linked",
98
+ "screens": [ { "name": "<screen>", "platform": "mobile|web", "nodeId": "<id>", "url": "<frame url>" } ],
99
+ "source": "<mcp id>" }
100
+ ```
101
+
89
102
  ## `reviews/`
90
103
  Human-readable review records, one file per round:
91
104
  `reviews/<artifact-base>--<YYYY-MM-DD>--<status>.md` where `status` ∈ `comments` | `approved`
@@ -6,18 +6,28 @@ description: 'Front state 5 of the gated SDLC. With the ux-designer, author ui-d
6
6
  # SDLC — Author UI Design (front state 5)
7
7
 
8
8
  **Goal:** Produce a human-authored, AI-assisted `ui-design.md` and `DESIGN.md` for an approved
9
- architecture. This is a **front state**: human-authored with AI assist, **never auto-advances**. When
10
- the UI is drafted, control passes to `yad-review-gate` (base rule: owner + 1 reviewer).
9
+ architecture **and**, when a design tool is connected, the **actual feature design** the mobile
10
+ screens and/or web pages inside that tool (e.g. Figma), linked back from the artifacts. This is a
11
+ **front state**: human-authored with AI assist, **never auto-advances**. When the UI is drafted, control
12
+ passes to `yad-review-gate` (base rule: owner + 1 reviewer).
11
13
 
12
14
  UI work is shaped by **Impeccable**, invoked as **harness slash-commands** (not a subprocess CLI) per
13
15
  the Phase 0 deviation. If Impeccable is not installed, the `ux-designer` lens authors the same outputs
14
16
  directly — the workflow does not block on the tool.
15
17
 
18
+ The visual design is materialized in the **design tool connected via `yad-connect-design`**
19
+ (`.sdlc/design.json`), reached through its MCP. When a tool is connected the `ux-designer` lens
20
+ **generates** screens into it (or **links** an existing human-made design and reads it back); when none
21
+ is connected, the step degrades to the Markdown artifacts only — the design tool is additive, exactly
22
+ like Impeccable.
23
+
16
24
  ## Conventions
17
25
 
18
26
  - `{project-root}` resolves from the project working directory.
19
27
  - Artifacts live under `{project-root}/epics/EP-<slug>/` (build plan §6).
20
28
  - `DESIGN.md` is Impeccable's conventional root design-system file (RESEARCH-NOTES §4).
29
+ - The connected design tool is recorded in `{project-root}/.sdlc/design.json` (`config.yaml` `design`),
30
+ written by `yad-connect-design`. The per-epic screen→frame map is `design-links.json` (Step 4b).
21
31
  - Speak in the configured `communication_language`; write documents in `document_output_language`.
22
32
 
23
33
  ## On Activation
@@ -66,6 +76,25 @@ components/tokens into the design system; `/impeccable craft` is shape-then-buil
66
76
  that Impeccable was not used**. Do not run `npx impeccable skills install` as part of this step — tool
67
77
  installation is out of scope for the front half.
68
78
 
79
+ ### Step 3b — Materialize the design in the connected tool (generate or link)
80
+ Read `{project-root}/.sdlc/design.json` (`config.yaml` `design.registry`). Decide the path:
81
+
82
+ - **No tool / `tool: "none"` / `source: "unavailable"`** (or the file is absent): **degrade** —
83
+ author the Markdown artifacts only and record `design: none` in the frontmatter with a one-line note
84
+ (mirrors the `impeccable: not-installed` degrade). Skip to Step 4.
85
+ - **A tool is connected and its MCP is available:** adopt the `ux-designer` lens and, using the provider
86
+ recorded in `design.json` (Figma via a Figma/html.to.design MCP, `pencil` via its MCP, etc.):
87
+ - **Generate** — when the provider is write-capable, produce one frame per screen the design covers,
88
+ for the platforms in `epic.repos` (mobile and/or web), reusing the code-maps (Step 2b) and
89
+ `DESIGN.md` tokens so screens match built components rather than inventing parallel ones.
90
+ - **Link** — when the user points at an existing design file (or the provider is read-only), reference
91
+ it and **read the frames back** so `ui-design.md` reflects the real design.
92
+
93
+ Capture each screen's `name`, `platform`, `nodeId`, and `url`. Record which direction was used
94
+ (`generated | linked`). Honest-capability rule: a read-only MCP supports **link** only — never claim a
95
+ screen was generated that the provider cannot produce. See
96
+ `../yad-connect-design/references/design-context.md`.
97
+
69
98
  ### Step 4 — Write the UI artifacts
70
99
  Write `{project-root}/epics/EP-<slug>/ui-design.md` using EXACTLY this template:
71
100
 
@@ -78,6 +107,7 @@ owner: <inherit from epic.md owner> # the epic owner carries through; not rety
78
107
  repos: [<inherit from epic>]
79
108
  impeccable: <used | not-installed>
80
109
  code-context: { repos: [], loaded: <YYYY-MM-DD or none> } # code-maps that informed component reuse (Step 2b)
110
+ design: <none | { tool: <figma|pencil|…>, direction: <generated|linked>, file: <url>, screens: <N> }> # the connected design tool (Step 3b)
81
111
  ---
82
112
 
83
113
  ## Screens & states
@@ -91,23 +121,52 @@ code-context: { repos: [], loaded: <YYYY-MM-DD or none> } # code-maps that inf
91
121
 
92
122
  ## Accessibility & responsiveness
93
123
  <!-- a11y notes; breakpoints/viewports covered -->
124
+
125
+ ## Design (<tool>)
126
+ <!-- omit this section when design: none. one row per screen, linking to its frame in the tool.
127
+ mirrors design-links.json (Step 4b). -->
128
+ <!-- - <Screen name> (<mobile|web>) — <frame url> -->
94
129
  ```
95
130
 
96
131
  Also create/update `{project-root}/epics/EP-<slug>/DESIGN.md` (Impeccable's design-system file, or a
97
132
  hand-authored equivalent when degraded) capturing the design tokens/components the screens rely on.
98
133
 
134
+ ### Step 4b — Write the design-links map (when a tool was used)
135
+ When Step 3b generated or linked a design, write the machine-readable screen→frame map to
136
+ `{project-root}/epics/EP-<slug>/.sdlc/design-links.json` (sibling of `contract-lock.json`):
137
+
138
+ ```json
139
+ {
140
+ "tool": "figma",
141
+ "fileUrl": "https://www.figma.com/file/<key>/<name>",
142
+ "generatedAt": "<YYYY-MM-DD>",
143
+ "direction": "generated | linked",
144
+ "screens": [
145
+ { "name": "Submit Inquiry", "platform": "mobile",
146
+ "nodeId": "123:45", "url": "https://www.figma.com/file/<key>/?node-id=123-45" }
147
+ ],
148
+ "source": "figma-mcp"
149
+ }
150
+ ```
151
+
152
+ Keep the `## Design (<tool>)` section of `ui-design.md` in step with this file. When Step 3b degraded
153
+ (`design: none`), do **not** write `design-links.json`.
154
+
99
155
  ### Step 5 — Advance the authoring step (NOT the gate)
100
156
  In `state.json`: set `ui-design.status: "done"`, set `ui-design-review.status: "in_review"`, and set
101
157
  `currentStep: "ui-design-review"`. Write `state.json`. Do **not** touch `approvals.json`.
102
158
 
103
159
  ### Step 6 — Stop at the gate (do NOT advance)
104
- Report: the paths to `ui-design.md` and `DESIGN.md`, whether Impeccable was used, and that the next
105
- action is **review** via `yad-review-gate` (base rule: owner + 1 reviewer). **Never record approval
106
- here.** Front states do not auto-advance. When the hub has a platform, the gate opens a review PR on the
160
+ Report: the paths to `ui-design.md` and `DESIGN.md`, whether Impeccable was used, the connected design
161
+ tool and what it produced (e.g. "Figma 4 screens generated", the file URL + `design-links.json` path,
162
+ or "no design tool markdown-only"), and that the next action is **review** via `yad-review-gate` (base
163
+ rule: owner + 1 reviewer). **Never record approval here.** Front states do not auto-advance. When the hub has a platform, the gate opens a review PR on the
107
164
  hub (via `yad-hub-bridge`) and `yad-review-gate action: sync` pulls platform approvals/comments into
108
165
  the ledger; otherwise the review is recorded file-only.
109
166
 
110
167
  ## Reference
111
168
  - Impeccable commands and the slash-command-vs-CLI deviation: `RESEARCH-NOTES.md` §4 + Deviation 3.
112
169
  - State schema and field meanings: `../yad-epic/references/state-schema.md`.
170
+ - Connecting a design tool (Figma, pluggable) + generate-vs-link recipes and the degrade path:
171
+ `../yad-connect-design/SKILL.md` (+ `references/design-context.md`).
113
172
  - Connecting code repos + the code-context the brain reads: `../yad-connect-repos/SKILL.md`.