yadflow 2.6.0 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/CHANGELOG.md +4 -7
  2. package/README.md +23 -2
  3. package/bin/yad.mjs +22 -1
  4. package/cli/docs.mjs +298 -0
  5. package/cli/manifest.mjs +6 -1
  6. package/docs/index.html +4 -4
  7. package/package.json +2 -2
  8. package/skills/sdlc/config.yaml +19 -0
  9. package/skills/sdlc/install.sh +1 -1
  10. package/skills/sdlc/module-help.csv +4 -0
  11. package/skills/yad-connect-docs/SKILL.md +132 -0
  12. package/skills/yad-connect-docs/references/docs-registry.md +74 -0
  13. package/skills/yad-docs/SKILL.md +159 -0
  14. package/skills/yad-docs/references/data-mapping.md +75 -0
  15. package/skills/yad-docs/references/theme-map.md +69 -0
  16. package/skills/yad-docs/templates/app/README.md +31 -0
  17. package/skills/yad-docs/templates/app/eslint.config.js +23 -0
  18. package/skills/yad-docs/templates/app/index.html +17 -0
  19. package/skills/yad-docs/templates/app/package-lock.json +4030 -0
  20. package/skills/yad-docs/templates/app/package.json +35 -0
  21. package/skills/yad-docs/templates/app/public/favicon.svg +28 -0
  22. package/skills/yad-docs/templates/app/public/logo.svg +39 -0
  23. package/skills/yad-docs/templates/app/public/vite.svg +1 -0
  24. package/skills/yad-docs/templates/app/src/App.tsx +98 -0
  25. package/skills/yad-docs/templates/app/src/components/Auth/LoginPage.tsx +101 -0
  26. package/skills/yad-docs/templates/app/src/components/Canvas/AnimatedMessage.tsx +101 -0
  27. package/skills/yad-docs/templates/app/src/components/Canvas/ConnectionLine.tsx +90 -0
  28. package/skills/yad-docs/templates/app/src/components/Canvas/FlowCanvas.tsx +216 -0
  29. package/skills/yad-docs/templates/app/src/components/Canvas/SystemComponent.tsx +153 -0
  30. package/skills/yad-docs/templates/app/src/components/Controls/PlaybackBar.tsx +284 -0
  31. package/skills/yad-docs/templates/app/src/components/Controls/StepDetail.tsx +167 -0
  32. package/skills/yad-docs/templates/app/src/components/DetailPanel/HandlerLogicSnippet.tsx +41 -0
  33. package/skills/yad-docs/templates/app/src/components/DetailPanel/RequestPayloadPreview.tsx +46 -0
  34. package/skills/yad-docs/templates/app/src/components/DetailPanel/RightPanel.tsx +88 -0
  35. package/skills/yad-docs/templates/app/src/components/DetailPanel/StatusCard.tsx +76 -0
  36. package/skills/yad-docs/templates/app/src/components/DetailPanel/TriggerEventCard.tsx +45 -0
  37. package/skills/yad-docs/templates/app/src/components/DocLayout/DocPageShell.tsx +80 -0
  38. package/skills/yad-docs/templates/app/src/components/DocLayout/DocSectionCard.tsx +55 -0
  39. package/skills/yad-docs/templates/app/src/components/DocLayout/DocTableOfContents.tsx +79 -0
  40. package/skills/yad-docs/templates/app/src/components/DocLayout/RoleCard.tsx +67 -0
  41. package/skills/yad-docs/templates/app/src/components/DocSections/ApiReferenceSection.tsx +108 -0
  42. package/skills/yad-docs/templates/app/src/components/DocSections/CancelabilitySection.tsx +73 -0
  43. package/skills/yad-docs/templates/app/src/components/DocSections/CriticalRunbookSection.tsx +177 -0
  44. package/skills/yad-docs/templates/app/src/components/DocSections/DataMigrationSection.tsx +102 -0
  45. package/skills/yad-docs/templates/app/src/components/DocSections/DbSchemaSection.tsx +98 -0
  46. package/skills/yad-docs/templates/app/src/components/DocSections/DeploymentGuideSection.tsx +104 -0
  47. package/skills/yad-docs/templates/app/src/components/DocSections/DriverIntegrationSection.tsx +127 -0
  48. package/skills/yad-docs/templates/app/src/components/DocSections/ExecutiveSummarySection.tsx +69 -0
  49. package/skills/yad-docs/templates/app/src/components/DocSections/FlowOverviewSection.tsx +73 -0
  50. package/skills/yad-docs/templates/app/src/components/DocSections/FlowPathsChecklistSection.tsx +96 -0
  51. package/skills/yad-docs/templates/app/src/components/DocSections/MiddlewareChainSection.tsx +107 -0
  52. package/skills/yad-docs/templates/app/src/components/DocSections/MonitoringAlertingSection.tsx +106 -0
  53. package/skills/yad-docs/templates/app/src/components/DocSections/NotificationLocalizationSection.tsx +102 -0
  54. package/skills/yad-docs/templates/app/src/components/DocSections/PMRoadmapSection.tsx +133 -0
  55. package/skills/yad-docs/templates/app/src/components/DocSections/PerformanceTestingSection.tsx +91 -0
  56. package/skills/yad-docs/templates/app/src/components/DocSections/RiderIntegrationSection.tsx +99 -0
  57. package/skills/yad-docs/templates/app/src/components/DocSections/SecuritySection.tsx +74 -0
  58. package/skills/yad-docs/templates/app/src/components/DocSections/StatusMachineSection.tsx +90 -0
  59. package/skills/yad-docs/templates/app/src/components/DocSections/TestPlanSection.tsx +163 -0
  60. package/skills/yad-docs/templates/app/src/components/Logs/SystemLogsTerminal.tsx +126 -0
  61. package/skills/yad-docs/templates/app/src/components/Navigation/TopNavBar.tsx +90 -0
  62. package/skills/yad-docs/templates/app/src/components/Reference/BullMQJobsList.tsx +60 -0
  63. package/skills/yad-docs/templates/app/src/components/Reference/DecisionTreeView.tsx +49 -0
  64. package/skills/yad-docs/templates/app/src/components/Reference/DeeplinkActionsChips.tsx +69 -0
  65. package/skills/yad-docs/templates/app/src/components/Reference/DriverUIStatesTable.tsx +61 -0
  66. package/skills/yad-docs/templates/app/src/components/Reference/FeatureFlagMatrix.tsx +73 -0
  67. package/skills/yad-docs/templates/app/src/components/Reference/RiderUIStatesTable.tsx +61 -0
  68. package/skills/yad-docs/templates/app/src/components/Reference/RulesLegendPanel.tsx +217 -0
  69. package/skills/yad-docs/templates/app/src/components/Reference/StakeholderToggle.tsx +41 -0
  70. package/skills/yad-docs/templates/app/src/components/Reference/TroubleshootingSection.tsx +93 -0
  71. package/skills/yad-docs/templates/app/src/components/Sidebar/PathSelector.tsx +148 -0
  72. package/skills/yad-docs/templates/app/src/components/Sidebar/SidebarFooter.tsx +40 -0
  73. package/skills/yad-docs/templates/app/src/components/Sidebar/StepList.tsx +234 -0
  74. package/skills/yad-docs/templates/app/src/components/shared/Badge.tsx +28 -0
  75. package/skills/yad-docs/templates/app/src/components/shared/CommandPalette.tsx +213 -0
  76. package/skills/yad-docs/templates/app/src/components/shared/Icon.tsx +21 -0
  77. package/skills/yad-docs/templates/app/src/components/shared/Tooltip.tsx +42 -0
  78. package/skills/yad-docs/templates/app/src/data/components.ts +74 -0
  79. package/skills/yad-docs/templates/app/src/data/docSections.ts +231 -0
  80. package/skills/yad-docs/templates/app/src/data/paths.ts +2319 -0
  81. package/skills/yad-docs/templates/app/src/data/referenceData.ts +392 -0
  82. package/skills/yad-docs/templates/app/src/data/roles.ts +145 -0
  83. package/skills/yad-docs/templates/app/src/data/types.ts +79 -0
  84. package/skills/yad-docs/templates/app/src/hooks/useAnimationQueue.ts +41 -0
  85. package/skills/yad-docs/templates/app/src/hooks/usePlayback.ts +100 -0
  86. package/skills/yad-docs/templates/app/src/hooks/useStakeholderFilter.ts +10 -0
  87. package/skills/yad-docs/templates/app/src/index.css +121 -0
  88. package/skills/yad-docs/templates/app/src/main.tsx +13 -0
  89. package/skills/yad-docs/templates/app/src/pages/RoleSelectPage.tsx +34 -0
  90. package/skills/yad-docs/templates/app/src/pages/StakeholderDocPage.tsx +98 -0
  91. package/skills/yad-docs/templates/app/src/pages/SubPathDetailPage.tsx +282 -0
  92. package/skills/yad-docs/templates/app/src/store/useAuthStore.ts +42 -0
  93. package/skills/yad-docs/templates/app/src/store/useFlowStore.ts +197 -0
  94. package/skills/yad-docs/templates/app/src/utils/iconMap.ts +46 -0
  95. package/skills/yad-docs/templates/app/tsconfig.app.json +28 -0
  96. package/skills/yad-docs/templates/app/tsconfig.json +7 -0
  97. package/skills/yad-docs/templates/app/tsconfig.node.json +26 -0
  98. package/skills/yad-docs/templates/app/vite.config.ts +10 -0
  99. package/skills/yad-docs-overview/SKILL.md +129 -0
  100. package/skills/yad-docs-overview/references/pipeline-model.md +102 -0
  101. package/skills/yad-docs-sync/SKILL.md +99 -0
  102. package/skills/yad-docs-sync/references/staleness.md +81 -0
package/CHANGELOG.md CHANGED
@@ -1,18 +1,15 @@
1
- # [2.6.0](https://github.com/abdelrahmannasr/yadflow/compare/v2.5.0...v2.6.0) (2026-06-14)
1
+ # [2.7.0](https://github.com/abdelrahmannasr/yadflow/compare/v2.6.0...v2.7.0) (2026-06-14)
2
2
 
3
3
 
4
4
  ### Bug Fixes
5
5
 
6
- * allow scoped/breaking commit subjects + titles; parse only the trailer block ([63444c0](https://github.com/abdelrahmannasr/yadflow/commit/63444c08c1e33b4151c7389eb5e87f6ae682aee6))
7
- * harden pattern-gate CI pass PR title via env, write body to mktemp ([2415397](https://github.com/abdelrahmannasr/yadflow/commit/2415397f81280f459785b8fa9ca29007b473561b))
8
- * let `yad ship` derive the PR title from the committed subject ([a5adba3](https://github.com/abdelrahmannasr/yadflow/commit/a5adba3212b236ffc5a2470b9ea50bf97c6c4138))
6
+ * drop unused today param from runDocs (lint) ([1c255f1](https://github.com/abdelrahmannasr/yadflow/commit/1c255f1c848b9571502e29b142a07366612d638c))
7
+ * publish per-epic docs sites in CI + check shell-version staleness ([9862646](https://github.com/abdelrahmannasr/yadflow/commit/9862646fbe910de8ad8248a5f1bb586a5604b18f))
9
8
 
10
9
 
11
10
  ### Features
12
11
 
13
- * add `yad ship` CLI to commit and open a PR/MR in one step ([c493e93](https://github.com/abdelrahmannasr/yadflow/commit/c493e93e5626eee590cd061c9e7dbc8047e78718))
14
- * add commit-message/pr-title/pr-template pattern gates (code + hub) ([6658837](https://github.com/abdelrahmannasr/yadflow/commit/6658837d7685b826884b665a5e7661fd6ae99828))
15
- * add yad-commit/yad-open-pr/yad-ship skills; rename Step E to yad-engineer-review ([c566567](https://github.com/abdelrahmannasr/yadflow/commit/c5665679e45f24ea53c682aca3a78eb52c9f984f))
12
+ * add interactive documentation skills + yad docs CLI ([4bf7a25](https://github.com/abdelrahmannasr/yadflow/commit/4bf7a25c38e28ef47704a8e2f5acec2f724e4e29))
16
13
 
17
14
  # [2.2.0](https://github.com/abdelrahmannasr/yadflow/compare/v2.1.0...v2.2.0) (2026-06-14)
18
15
 
package/README.md CHANGED
@@ -133,7 +133,7 @@ simultaneous advancements can be lost; the next event or scheduled sweep re-sync
133
133
  ### What `setup` walks you through (10 steps)
134
134
 
135
135
  1. **Preflight** — confirm the hub is a git repo (offers `git init`); check `git`/`node`/`npx`.
136
- 2. **Install the module** — copy all 25 `yad-*` skills into the IDE skill dirs you pick
136
+ 2. **Install the module** — copy all 29 `yad-*` skills into the IDE skill dirs you pick
137
137
  (`.claude/`, `.agents/`, `.zencoder/`, `.opencode/`) and register `_bmad/sdlc/`.
138
138
  3. **Hub platform & roster** — detect GitHub/GitLab from the remote; record reviewers → `.sdlc/hub.json`.
139
139
  4. **Connect a design tool** — record the design tool (Figma / pencil / none) → `.sdlc/design.json` so
@@ -189,7 +189,7 @@ with a fix-it hint per finding. Failures carry stable, greppable codes, also pri
189
189
 
190
190
  Filing a bug? Attach `yad doctor --json` — it contains no secrets (names, paths, and check results only).
191
191
 
192
- ## Agent skills (all 25)
192
+ ## Agent skills (all 29)
193
193
 
194
194
  The CLI **installs and wires** the module; the skills below are the **agents you invoke by name** in your
195
195
  AI IDE (e.g. *“run `yad-epic`”*) to actually do the work. State lives in files you can also edit
@@ -218,6 +218,27 @@ directly. Each skill stops at a gate and never auto-advances unless a step has *
218
218
  tokens), detecting the **DeepTutor CLI on PATH** (a subprocess like Repomix — DeepTutor ships no MCP)
219
219
  and degrading to **harness-native** tutoring when absent. Idempotent and refreshable; one connection
220
220
  per project.
221
+ - **`yad-connect-docs`** — Connects a docs/Pages target (GitHub Pages / GitLab Pages, auto-detected from
222
+ `hub.json`) so the generated documentation sites can deploy. Records the target + scope + base path in
223
+ `.sdlc/docs.json` (local-user auth, no stored tokens), degrading to **build-only** when no Pages host /
224
+ CLI is present. Idempotent and refreshable; one connection per project.
225
+
226
+ ### Living documentation (generated, themed, auto-kept-fresh)
227
+
228
+ - **`yad-docs`** — Generates an **interactive documentation site** for an epic (a React + Vite + Tailwind
229
+ SPA: an animated front-stage flow canvas + role-based stakeholder doc pages) from the authored
230
+ artifacts — `epic.md`, `architecture.md`, the locked `contract.md`, `ui-design.md`, the stories — into
231
+ `epics/EP-<slug>/docs-site/`, themed by the **connected design system** (`DESIGN.md` / `design.json`
232
+ tokens → the site's CSS). The content lives in generated `src/data/*.ts`; the shell is a vendored
233
+ template. An **output enrichment, never a gate** — it never touches epic state, approvals, or the
234
+ contract lock. `generate` / `refresh` / `deploy`.
235
+ - **`yad-docs-overview`** — Generates the project **SDLC-overview site** (`docs/sdlc-site/`) — every
236
+ stage from setup → ship as flow paths / system components / stakeholder roles, reusing the same shell —
237
+ superseding the hand-maintained `docs/index.html`.
238
+ - **`yad-docs-sync`** — Keeps the sites fresh: detects staleness (a content hash of the authored
239
+ artifacts + the connected repos' HEAD shas vs each site's build manifest), regenerates + redeploys, and
240
+ can wire a CI job that rebuilds on push. Generalizes the rule that feature work must hand-update the
241
+ docs — the overview now regenerates whenever the skill set / pipeline changes.
221
242
 
222
243
  ### The learning layer (cross-cutting — any member, any stage)
223
244
 
package/bin/yad.mjs CHANGED
@@ -10,6 +10,7 @@ import { runCommit } from '../cli/commit.mjs';
10
10
  import { runOpenPr } from '../cli/openpr.mjs';
11
11
  import { runShip } from '../cli/ship.mjs';
12
12
  import { runRepo } from '../cli/repo.mjs';
13
+ import { runDocs } from '../cli/docs.mjs';
13
14
  import { runDoctor } from '../cli/doctor.mjs';
14
15
 
15
16
  const HELP = `${c.bold('yad')} — setup, review-gate & build helpers for the SDLC Workflow module ${c.dim('v' + VERSION)}
@@ -39,6 +40,12 @@ ${c.bold('Build helpers')}
39
40
  yad repo list Show connected repos (fresh / stale)
40
41
  yad repo refresh [name] Re-pack a stale repo (a human decision)
41
42
 
43
+ ${c.bold('Interactive docs (generated sites)')}
44
+ yad docs list Show the docs target + per-site freshness
45
+ yad docs build [--epic <id>|--overview] npm-build a generated doc site
46
+ yad docs deploy [--epic <id>|--overview] Build + report the Pages deploy
47
+ yad docs sync [--check|--refresh|--wire] Staleness sweep; --wire installs the Pages CI
48
+
42
49
  ${c.bold('Options')}
43
50
  --dir <path> Target project root (default: cwd)
44
51
  --type <t> commit: feat|fix|docs|refactor|test|perf|build|ci|chore|revert
@@ -48,6 +55,9 @@ ${c.bold('Options')}
48
55
  --contract-change commit/open-pr: mark the contract surface touched
49
56
  --risk <level> open-pr: low|medium|high (default low)
50
57
  --repo <name> open-pr: target a registered repo by name
58
+ --epic <id> docs: target one epic's site (EP-<slug>)
59
+ --overview docs: target the project SDLC-overview site
60
+ --check/--refresh/--wire docs sync: report stale / rebuild / install Pages CI
51
61
  --dry-run commit: print the message, do not commit
52
62
  --force commit: bypass the atomic-file guard / re-copy unchanged files
53
63
  --branch <head> gate ci: the review PR/MR head branch (review/EP-<slug>/<artifact>)
@@ -56,7 +66,7 @@ ${c.bold('Options')}
56
66
  -h, --help Show this help
57
67
  -v, --version Print version`;
58
68
 
59
- const VALUE_FLAGS = new Set(['--dir', '--type', '--message', '--task', '--ai', '--risk', '--repo', '--platform', '--base', '--title', '--scope', '--branch', '--pr']);
69
+ const VALUE_FLAGS = new Set(['--dir', '--type', '--message', '--task', '--ai', '--risk', '--repo', '--platform', '--base', '--title', '--scope', '--branch', '--pr', '--epic']);
60
70
 
61
71
  function parseArgs(argv) {
62
72
  const o = { _: [], dir: process.cwd(), fix: false, force: false, scope: 'all' };
@@ -66,6 +76,10 @@ function parseArgs(argv) {
66
76
  else if (a === '--force') o.force = true;
67
77
  else if (a === '--contract-change') o.contractChange = true;
68
78
  else if (a === '--no-push') o.noPush = true;
79
+ else if (a === '--overview') o.overview = true;
80
+ else if (a === '--check') o.check = true;
81
+ else if (a === '--refresh') o.refresh = true;
82
+ else if (a === '--wire') o.wire = true;
69
83
  else if (a === '--dry-run') o.dryRun = true;
70
84
  else if (a === '--json') o.json = true;
71
85
  else if (a === '-h' || a === '--help') o.help = true;
@@ -133,6 +147,13 @@ async function main() {
133
147
  await runRepo(o.dir, { action: action || 'list', name, today });
134
148
  break;
135
149
  }
150
+ case 'docs': {
151
+ const [, action] = o._;
152
+ if (o.epic && !isValidEpicId(o.epic)) { log(c.red(`invalid epic id: ${o.epic} (expected EP-<slug>, [a-z0-9-] only)`)); process.exitCode = 1; break; }
153
+ const sync = o.wire ? 'wire' : o.refresh ? 'refresh' : 'check';
154
+ await runDocs(o.dir, { action: action || 'list', epic: o.epic, overview: o.overview, sync, today });
155
+ break;
156
+ }
136
157
  default:
137
158
  log(c.red(`unknown command: ${cmd}`));
138
159
  log(HELP);
package/cli/docs.mjs ADDED
@@ -0,0 +1,298 @@
1
+ // `yad docs list|build|deploy|sync` — the build/deploy/staleness mechanics for the interactive
2
+ // documentation sites generated by the yad-docs / yad-docs-overview skills. The CONTENT generation
3
+ // (reading epic/architecture/contract/ui/stories → writing src/data/*.ts and theming index.css) is
4
+ // the AI step inside those skills; this module only does the mechanical parts: npm build (a
5
+ // subprocess, like yad-spec shelling npx repomix), the platform Pages wiring (reusing platform.mjs),
6
+ // and the manifest-hash staleness check (reusing the head-sha idea from repo.mjs). It NEVER touches
7
+ // epic state, approvals, or the contract lock — docs are an output enrichment, not a gate.
8
+ //
9
+ // Pure mapping fns (deployTargetFromHub / siteBasePath / docsArtifactHash / docsStale / pagesWorkflow)
10
+ // are exported for unit tests; the side-effecting runDocs orchestrates them.
11
+ import path from 'node:path';
12
+ import fs from 'node:fs';
13
+ import { createHash } from 'node:crypto';
14
+ import { c, log, ok, info, warn, hand, fail, readJSON, run, has, exists } from './lib.mjs';
15
+ import { PROJECT_FILES, VERSION } from './manifest.mjs';
16
+ import { detectPlatform, platformReady } from './platform.mjs';
17
+ import { gitHead } from './setup.mjs';
18
+ import { contractSurfaceHash } from './epic-state.mjs';
19
+
20
+ // ---- registry + site locations ------------------------------------------------------------------
21
+ function loadDocs(root) {
22
+ const regPath = path.join(root, PROJECT_FILES.docsConfig);
23
+ return { regPath, docs: readJSON(regPath, null) };
24
+ }
25
+
26
+ // Per-epic site lives beside the epic artifacts; the overview is project-level under docs/.
27
+ export function siteDir(root, { epic, overview } = {}) {
28
+ if (overview) return path.join(root, 'docs/sdlc-site');
29
+ return path.join(root, 'epics', epic, 'docs-site');
30
+ }
31
+ export function manifestPath(root, { epic, overview } = {}) {
32
+ if (overview) return path.join(siteDir(root, { overview }), '.docs-build.json');
33
+ return path.join(root, 'epics', epic, '.sdlc/docs-build.json');
34
+ }
35
+
36
+ // ---- pure: platform/target + base path ----------------------------------------------------------
37
+ // hub.json platform -> the default Pages target (github-pages | gitlab-pages | none/build-only).
38
+ export function deployTargetFromHub(hub = {}) {
39
+ const platform = hub?.platform || detectPlatform(hub?.git_url || '');
40
+ if (platform === 'github') return 'github-pages';
41
+ if (platform === 'gitlab') return 'gitlab-pages';
42
+ return 'none';
43
+ }
44
+
45
+ // The Vite `base` for one site: join the project base path (from docs.json) with the per-site
46
+ // subpath. Per-epic sites nest under epics/<id>/; the overview is the Pages root. Always a
47
+ // leading+trailing slash so it works as a Vite base and a router basename.
48
+ export function siteBasePath(docs = {}, { epic, overview } = {}) {
49
+ const root = (docs.basePath || '/').replace(/\/+$/, '') || '';
50
+ const sub = overview ? '' : epic ? `/epics/${epic}` : '';
51
+ const joined = `${root}${sub}`.replace(/\/+/g, '/');
52
+ return joined ? `${joined.replace(/\/$/, '')}/` : '/';
53
+ }
54
+
55
+ // ---- pure: staleness ----------------------------------------------------------------------------
56
+ // The artifacts whose content a per-epic site is generated from (any that exist contribute).
57
+ // contract.md is deliberately EXCLUDED here: it is folded in via its CONTRACT-SURFACE block hash
58
+ // (docsArtifactHash's `extra`), so docs-staleness tracks the same locked surface as the contract
59
+ // lock — non-surface prose edits to contract.md don't needlessly mark the docs stale.
60
+ export function docsArtifactFiles(root, epic) {
61
+ const epicRoot = path.join(root, 'epics', epic);
62
+ const flat = ['epic.md', 'architecture.md', 'ui-design.md', 'DESIGN.md', 'test-cases.md']
63
+ .map((f) => path.join(epicRoot, f));
64
+ const storiesDir = path.join(epicRoot, 'stories');
65
+ const stories = exists(storiesDir)
66
+ ? fs.readdirSync(storiesDir).filter((f) => f.endsWith('.md')).sort().map((f) => path.join(storiesDir, f))
67
+ : [];
68
+ return [...flat, ...stories].filter(exists).sort();
69
+ }
70
+
71
+ // sha256 over the (sorted) artifact files' bytes plus an optional `extra` string (the contract
72
+ // surface hash) — the content baseline a site was built from. Deterministic: the file list is
73
+ // sorted and each file is length-prefixed so concatenation is unambiguous (a + bc never collides
74
+ // with ab + c).
75
+ export function docsArtifactHash(files = [], extra = '') {
76
+ const h = createHash('sha256');
77
+ for (const f of [...files].sort()) {
78
+ const buf = fs.readFileSync(f);
79
+ h.update(`${path.basename(f)}:${buf.length}\n`);
80
+ h.update(buf);
81
+ }
82
+ if (extra) h.update(`\ncontract-surface:${extra}`);
83
+ return 'sha256:' + h.digest('hex');
84
+ }
85
+
86
+ // Current HEAD sha per repo the epic touches (mirrors repo.mjs head-sha staleness).
87
+ export function repoHeadsFor(root, repos = [], registry = { repos: [] }) {
88
+ const out = {};
89
+ for (const name of repos) {
90
+ const entry = (registry.repos || []).find((r) => r.name === name);
91
+ if (!entry) continue;
92
+ out[name] = gitHead(path.resolve(root, entry.path)) || null;
93
+ }
94
+ return out;
95
+ }
96
+
97
+ // Compare a build manifest to the current world; list the concrete reasons it is stale.
98
+ export function docsStale(manifest, { artifactHash, repoHeads = {}, templateVersion } = {}) {
99
+ const reasons = [];
100
+ if (!manifest) return { stale: true, reasons: ['never built'] };
101
+ if (artifactHash && manifest.artifactHash && artifactHash !== manifest.artifactHash) {
102
+ reasons.push('authored artifacts changed');
103
+ }
104
+ for (const [repo, head] of Object.entries(repoHeads)) {
105
+ const was = (manifest.repoHeads || {})[repo];
106
+ if (head && was && head !== was) reasons.push(`repo ${repo} HEAD advanced`);
107
+ }
108
+ if (templateVersion && manifest.templateVersion && templateVersion !== manifest.templateVersion) {
109
+ reasons.push(`doc shell upgraded (${manifest.templateVersion} → ${templateVersion})`);
110
+ }
111
+ return { stale: reasons.length > 0, reasons };
112
+ }
113
+
114
+ // ---- pure: the Pages CI workflow (committed by `yad docs sync --wire`) ---------------------------
115
+ // GitHub Actions deploy-pages, or a GitLab `pages` job. Both assemble a single `public/` tree: the
116
+ // overview at the root and every per-epic site under `epics/<id>/` — matching siteBasePath's nesting
117
+ // (overview at `<base>/`, epics at `<base>/epics/EP-<slug>/`). A concurrency group prevents the
118
+ // deploy from retriggering. The shared shell script keeps the two platforms byte-for-byte aligned.
119
+ const BUILD_PUBLIC = [
120
+ 'mkdir -p public',
121
+ 'if [ -d docs/sdlc-site ]; then (cd docs/sdlc-site && npm ci && npm run build) && cp -r docs/sdlc-site/dist/. public/; fi',
122
+ 'for d in epics/*/docs-site; do [ -d "$d" ] || continue; id=$(basename "$(dirname "$d")"); (cd "$d" && npm ci && npm run build) && mkdir -p "public/epics/$id" && cp -r "$d/dist/." "public/epics/$id/"; done',
123
+ ];
124
+ export function pagesWorkflow(platform) {
125
+ if (platform === 'gitlab') {
126
+ return `# yad-managed — built by \`yad docs sync --wire\`. include it from .gitlab-ci.yml:
127
+ # include: { local: .gitlab/ci/yad-docs.yml }
128
+ # Edit the skill, not this file.
129
+ pages:
130
+ stage: deploy
131
+ image: node:20
132
+ rules:
133
+ - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
134
+ script:
135
+ ${BUILD_PUBLIC.map((l) => ` - '${l.replace(/'/g, "'\\''")}'`).join('\n')}
136
+ artifacts:
137
+ paths: [public]
138
+ resource_group: pages
139
+ `;
140
+ }
141
+ return `# yad-managed — built by \`yad docs sync --wire\`. Edit the skill, not this file.
142
+ name: yad-docs
143
+ on:
144
+ push:
145
+ branches: [main, master]
146
+ workflow_dispatch:
147
+ permissions:
148
+ contents: read
149
+ pages: write
150
+ id-token: write
151
+ concurrency:
152
+ group: yad-docs-pages
153
+ cancel-in-progress: true
154
+ jobs:
155
+ build-deploy:
156
+ runs-on: ubuntu-latest
157
+ environment:
158
+ name: github-pages
159
+ url: \${{ steps.deployment.outputs.page_url }}
160
+ steps:
161
+ - uses: actions/checkout@v4
162
+ - uses: actions/setup-node@v4
163
+ with:
164
+ node-version: 20
165
+ - name: Build the overview + per-epic sites into ./public
166
+ run: |
167
+ ${BUILD_PUBLIC.map((l) => ` ${l}`).join('\n')}
168
+ - uses: actions/configure-pages@v5
169
+ - uses: actions/upload-pages-artifact@v3
170
+ with:
171
+ path: public
172
+ - id: deployment
173
+ uses: actions/deploy-pages@v4
174
+ `;
175
+ }
176
+ export function pagesWorkflowPath(platform) {
177
+ return platform === 'gitlab' ? '.gitlab/ci/yad-docs.yml' : '.github/workflows/yad-docs.yml';
178
+ }
179
+
180
+ // ---- build (subprocess) -------------------------------------------------------------------------
181
+ function buildSite(dir) {
182
+ if (!exists(dir)) { warn(`no generated site at ${path.relative(process.cwd(), dir)} — run the yad-docs skill first`); return { ok: false, missing: true }; }
183
+ if (!has('npm')) { warn('npm not on PATH — cannot build; the CI workflow will build on push'); return { ok: false, noNpm: true }; }
184
+ const install = exists(path.join(dir, 'package-lock.json')) ? ['ci'] : ['install'];
185
+ log(` ${c.dim('$')} npm ${install[0]} ${c.dim(`(${path.relative(process.cwd(), dir)})`)}`);
186
+ const i = run('npm', install, { cwd: dir, stdio: 'inherit' });
187
+ if (!i.ok) { fail('npm install failed'); return { ok: false }; }
188
+ const b = run('npm', ['run', 'build'], { cwd: dir, stdio: 'inherit' });
189
+ if (!b.ok) { fail('npm run build failed'); return { ok: false }; }
190
+ return { ok: true, dist: path.join(dir, 'dist') };
191
+ }
192
+
193
+ // ---- orchestration ------------------------------------------------------------------------------
194
+ export async function runDocs(root, { action = 'list', epic, overview, sync } = {}) {
195
+ const { docs } = loadDocs(root);
196
+ const targets = overview ? [{ overview: true }] : epic ? [{ epic }] : enumerateSites(root);
197
+
198
+ if (action === 'list') {
199
+ if (!docs) { warn('no docs target connected (.sdlc/docs.json) — run yad-connect-docs'); }
200
+ else {
201
+ log(c.bold('\ndocs target'));
202
+ info(`target ${c.cyan(docs.target)} scope ${docs.scope} base ${docs.basePath} ${docs.source === 'unavailable' ? c.yellow('(build-only)') : ''}`);
203
+ }
204
+ log(c.bold('\ngenerated sites'));
205
+ if (!targets.length) { info('none generated yet'); return { sites: 0 }; }
206
+ for (const t of targets) reportFreshness(root, t);
207
+ return { sites: targets.length };
208
+ }
209
+
210
+ if (action === 'build' || action === 'deploy') {
211
+ let built = 0;
212
+ for (const t of targets) {
213
+ const r = buildSite(siteDir(root, t));
214
+ if (r.ok) { built++; ok(`built ${label(t)} ${c.dim('→ ' + path.relative(root, r.dist))}`); }
215
+ }
216
+ if (action === 'deploy') {
217
+ const platform = docs ? (docs.target === 'gitlab-pages' ? 'gitlab' : docs.target === 'github-pages' ? 'github' : null) : null;
218
+ if (!platform || !platformReady(platform)) {
219
+ hand('no Pages platform/CLI — built locally only; commit + push so the CI workflow can publish (yad docs sync --wire)');
220
+ } else {
221
+ ok(`deploy via the ${platform} Pages workflow on push (yad docs sync --wire installs it)`);
222
+ if (docs?.basePath) info(`will publish under ${docs.basePath}`);
223
+ }
224
+ }
225
+ return { built };
226
+ }
227
+
228
+ if (action === 'sync') {
229
+ if (sync === 'wire') return wirePages(root, docs);
230
+ // check (default) + refresh both compute staleness; refresh additionally rebuilds.
231
+ const registry = readJSON(path.join(root, PROJECT_FILES.reposRegistry), { repos: [] });
232
+ let stale = 0;
233
+ for (const t of targets) {
234
+ const s = freshness(root, t, registry);
235
+ if (s.stale) {
236
+ stale++;
237
+ warn(`${label(t)} — ${c.yellow('stale')}: ${s.reasons.join('; ')}`);
238
+ if (sync === 'refresh') buildSite(siteDir(root, t));
239
+ } else ok(`${label(t)} ${c.dim('— fresh')}`);
240
+ }
241
+ if (stale && sync !== 'refresh') hand('regenerate content with the yad-docs / yad-docs-overview skill (the AI step), then `yad docs deploy`');
242
+ return { stale };
243
+ }
244
+
245
+ fail(`unknown docs action: ${action} (list | build | deploy | sync)`);
246
+ process.exitCode = 1;
247
+ return {};
248
+ }
249
+
250
+ // ---- helpers ------------------------------------------------------------------------------------
251
+ function enumerateSites(root) {
252
+ const out = [];
253
+ if (exists(siteDir(root, { overview: true }))) out.push({ overview: true });
254
+ const epicsDir = path.join(root, 'epics');
255
+ if (exists(epicsDir)) {
256
+ for (const e of fs.readdirSync(epicsDir).sort()) {
257
+ if (exists(path.join(epicsDir, e, 'docs-site'))) out.push({ epic: e });
258
+ }
259
+ }
260
+ return out;
261
+ }
262
+ function label(t) { return t.overview ? 'overview (docs/sdlc-site)' : `epic ${t.epic}`; }
263
+
264
+ function freshness(root, t, registry) {
265
+ const manifest = readJSON(manifestPath(root, t), null);
266
+ if (t.overview) {
267
+ // The overview is generated from the project pipeline definition, not an epic's artifacts.
268
+ const files = ['skills/sdlc/config.yaml', 'skills/sdlc/module-help.csv', 'docs/diagrams/sdlc-overview.mmd']
269
+ .map((f) => path.join(root, f)).filter(exists);
270
+ return docsStale(manifest, { artifactHash: docsArtifactHash(files), templateVersion: VERSION });
271
+ }
272
+ const epicMeta = readJSON(path.join(root, 'epics', t.epic, '.sdlc/state.json'), {});
273
+ const repos = epicMeta.repos || [];
274
+ const surface = contractSurfaceHash(path.join(root, 'epics', t.epic)); // null when no locked surface
275
+ return docsStale(manifest, {
276
+ artifactHash: docsArtifactHash(docsArtifactFiles(root, t.epic), surface || ''),
277
+ repoHeads: repoHeadsFor(root, repos, registry),
278
+ templateVersion: VERSION,
279
+ });
280
+ }
281
+ function reportFreshness(root, t) {
282
+ const registry = readJSON(path.join(root, PROJECT_FILES.reposRegistry), { repos: [] });
283
+ const s = freshness(root, t, registry);
284
+ if (s.stale) warn(`${label(t)} — ${c.yellow('stale')}: ${s.reasons.join('; ')}`);
285
+ else ok(`${label(t)} ${c.dim('— fresh')}`);
286
+ }
287
+
288
+ function wirePages(root, docs) {
289
+ const platform = docs?.target === 'gitlab-pages' ? 'gitlab' : 'github';
290
+ const rel = pagesWorkflowPath(platform);
291
+ const dest = path.join(root, rel);
292
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
293
+ fs.writeFileSync(dest, pagesWorkflow(platform));
294
+ ok(`wired ${rel} ${c.dim(`(${platform} Pages)`)}`);
295
+ if (platform === 'gitlab') hand(`include it from .gitlab-ci.yml: include: { local: ${rel} }`);
296
+ else hand('commit it; set the repo Pages source to "GitHub Actions" so it publishes on push');
297
+ return { wired: rel };
298
+ }
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 25 hand-authored yad-* skills (mirrors skills/sdlc/install.sh).
13
+ // The 29 hand-authored yad-* skills (mirrors skills/sdlc/install.sh).
14
14
  export const SKILLS = [
15
15
  'yad-analysis',
16
16
  'yad-epic',
@@ -22,6 +22,10 @@ export const SKILLS = [
22
22
  'yad-connect-design',
23
23
  'yad-connect-testing',
24
24
  'yad-connect-learning',
25
+ 'yad-connect-docs',
26
+ 'yad-docs',
27
+ 'yad-docs-overview',
28
+ 'yad-docs-sync',
25
29
  'yad-learn',
26
30
  'yad-spec',
27
31
  'yad-implement',
@@ -121,6 +125,7 @@ export const PROJECT_FILES = {
121
125
  designConfig: '.sdlc/design.json',
122
126
  testingConfig: '.sdlc/testing.json',
123
127
  learningConfig: '.sdlc/learning.json',
128
+ docsConfig: '.sdlc/docs.json',
124
129
  version: '.sdlc/cli-version.json',
125
130
  };
126
131
 
package/docs/index.html CHANGED
@@ -284,7 +284,7 @@
284
284
  <div class="lane">
285
285
  <div class="lane-title">0 · One-time setup (team lead, per project)</div>
286
286
  <div class="flow-v">
287
- <div class="node plain"><strong>Install the module</strong><small><code>npx yadflow setup</code> — copies the 25 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 29 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>
@@ -447,7 +447,7 @@ each code repo/
447
447
  <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>
448
448
  <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>
449
449
  <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>
450
- <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 25 skills live and where you pull updates from. No real product work happens inside it.</div>
450
+ <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 29 skills live and where you pull updates from. No real product work happens inside it.</div>
451
451
  <div class="term"><strong>Front half (“decide” / the brain)</strong><span class="badge b-core">core</span><br>The first half of the workflow, run once per epic in the product hub: analysis (optional) → epic → architecture + contract → UI design → stories — each followed by a human review gate, ending at <code>ready-for-build</code>. <strong>Test cases</strong> is a parallel, non-blocking track that opens at the stories gate and runs alongside the build half. Permanently human-approved.</div>
452
452
  <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>
453
453
  <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>
@@ -684,7 +684,7 @@ each code repo/
684
684
 
685
685
  <!-- ======================================================= -->
686
686
  <section id="skills">
687
- <h2>6. The 25 skills — what each does, how to use it, and when</h2>
687
+ <h2>6. The 29 skills — what each does, how to use it, and when</h2>
688
688
  <p>A <strong>skill</strong> is an agent you invoke <em>by name</em> in your AI IDE — you just ask in
689
689
  plain words, e.g. <em>“run <code>yad-epic</code>”</em>, adding any inputs the skill needs
690
690
  (<code>repo: backend</code>, <code>story: EP-…-S01</code>, <code>action: wire</code>, …).
@@ -979,7 +979,7 @@ yad-status EP-istifta-inquiries # one epic in detail</code></pre>
979
979
 
980
980
  <div class="skillcard">
981
981
  <h4><code>npx yadflow setup</code></h4>
982
- <div class="row"><span class="lbl lbl-what">What</span> The guided first-run wizard. Ten steps: preflight (git/node check), install all 25 skills into the IDE dirs you pick, detect the hub’s platform + record the reviewer roster, connect a design tool (Figma-first; optional), connect a testing tool (Playwright-first; optional), connect a learning tool (DeepTutor-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>
982
+ <div class="row"><span class="lbl lbl-what">What</span> The guided first-run wizard. Ten steps: preflight (git/node check), install all 29 skills into the IDE dirs you pick, detect the hub’s platform + record the reviewer roster, connect a design tool (Figma-first; optional), connect a testing tool (Playwright-first; optional), connect a learning tool (DeepTutor-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>
983
983
  <div class="row"><span class="lbl lbl-how">How</span></div>
984
984
  <pre><code>cd &lt;product-hub-repo&gt;
985
985
  npx yadflow setup</code></pre>
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "yadflow",
3
- "version": "2.6.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 + 25 yad-* skills.",
3
+ "version": "2.7.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 + 29 yad-* skills.",
5
5
  "type": "module",
6
6
  "author": "AbdelRahman Nasr",
7
7
  "license": "MIT",
@@ -176,6 +176,25 @@ learning:
176
176
  # tokens in the registry (kb name + sources are plain references, never credentials). Opt-in, never a gate.
177
177
  auth: user
178
178
 
179
+ # Interactive documentation (yad-connect-docs / yad-docs / yad-docs-overview / yad-docs-sync) — the
180
+ # generated React+Vite+Tailwind doc SITES. yad-docs builds a per-epic site (epics/EP-<slug>/docs-site/)
181
+ # from the authored artifacts, themed by the connected design system; yad-docs-overview builds the
182
+ # project SDLC-overview site (docs/sdlc-site/) from the pipeline definition, superseding docs/index.html.
183
+ # The deploy TARGET is a Pages host, auto-detected from hub.platform (github->github-pages,
184
+ # gitlab->gitlab-pages, null->build-only). The yad CLI does the npm build + Pages wiring + staleness;
185
+ # the content generation is the AI step. Docs are an OUTPUT ENRICHMENT — never a gate, never touch state.
186
+ docs:
187
+ registry: "{project-root}/.sdlc/docs.json" # project-wide docs/Pages connection (NOT per-epic)
188
+ targets: [github-pages, gitlab-pages] # supported Pages hosts; auto-detected from hub.platform
189
+ scope: hub # publish from: hub (default) | <repo-name> | dedicated
190
+ degrade: build-only # no Pages host / no gh|glab => build the dist, publish via CI
191
+ login_gate: false # public docs by default (the client-side gate is presentational only)
192
+ manifest: "{project-root}/epics/EP-<slug>/.sdlc/docs-build.json" # per-epic staleness baseline (yad-docs)
193
+ overview: "{project-root}/docs/sdlc-site/" # the project SDLC-overview site (yad-docs-overview)
194
+ # Auth: `yad-connect-docs` records only how to reach the Pages host (platform + base path); it stores NO
195
+ # tokens — publishing runs as the LOCAL user's own gh/glab or the platform CI. Idempotent, refreshable.
196
+ auth: user
197
+
179
198
  # Hub platform + front-half review bridge (yad-connect-repos `detect-hub`; yad-review-gate + yad-hub-bridge).
180
199
  # The product hub is itself a git repo on a platform. With the bridge enabled, the front-half review/
181
200
  # 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-test-cases yad-connect-repos yad-connect-design yad-connect-testing yad-connect-learning 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-analysis yad-epic yad-architecture yad-ui yad-stories yad-test-cases yad-connect-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
 
@@ -24,3 +24,7 @@ SDLC Workflow,yad-engineer-review,Engineer Review & Merge,ER,"Build-half Step E:
24
24
  SDLC Workflow,yad-backfill,Backfill Specs,BF,"Build-half Step G: generate specs for already-built features in an existing repo. Confirm Repomix (npx repomix CLI), pack ONE feature (compress + git logs, secret-scan), feed to AI with a 'describe what exists, do not invent' prompt, write a DRAFT spec marked verified: false. Human approval (reuse yad-review-gate) makes it real. Boundary auto-proposed and human-confirmed. A change is blocked only until the features it touches have approved specs. Never auto-advances.",,{repo: <repo>} {feature: <name + globs>} {action: pack|draft|approve|gate},3-build,,,false,demo-repos/<repo>/specs/backfill/<feature>/,spec.md backfill-check.sh
25
25
  SDLC Workflow,yad-run,Run (Automation),RN,"Phase 4 orchestrator: drive a story's back-half loop (spec→tasks→implement→checks) in one code repo, reading each step's automation dial from build-state — on machine_advance it advances on its own, on human_approve it stops for a human. Records every run in the trust log. Realizes Step B (a clean checks pass auto-advances to the engineer review when earned; any FAIL / scope overrun / contract touch HALTS). set-dial earns/reverts a step's automation (machine_advance gated by the trust threshold; front states and engineer-review refused); kill/unkill toggles the system-wide kill switch. Front states and the human merge gate never auto-advance.",,{epic: EP-<slug>} {story: EP-<slug>-S0N} {repo: <repo>} {action: run|set-dial|kill|unkill} {step: <back-step>} {to: human_approve|machine_advance},4-automate,yad-spec,yad-engineer-review,false,epics/EP-<slug>/.sdlc/,build-state/<story-id>.json trust-log.json
26
26
  SDLC Workflow,yad-status,SDLC Status,SS,"Read-only: print the full front-state chain, per-step dials, contract lock, story repo tags, and pending approvals at the active gate. For stories in the build half, also print each back-half step's automation dial and status, the trust record (runs / % approved-unchanged / earned vs gathering evidence), and the system-wide kill-switch state.",,{epic: EP-<slug>},1-front,,,false,,
27
+ SDLC Workflow,yad-connect-docs,Connect Docs Target,DX,"Setup/maintenance: connect a docs/Pages publishing target so the interactive-docs steps can deploy the generated site (not just commit its source). Auto-detects the platform from .sdlc/hub.json (github->github-pages, gitlab->gitlab-pages, null->build-only), resolves the Vite base path, and records the target in .sdlc/docs.json (local-user auth, no stored tokens). Degrades to build-only when gh/glab is absent. Idempotent and refreshable; one connection per project. Not a gated state — never touches epic state or approvals.",,{action: connect|refresh|list|disconnect} {target: github-pages|gitlab-pages|none} {scope: hub|<repo>|dedicated} {base_path: <override>},0-setup,,yad-docs,false,.sdlc/,docs.json
28
+ SDLC Workflow,yad-docs,Author Docs Site,DS,"Generate the per-epic interactive documentation SPA (animated flow canvas + role-based stakeholder doc pages) from the epic's approved artifacts (epic, architecture, locked contract, ui-design, stories, code-context, test-cases), themed by the connected design system, into epics/EP-<slug>/docs-site/. Copies the vendored React/Vite/Tailwind shell verbatim and generates src/data/*.ts deterministically; drives the yad docs build/deploy CLI to publish to Pages (or build-only). An OUTPUT ENRICHMENT — never a gate; never mutates state.json, approvals, or the contract lock.",,{epic: EP-<slug>} {action: generate|refresh|deploy} {login_gate: true|false},,yad-review-gate,,false,epics/EP-<slug>/docs-site/,docs-site/ docs-build.json
29
+ SDLC Workflow,yad-docs-overview,Docs Overview Site,DO,"Generate the project SDLC-overview interactive site (docs/sdlc-site/) — every stage from setup to ship modeled as flow paths, system components, and stakeholder roles — reusing the same vendored shell, themed with yadflow's brand palette. Reads config.yaml + module-help.csv + the overview diagram as the pipeline source. Supersedes the hand-maintained docs/index.html (left as a thin redirect for one release). Not a gate — a project-level enrichment that regenerates whenever the skill set / pipeline changes.",,{action: generate|deploy},,,,false,docs/sdlc-site/,sdlc-site/ .docs-build.json
30
+ SDLC Workflow,yad-docs-sync,Docs Sync,DY,"Maintenance/CI: keep the generated doc sites fresh. Detect staleness (a content hash of the approved artifacts + the connected repos' HEAD shas + the doc-shell version vs each site's build manifest), report which sites drifted and why, regenerate + redeploy the stale ones, and wire a CI job that rebuilds on push (carrying [skip ci] + a concurrency group to prevent deploy loops). Generalizes the rule that feature work must hand-update docs/index.html + diagrams + skill counts. Refresh is always a human/CI decision; never a gate.",,{action: check|refresh|wire} {epic: EP-<slug>},,,,false,epics/EP-<slug>/.sdlc/,docs-build.json yad-docs.yml