yadflow 2.5.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 (124) hide show
  1. package/CHANGELOG.md +8 -2
  2. package/README.md +65 -22
  3. package/bin/yad.mjs +27 -1
  4. package/cli/docs.mjs +298 -0
  5. package/cli/doctor.mjs +1 -0
  6. package/cli/manifest.mjs +23 -2
  7. package/cli/ship.mjs +37 -0
  8. package/docs/index.html +44 -13
  9. package/package.json +2 -2
  10. package/skills/sdlc/config.yaml +26 -2
  11. package/skills/sdlc/install.sh +1 -1
  12. package/skills/sdlc/module-help.csv +11 -4
  13. package/skills/yad-checks/references/check-gates.md +58 -2
  14. package/skills/yad-checks/templates/checks/commit-message.sh +82 -0
  15. package/skills/yad-checks/templates/github/yad-checks.yml +27 -0
  16. package/skills/yad-checks/templates/github/yad-hub-checks.yml +36 -0
  17. package/skills/yad-checks/templates/gitlab/yad-checks.gitlab-ci.yml +20 -0
  18. package/skills/yad-checks/templates/gitlab/yad-hub-checks.gitlab-ci.yml +39 -0
  19. package/skills/yad-commit/SKILL.md +66 -0
  20. package/skills/yad-connect-docs/SKILL.md +132 -0
  21. package/skills/yad-connect-docs/references/docs-registry.md +74 -0
  22. package/skills/yad-docs/SKILL.md +159 -0
  23. package/skills/yad-docs/references/data-mapping.md +75 -0
  24. package/skills/yad-docs/references/theme-map.md +69 -0
  25. package/skills/yad-docs/templates/app/README.md +31 -0
  26. package/skills/yad-docs/templates/app/eslint.config.js +23 -0
  27. package/skills/yad-docs/templates/app/index.html +17 -0
  28. package/skills/yad-docs/templates/app/package-lock.json +4030 -0
  29. package/skills/yad-docs/templates/app/package.json +35 -0
  30. package/skills/yad-docs/templates/app/public/favicon.svg +28 -0
  31. package/skills/yad-docs/templates/app/public/logo.svg +39 -0
  32. package/skills/yad-docs/templates/app/public/vite.svg +1 -0
  33. package/skills/yad-docs/templates/app/src/App.tsx +98 -0
  34. package/skills/yad-docs/templates/app/src/components/Auth/LoginPage.tsx +101 -0
  35. package/skills/yad-docs/templates/app/src/components/Canvas/AnimatedMessage.tsx +101 -0
  36. package/skills/yad-docs/templates/app/src/components/Canvas/ConnectionLine.tsx +90 -0
  37. package/skills/yad-docs/templates/app/src/components/Canvas/FlowCanvas.tsx +216 -0
  38. package/skills/yad-docs/templates/app/src/components/Canvas/SystemComponent.tsx +153 -0
  39. package/skills/yad-docs/templates/app/src/components/Controls/PlaybackBar.tsx +284 -0
  40. package/skills/yad-docs/templates/app/src/components/Controls/StepDetail.tsx +167 -0
  41. package/skills/yad-docs/templates/app/src/components/DetailPanel/HandlerLogicSnippet.tsx +41 -0
  42. package/skills/yad-docs/templates/app/src/components/DetailPanel/RequestPayloadPreview.tsx +46 -0
  43. package/skills/yad-docs/templates/app/src/components/DetailPanel/RightPanel.tsx +88 -0
  44. package/skills/yad-docs/templates/app/src/components/DetailPanel/StatusCard.tsx +76 -0
  45. package/skills/yad-docs/templates/app/src/components/DetailPanel/TriggerEventCard.tsx +45 -0
  46. package/skills/yad-docs/templates/app/src/components/DocLayout/DocPageShell.tsx +80 -0
  47. package/skills/yad-docs/templates/app/src/components/DocLayout/DocSectionCard.tsx +55 -0
  48. package/skills/yad-docs/templates/app/src/components/DocLayout/DocTableOfContents.tsx +79 -0
  49. package/skills/yad-docs/templates/app/src/components/DocLayout/RoleCard.tsx +67 -0
  50. package/skills/yad-docs/templates/app/src/components/DocSections/ApiReferenceSection.tsx +108 -0
  51. package/skills/yad-docs/templates/app/src/components/DocSections/CancelabilitySection.tsx +73 -0
  52. package/skills/yad-docs/templates/app/src/components/DocSections/CriticalRunbookSection.tsx +177 -0
  53. package/skills/yad-docs/templates/app/src/components/DocSections/DataMigrationSection.tsx +102 -0
  54. package/skills/yad-docs/templates/app/src/components/DocSections/DbSchemaSection.tsx +98 -0
  55. package/skills/yad-docs/templates/app/src/components/DocSections/DeploymentGuideSection.tsx +104 -0
  56. package/skills/yad-docs/templates/app/src/components/DocSections/DriverIntegrationSection.tsx +127 -0
  57. package/skills/yad-docs/templates/app/src/components/DocSections/ExecutiveSummarySection.tsx +69 -0
  58. package/skills/yad-docs/templates/app/src/components/DocSections/FlowOverviewSection.tsx +73 -0
  59. package/skills/yad-docs/templates/app/src/components/DocSections/FlowPathsChecklistSection.tsx +96 -0
  60. package/skills/yad-docs/templates/app/src/components/DocSections/MiddlewareChainSection.tsx +107 -0
  61. package/skills/yad-docs/templates/app/src/components/DocSections/MonitoringAlertingSection.tsx +106 -0
  62. package/skills/yad-docs/templates/app/src/components/DocSections/NotificationLocalizationSection.tsx +102 -0
  63. package/skills/yad-docs/templates/app/src/components/DocSections/PMRoadmapSection.tsx +133 -0
  64. package/skills/yad-docs/templates/app/src/components/DocSections/PerformanceTestingSection.tsx +91 -0
  65. package/skills/yad-docs/templates/app/src/components/DocSections/RiderIntegrationSection.tsx +99 -0
  66. package/skills/yad-docs/templates/app/src/components/DocSections/SecuritySection.tsx +74 -0
  67. package/skills/yad-docs/templates/app/src/components/DocSections/StatusMachineSection.tsx +90 -0
  68. package/skills/yad-docs/templates/app/src/components/DocSections/TestPlanSection.tsx +163 -0
  69. package/skills/yad-docs/templates/app/src/components/Logs/SystemLogsTerminal.tsx +126 -0
  70. package/skills/yad-docs/templates/app/src/components/Navigation/TopNavBar.tsx +90 -0
  71. package/skills/yad-docs/templates/app/src/components/Reference/BullMQJobsList.tsx +60 -0
  72. package/skills/yad-docs/templates/app/src/components/Reference/DecisionTreeView.tsx +49 -0
  73. package/skills/yad-docs/templates/app/src/components/Reference/DeeplinkActionsChips.tsx +69 -0
  74. package/skills/yad-docs/templates/app/src/components/Reference/DriverUIStatesTable.tsx +61 -0
  75. package/skills/yad-docs/templates/app/src/components/Reference/FeatureFlagMatrix.tsx +73 -0
  76. package/skills/yad-docs/templates/app/src/components/Reference/RiderUIStatesTable.tsx +61 -0
  77. package/skills/yad-docs/templates/app/src/components/Reference/RulesLegendPanel.tsx +217 -0
  78. package/skills/yad-docs/templates/app/src/components/Reference/StakeholderToggle.tsx +41 -0
  79. package/skills/yad-docs/templates/app/src/components/Reference/TroubleshootingSection.tsx +93 -0
  80. package/skills/yad-docs/templates/app/src/components/Sidebar/PathSelector.tsx +148 -0
  81. package/skills/yad-docs/templates/app/src/components/Sidebar/SidebarFooter.tsx +40 -0
  82. package/skills/yad-docs/templates/app/src/components/Sidebar/StepList.tsx +234 -0
  83. package/skills/yad-docs/templates/app/src/components/shared/Badge.tsx +28 -0
  84. package/skills/yad-docs/templates/app/src/components/shared/CommandPalette.tsx +213 -0
  85. package/skills/yad-docs/templates/app/src/components/shared/Icon.tsx +21 -0
  86. package/skills/yad-docs/templates/app/src/components/shared/Tooltip.tsx +42 -0
  87. package/skills/yad-docs/templates/app/src/data/components.ts +74 -0
  88. package/skills/yad-docs/templates/app/src/data/docSections.ts +231 -0
  89. package/skills/yad-docs/templates/app/src/data/paths.ts +2319 -0
  90. package/skills/yad-docs/templates/app/src/data/referenceData.ts +392 -0
  91. package/skills/yad-docs/templates/app/src/data/roles.ts +145 -0
  92. package/skills/yad-docs/templates/app/src/data/types.ts +79 -0
  93. package/skills/yad-docs/templates/app/src/hooks/useAnimationQueue.ts +41 -0
  94. package/skills/yad-docs/templates/app/src/hooks/usePlayback.ts +100 -0
  95. package/skills/yad-docs/templates/app/src/hooks/useStakeholderFilter.ts +10 -0
  96. package/skills/yad-docs/templates/app/src/index.css +121 -0
  97. package/skills/yad-docs/templates/app/src/main.tsx +13 -0
  98. package/skills/yad-docs/templates/app/src/pages/RoleSelectPage.tsx +34 -0
  99. package/skills/yad-docs/templates/app/src/pages/StakeholderDocPage.tsx +98 -0
  100. package/skills/yad-docs/templates/app/src/pages/SubPathDetailPage.tsx +282 -0
  101. package/skills/yad-docs/templates/app/src/store/useAuthStore.ts +42 -0
  102. package/skills/yad-docs/templates/app/src/store/useFlowStore.ts +197 -0
  103. package/skills/yad-docs/templates/app/src/utils/iconMap.ts +46 -0
  104. package/skills/yad-docs/templates/app/tsconfig.app.json +28 -0
  105. package/skills/yad-docs/templates/app/tsconfig.json +7 -0
  106. package/skills/yad-docs/templates/app/tsconfig.node.json +26 -0
  107. package/skills/yad-docs/templates/app/vite.config.ts +10 -0
  108. package/skills/yad-docs-overview/SKILL.md +129 -0
  109. package/skills/yad-docs-overview/references/pipeline-model.md +102 -0
  110. package/skills/yad-docs-sync/SKILL.md +99 -0
  111. package/skills/yad-docs-sync/references/staleness.md +81 -0
  112. package/skills/yad-engineer-review/SKILL.md +86 -0
  113. package/skills/{yad-ship → yad-engineer-review}/references/ship-and-record.md +2 -2
  114. package/skills/{yad-ship → yad-engineer-review}/templates/.coderabbit.yaml +1 -1
  115. package/skills/yad-epic/references/state-schema.md +1 -1
  116. package/skills/yad-implement/SKILL.md +1 -1
  117. package/skills/yad-implement/references/implement-conventions.md +1 -1
  118. package/skills/yad-open-pr/SKILL.md +72 -0
  119. package/skills/yad-pr-template/templates/checks/pr-template.sh +62 -0
  120. package/skills/yad-pr-template/templates/checks/pr-title.sh +51 -0
  121. package/skills/yad-run/SKILL.md +2 -2
  122. package/skills/yad-run/references/run-loop.md +4 -4
  123. package/skills/yad-ship/SKILL.md +44 -66
  124. package/skills/yad-spec/SKILL.md +1 -1
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 22 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',
@@ -29,7 +33,10 @@ export const SKILLS = [
29
33
  'yad-pr-template',
30
34
  'yad-review-comments',
31
35
  'yad-hub-bridge',
36
+ 'yad-commit',
37
+ 'yad-open-pr',
32
38
  'yad-ship',
39
+ 'yad-engineer-review',
33
40
  'yad-backfill',
34
41
  'yad-run',
35
42
  'yad-review-gate',
@@ -51,7 +58,11 @@ export const LEGACY_SKILLS = {
51
58
  'yad-pr-template': 'sdlc-pr-template',
52
59
  'yad-review-comments': 'sdlc-review-comments',
53
60
  'yad-hub-bridge': 'sdlc-hub-bridge',
54
- 'yad-ship': 'sdlc-ship',
61
+ // Step E ("ship") was renamed to yad-engineer-review (the yad-ship name now belongs to the new
62
+ // commit+open-PR combined skill). Pre-2.0 installs carry sdlc-ship → migrate it to yad-engineer-review.
63
+ // A 2.x install carrying yad-ship-as-Step-E is simply overwritten with the new combined content on
64
+ // `yad update` (same name, fresh copy), and yad-engineer-review is installed as a new skill.
65
+ 'yad-engineer-review': 'sdlc-ship',
55
66
  'yad-backfill': 'sdlc-backfill',
56
67
  'yad-run': 'sdlc-run',
57
68
  'yad-review-gate': 'sdlc-review-gate',
@@ -114,6 +125,7 @@ export const PROJECT_FILES = {
114
125
  designConfig: '.sdlc/design.json',
115
126
  testingConfig: '.sdlc/testing.json',
116
127
  learningConfig: '.sdlc/learning.json',
128
+ docsConfig: '.sdlc/docs.json',
117
129
  version: '.sdlc/cli-version.json',
118
130
  };
119
131
 
@@ -153,7 +165,10 @@ export const REPO_WIRING = {
153
165
  { src: 'skills/yad-checks/templates/checks/contract-check.sh', dest: 'checks/contract-check.sh', exec: true },
154
166
  { src: 'skills/yad-checks/templates/checks/build-test-lint.sh', dest: 'checks/build-test-lint.sh', exec: true },
155
167
  { src: 'skills/yad-checks/templates/checks/verified-commits.sh', dest: 'checks/verified-commits.sh', exec: true },
168
+ { src: 'skills/yad-checks/templates/checks/commit-message.sh', dest: 'checks/commit-message.sh', exec: true },
156
169
  { src: 'skills/yad-pr-template/templates/checks/risk-route.sh', dest: 'checks/risk-route.sh', exec: true },
170
+ { src: 'skills/yad-pr-template/templates/checks/pr-title.sh', dest: 'checks/pr-title.sh', exec: true },
171
+ { src: 'skills/yad-pr-template/templates/checks/pr-template.sh', dest: 'checks/pr-template.sh', exec: true },
157
172
  ],
158
173
  github: [
159
174
  { src: 'skills/yad-checks/templates/github/yad-checks.yml', dest: '.github/workflows/yad-checks.yml' },
@@ -179,13 +194,19 @@ export const wiringFor = (platform) => [
179
194
  export const HUB_WIRING = {
180
195
  common: [
181
196
  { src: 'skills/yad-checks/templates/checks/verified-commits.sh', dest: 'checks/verified-commits.sh', exec: true },
197
+ // Pattern gates run on the hub too (profile: hub) — commit subject + PR title + PR body.
198
+ { src: 'skills/yad-checks/templates/checks/commit-message.sh', dest: 'checks/commit-message.sh', exec: true },
199
+ { src: 'skills/yad-pr-template/templates/checks/pr-title.sh', dest: 'checks/pr-title.sh', exec: true },
200
+ { src: 'skills/yad-pr-template/templates/checks/pr-template.sh', dest: 'checks/pr-template.sh', exec: true },
182
201
  ],
183
202
  github: [
184
203
  { src: 'skills/yad-hub-bridge/templates/github/yad-gate-sync.yml', dest: '.github/workflows/yad-gate-sync.yml' },
185
204
  { src: 'skills/yad-checks/templates/github/yad-verified-commits.yml', dest: '.github/workflows/yad-verified-commits.yml' },
205
+ { src: 'skills/yad-checks/templates/github/yad-hub-checks.yml', dest: '.github/workflows/yad-hub-checks.yml' },
186
206
  ],
187
207
  gitlab: [
188
208
  { src: 'skills/yad-hub-bridge/templates/gitlab/yad-gate-sync.gitlab-ci.yml', dest: '.gitlab/ci/yad-gate-sync.yml' },
189
209
  { src: 'skills/yad-checks/templates/gitlab/yad-verified-commits.gitlab-ci.yml', dest: '.gitlab/ci/yad-verified-commits.yml' },
210
+ { src: 'skills/yad-checks/templates/gitlab/yad-hub-checks.gitlab-ci.yml', dest: '.gitlab/ci/yad-hub-checks.yml' },
190
211
  ],
191
212
  };
package/cli/ship.mjs ADDED
@@ -0,0 +1,37 @@
1
+ // `yad ship` — commit the staged atomic change AND open its task PR/MR, in one step (build half).
2
+ // A thin orchestration over the two existing engines: `yad commit` then `yad open-pr`. It holds no
3
+ // commit/PR logic of its own — it reuses runCommit/runOpenPr so the conventions stay in one place.
4
+ // The PR step runs ONLY when the commit actually lands: a failed commit, a tripped atomic guard, or a
5
+ // --dry-run all stop before anything is pushed.
6
+ import { c, log, info } from './lib.mjs';
7
+ import { runCommit } from './commit.mjs';
8
+ import { runOpenPr } from './openpr.mjs';
9
+
10
+ export async function runShip(root, opts = {}) {
11
+ log(c.bold('\nyad ship'));
12
+
13
+ // Step 1 — commit by convention (atomic guard, trailers, AI co-author). On --dry-run this just
14
+ // prints the message and returns without committing; we then stop (nothing to open a PR for).
15
+ const committed = await runCommit(root, {
16
+ type: opts.type, message: opts.message, task: opts.task, ai: opts.ai,
17
+ contractChange: opts.contractChange, dryRun: opts.dryRun, force: opts.force,
18
+ });
19
+
20
+ if (opts.dryRun) { info('dry run — not committed, PR/MR not opened'); return committed; }
21
+
22
+ // runCommit signals failure by setting process.exitCode (not by throwing) — honour it and abort the
23
+ // PR step so we never open a PR for a branch whose commit did not land.
24
+ if (process.exitCode) { info('commit did not land — skipping open-pr'); return committed; }
25
+
26
+ // Step 2 — open the task PR/MR from the committed template (pushes the branch, auto-assigns the
27
+ // repo-scoped roster). Pass ONLY an explicit --title: when omitted, runOpenPr derives the title
28
+ // from the committed subject (the full `<type>: …` form), which the pr-title gate expects — passing
29
+ // the bare --message here would override that with a type-less title and fail the gate.
30
+ const opened = await runOpenPr(root, {
31
+ repo: opts.repo, platform: opts.platform, base: opts.base,
32
+ title: opts.title, task: opts.task,
33
+ risk: opts.risk, contractChange: opts.contractChange,
34
+ });
35
+
36
+ return { ...committed, ...opened };
37
+ }
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 22 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>
@@ -344,7 +344,7 @@
344
344
  <div class="arrow-v">▼</div>
345
345
  <div class="node plain"><strong>Open PR/MR</strong><small>from the wired template; risk routing picks the required reviewers</small></div>
346
346
  <div class="arrow-v">▼</div>
347
- <div class="node plain"><strong>yad-ship: AI review</strong><small>CodeRabbit first pass — advisory only, never the authority</small></div>
347
+ <div class="node plain"><strong>yad-engineer-review: AI review</strong><small>CodeRabbit first pass — advisory only, never the authority</small></div>
348
348
  <div class="arrow-v">▼</div>
349
349
  <div class="node locked"><strong>Engineer review</strong><small>a human reads the diff against the spec — <strong>never automated</strong></small></div>
350
350
  <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 22 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>
@@ -509,6 +509,9 @@ each code repo/
509
509
  <div class="term"><strong>Contract-check</strong><span class="badge b-build">build</span><br>CI gate #2: a diff that changes the contract surface without declaring <code>Contract-Change: yes</code> <em>and</em> an updated, re-locked contract <strong>fails</strong> — routing the change back up to the architecture gate. This is how the contract stays honest.</div>
510
510
  <div class="term"><strong>Build/test/lint gate</strong><span class="badge b-build">build</span><br>CI gate #3: the ordinary safety net — the project must build, its tests must pass, lint must be clean.</div>
511
511
  <div class="term"><strong>Verified-commits gate</strong><span class="badge b-build">build</span><br>CI gate #4: every commit must carry a platform-verified signature and be authored by a roster-known email — rejecting unverified commits from unknown users, on the hub and every repo.</div>
512
+ <div class="term"><strong>Commit-message gate</strong><span class="badge b-build">build</span><br>CI gate #5: every commit subject must follow Conventional Commits (<code>&lt;type&gt;: …</code>, no trailing period) and its trailers must be ordered <code>Task → Contract-Change → Co-Authored-By</code>. Profile-aware (code|hub).</div>
513
+ <div class="term"><strong>PR-title gate</strong><span class="badge b-build">build</span><br>CI gate #6: the PR/MR title must follow the convention — a Conventional-Commits subject on code repos, a <code>review: &lt;artifact&gt; (EP-&lt;slug&gt;)</code> title on the hub.</div>
514
+ <div class="term"><strong>PR-template gate</strong><span class="badge b-build">build</span><br>CI gate #7: the PR/MR body must actually use the committed template (its required sections present + the risk field filled), so the review and routing have their inputs. Profile-aware (code|hub).</div>
512
515
  <div class="term"><strong><code>Task:</code> trailer</strong><span class="badge b-build">build</span><br>A line at the end of every commit message naming the story + task it implements (e.g. <code>Task: EP-istifta-inquiries-S01-T03</code>). What the spec-link check verifies.</div>
513
516
  <div class="term"><strong><code>Contract-Change: yes</code> trailer</strong><span class="badge b-build">build</span><br>A commit trailer added <em>only</em> when the diff deliberately touches the locked contract surface — paired with an updated, re-locked contract, or the contract-check fails.</div>
514
517
  <div class="term"><strong>Conventional Commits</strong><span class="badge b-build">build</span><br>The commit/PR title convention used everywhere: <code>feat: …</code>, <code>fix: …</code> (lowercase after the type). Merged <code>feat:</code>/<code>fix:</code> commits also drive automated releases of the CLI itself.</div>
@@ -538,7 +541,7 @@ each code repo/
538
541
  <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>
539
542
  <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>
540
543
  <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>
541
- <div class="term"><strong>Skill</strong><span class="badge b-tool">tools</span><br>One of the 22 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>
544
+ <div class="term"><strong>Skill</strong><span class="badge b-tool">tools</span><br>One of the 25 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>
542
545
  <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>
543
546
 
544
547
  <h3>People &amp; AI roles</h3>
@@ -681,7 +684,7 @@ each code repo/
681
684
 
682
685
  <!-- ======================================================= -->
683
686
  <section id="skills">
684
- <h2>6. The 22 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>
685
688
  <p>A <strong>skill</strong> is an agent you invoke <em>by name</em> in your AI IDE — you just ask in
686
689
  plain words, e.g. <em>“run <code>yad-epic</code>”</em>, adding any inputs the skill needs
687
690
  (<code>repo: backend</code>, <code>story: EP-…-S01</code>, <code>action: wire</code>, …).
@@ -706,7 +709,10 @@ each code repo/
706
709
  <tr><td><a href="#sk-impl"><code>yad-implement</code></a></td><td>Build B</td><td>Implement one atomic task as a small branch.</td></tr>
707
710
  <tr><td><a href="#sk-checks"><code>yad-checks</code></a></td><td>Build C</td><td>Wire / run the CI gates.</td></tr>
708
711
  <tr><td><a href="#sk-prt"><code>yad-pr-template</code></a></td><td>Build D</td><td>Install the PR/MR template + risk routing.</td></tr>
709
- <tr><td><a href="#sk-ship"><code>yad-ship</code></a></td><td>Build E</td><td>AI review engineer review ship + record.</td></tr>
712
+ <tr><td><a href="#sk-commit"><code>yad-commit</code></a></td><td>Build helper</td><td>Commit one staged atomic change by the conventions.</td></tr>
713
+ <tr><td><a href="#sk-openpr"><code>yad-open-pr</code></a></td><td>Build helper</td><td>Open a code-repo task PR/MR from the committed template.</td></tr>
714
+ <tr><td><a href="#sk-ship"><code>yad-ship</code></a></td><td>Build helper</td><td>Commit <strong>and</strong> open the task PR/MR in one step.</td></tr>
715
+ <tr><td><a href="#sk-engineer-review"><code>yad-engineer-review</code></a></td><td>Build E</td><td>AI review → engineer review → merge + record.</td></tr>
710
716
  <tr><td><a href="#sk-backfill"><code>yad-backfill</code></a></td><td>Build G</td><td>Spec already-built / legacy code before changing it.</td></tr>
711
717
  <tr><td><a href="#sk-run"><code>yad-run</code></a></td><td>Automation</td><td>Drive the back half on the dials; set dials; kill switch.</td></tr>
712
718
  <tr><td><a href="#sk-status"><code>yad-status</code></a></td><td>Anytime</td><td>Read-only view: where everything is and what’s blocking.</td></tr>
@@ -876,7 +882,7 @@ yad open-pr --repo backend</code></pre>
876
882
 
877
883
  <div class="skillcard" id="sk-checks">
878
884
  <h4><code>yad-checks</code> — Build Step C</h4>
879
- <div class="row"><span class="lbl lbl-what">What</span> The production-safety CI gates: <strong>spec-link</strong> (every change links a real story/spec), <strong>contract-check</strong> (a contract-surface diff without a re-locked contract FAILS), <strong>build/test/lint</strong>, and <strong>verified-commits</strong> (signed commits from roster-known authors). <code>wire</code> installs them into the repo’s CI (merging with any existing CI, never clobbering); <code>run</code> executes them right now, locally.</div>
885
+ <div class="row"><span class="lbl lbl-what">What</span> The production-safety CI gates: <strong>spec-link</strong> (every change links a real story/spec), <strong>contract-check</strong> (a contract-surface diff without a re-locked contract FAILS), <strong>build/test/lint</strong>, <strong>verified-commits</strong> (signed commits from roster-known authors), and the <strong>pattern gates</strong> — <strong>commit-message</strong> (Conventional subject + trailer order), <strong>pr-title</strong>, and <strong>pr-template</strong> (the PR/MR body uses the template). The pattern gates are profile-aware (<code>code</code>|<code>hub</code>), so they run on code repos <em>and</em> the product hub. <code>wire</code> installs them into the repo’s CI (merging with any existing CI, never clobbering); <code>run</code> executes them right now, locally.</div>
880
886
  <div class="row"><span class="lbl lbl-how">How</span></div>
881
887
  <pre><code>yad-checks repo: backend action: wire # once, during setup
882
888
  yad-checks repo: backend action: run # before opening each PR
@@ -886,7 +892,7 @@ yad-checks repo: hub action: wire # hub-flavored gates for the product hub
886
892
 
887
893
  <div class="skillcard" id="sk-prt">
888
894
  <h4><code>yad-pr-template</code> — Build Step D</h4>
889
- <div class="row"><span class="lbl lbl-what">What</span> Detects the repo’s platform and commits the matching PR/MR template, which carries an <strong>Impact &amp; Risk block</strong>. A <code>high</code> risk level — or a touched contract/auth/payments surface — routes the review to the domain owners. <code>route</code> reads a PR body and prints exactly who must review it.</div>
895
+ <div class="row"><span class="lbl lbl-what">What</span> Detects the repo’s platform and commits the matching PR/MR template, which carries an <strong>Impact &amp; Risk block</strong>. A <code>high</code> risk level — or a touched contract/auth/payments surface — routes the review to the domain owners. <code>route</code> reads a PR body and prints exactly who must review it. It also ships the <code>pr-title.sh</code> / <code>pr-template.sh</code> gate scripts that <code>yad-checks</code> runs.</div>
890
896
  <div class="row"><span class="lbl lbl-how">How</span></div>
891
897
  <pre><code>yad-pr-template repo: backend action: wire # once, during setup
892
898
  yad-pr-template repo: backend action: route # who must review this PR?
@@ -894,13 +900,38 @@ yad-pr-template repo: hub action: wire # the hub's front-half review temp
894
900
  <div class="row"><span class="lbl lbl-when">When</span> <code>wire</code> once per repo (and the hub) at setup; <code>route</code> whenever you’ve opened a PR and want the required reviewer list.</div>
895
901
  </div>
896
902
 
903
+ <div class="skillcard" id="sk-commit">
904
+ <h4><code>yad-commit</code> — Build helper</h4>
905
+ <div class="row"><span class="lbl lbl-what">What</span> Commits <strong>one staged atomic change</strong> by the conventions: a Conventional-Commits subject, the fixed trailer block (<code>Task → Contract-Change → Co-Authored-By</code>), and a ≤3-file atomic guard. The human git author owns the commit; an assisting AI is recorded only as a <code>Co-Authored-By</code> footer chosen per-commit with <code>--ai</code> (default <code>none</code>, human-only).</div>
906
+ <div class="row"><span class="lbl lbl-how">How</span> From the repo holding the staged change:</div>
907
+ <pre><code>yad commit --type feat -m "add the inquiry endpoint" --ai claude
908
+ yad commit --type fix -m "handle null user" --dry-run # preview only</code></pre>
909
+ <div class="row"><span class="lbl lbl-when">When</span> Any time you have a staged atomic change to commit by the rules — on a code repo or the hub.</div>
910
+ </div>
911
+
912
+ <div class="skillcard" id="sk-openpr">
913
+ <h4><code>yad-open-pr</code> — Build helper</h4>
914
+ <div class="row"><span class="lbl lbl-what">What</span> Opens a code-repo <strong>task PR/MR</strong> from the committed platform template: detects GitHub/GitLab, pushes the task branch, and creates the PR/MR with the body prefilled and the title defaulting to the commit subject. Auto-assigns from the hub roster — assignee = the committer, reviewers = the repo’s reviewers + domain-owners. It never merges.</div>
915
+ <div class="row"><span class="lbl lbl-how">How</span> On the task branch:</div>
916
+ <pre><code>yad open-pr --repo backend --risk low</code></pre>
917
+ <div class="row"><span class="lbl lbl-when">When</span> After committing a task, to raise its PR/MR for the check gates + engineer review.</div>
918
+ </div>
919
+
897
920
  <div class="skillcard" id="sk-ship">
898
- <h4><code>yad-ship</code> — Build Step E</h4>
921
+ <h4><code>yad-ship</code> — Build helper</h4>
922
+ <div class="row"><span class="lbl lbl-what">What</span> Does the two routine build-half hand-actions for one task in <strong>one step</strong>: commit by convention, then open the task PR/MR. A thin wrapper over <code>yad-commit</code> then <code>yad-open-pr</code> — the PR step runs <em>only</em> if the commit lands (a failed commit, tripped guard, or <code>--dry-run</code> stops before pushing). It never merges.</div>
923
+ <div class="row"><span class="lbl lbl-how">How</span> On the task branch, with the change staged:</div>
924
+ <pre><code>yad ship --type feat -m "add the inquiry endpoint" --ai claude --repo backend --risk low</code></pre>
925
+ <div class="row"><span class="lbl lbl-when">When</span> The common case — turn an implemented, staged task into a reviewable PR/MR in one command.</div>
926
+ </div>
927
+
928
+ <div class="skillcard" id="sk-engineer-review">
929
+ <h4><code>yad-engineer-review</code> — Build Step E</h4>
899
930
  <div class="row"><span class="lbl lbl-what">What</span> The last mile of a task’s PR: <code>ai-review</code> triggers the advisory AI pass (CodeRabbit — a second set of eyes, never the authority); <code>approve</code> records the <strong>human engineer approval</strong> (owner + 1 reviewer, same escalation); <code>ship</code> merges and records the ship in <code>build-log.json</code>, moving the story to <code>in-build</code> → <code>shipped</code> so the epic → story → task → PR chain stays traceable.</div>
900
931
  <div class="row"><span class="lbl lbl-how">How</span> Against the task’s PR, in order:</div>
901
- <pre><code>yad-ship story: EP-istifta-inquiries-S01 task: T01 repo: backend action: ai-review
902
- yad-ship … action: approve # the human engineer's approval
903
- yad-ship … action: ship # merge + record in build-log.json</code></pre>
932
+ <pre><code>yad-engineer-review story: EP-istifta-inquiries-S01 task: T01 repo: backend action: ai-review
933
+ yad-engineer-review … action: approve # the human engineer's approval
934
+ yad-engineer-review … action: ship # merge + record in build-log.json</code></pre>
904
935
  <div class="row"><span class="lbl lbl-when">When</span> Once a task’s PR is open and the check gates are green. The engineer approval here is <strong>permanently human</strong> — no dial can ever automate it.</div>
905
936
  </div>
906
937
 
@@ -948,7 +979,7 @@ yad-status EP-istifta-inquiries # one epic in detail</code></pre>
948
979
 
949
980
  <div class="skillcard">
950
981
  <h4><code>npx yadflow setup</code></h4>
951
- <div class="row"><span class="lbl lbl-what">What</span> The guided first-run wizard. Ten steps: preflight (git/node check), install all 22 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>
952
983
  <div class="row"><span class="lbl lbl-how">How</span></div>
953
984
  <pre><code>cd &lt;product-hub-repo&gt;
954
985
  npx yadflow setup</code></pre>
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "yadflow",
3
- "version": "2.5.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, repo). A BMAD module + 22 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",
@@ -73,8 +73,13 @@ build:
73
73
  - { id: cursor, name: "Cursor", email: "noreply@cursor.com" }
74
74
  - { id: coderabbit, name: "CodeRabbit", email: "noreply@coderabbit.ai" }
75
75
  - { id: none, name: "(no AI assistance)", email: "" }
76
- # Step C (yad-checks) — the three CI gates that must pass before merge. CI-agnostic bash in checks/.
76
+ # Step C (yad-checks) — the CI gates that must pass before merge. CI-agnostic bash in checks/.
77
77
  gates: [spec-link, contract-check, build-test-lint]
78
+ # Pattern gates — commit subject + PR/MR title + PR/MR template-usage. Profile-aware (code|hub): code
79
+ # repos validate the Conventional-Commits / task-PR conventions; the product hub validates its
80
+ # artifact-review conventions (review/EP-<slug>/<artifact>, the hub PR template). verified-commits is
81
+ # the separate security gate (signature + roster-known author). All blocking in CI; both repos + hub.
82
+ pattern_gates: [commit-message, pr-title, pr-template]
78
83
  ci_platforms: [github, gitlab] # both wired from skills/yad-checks/templates/; GitHub is this product repo's platform
79
84
  contract_surface_glob: "specs/*/contracts/**" # the repo's quoted contract slice; a diff here needs Contract-Change + a re-locked contract
80
85
  # Step D (yad-pr-template) — platform-matched PR/MR template + risk routing.
@@ -85,7 +90,7 @@ build:
85
90
  github: ".github/pull_request_template.md"
86
91
  gitlab: ".gitlab/merge_request_templates/Default.md"
87
92
  risk_levels: [low, medium, high] # high (or a contract/auth/payments surface) routes to domain owners (yad-review-gate escalation)
88
- # Step E (yad-ship) — AI review (advisory) + engineer review (the human gate) + ship.
93
+ # Step E (yad-engineer-review) — AI review (advisory) + engineer review (the human gate) + merge.
89
94
  ai_review: coderabbit # advisory first pass; never the authority (.coderabbit.yaml)
90
95
  build_log: "epics/EP-<slug>/.sdlc/build-log.json" # append-only ship ledger (back-half analogue of approvals.json)
91
96
  story_build_states: [in-build, shipped] # in-build = some tasks shipped; shipped = all tasks in tasks.md shipped
@@ -171,6 +176,25 @@ learning:
171
176
  # tokens in the registry (kb name + sources are plain references, never credentials). Opt-in, never a gate.
172
177
  auth: user
173
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
+
174
198
  # Hub platform + front-half review bridge (yad-connect-repos `detect-hub`; yad-review-gate + yad-hub-bridge).
175
199
  # The product hub is itself a git repo on a platform. With the bridge enabled, the front-half review/
176
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-ship 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
 
@@ -13,11 +13,18 @@ SDLC Workflow,yad-connect-learning,Connect Learning Tool,CL,"Setup/maintenance:
13
13
  SDLC Workflow,yad-learn,Learn (Tutor),LN,"Cross-cutting learning layer: at ANY SDLC stage a team member can ask to learn a concept and be tutored in the context of what the team is building. Routes to the connected learning tool (.sdlc/learning.json, DeepTutor-first) grounded in the project knowledge base, or degrades to harness-native tutoring (the harness model reading the artifacts). Renders a tutorial artifact and appends to a per-member learning ledger kept LOCAL-ONLY (gitignored, never committed/pushed to the hub or any code repo) so it stays a private, personal skills log (yad-status rolls up the local records). Purely opt-in — never blocks a gate, never touches epic state, approvals, or the contract lock.",,{concept: <idea>} {context: <focus>} {epic: EP-<slug>} {stage: <sdlc stage>} {member: <who>} {mode: explain|deep|quiz} {action: learn|list|complete},,,,,false,epics/EP-<slug>/,learning-records.json learning/<member>--<concept>.md
14
14
  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
15
15
  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
16
- 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
17
- SDLC Workflow,yad-pr-template,PR/MR Template,PT,"Build-half Step D: detect a code repo's platform and commit the matching PR/MR template (.github/pull_request_template.md or .gitlab/merge_request_templates/Default.md) with an Impact & Risk block. A high risk level (or a touched contract/auth/payments surface) routes the review to domain owners — the same escalation yad-review-gate applies. risk-route.sh prints the required reviewers from a PR body. Never auto-advances.",,{repo: <one of an epic's repos>} {action: wire|route} {body: PR description file},3-build,yad-checks,,false,demo-repos/<repo>/,.github/pull_request_template.md .gitlab/merge_request_templates/Default.md checks/risk-route.sh
16
+ SDLC Workflow,yad-checks,Check Gates,CK,"Build-half Step C: wire and run the production-safety CI gates on a code repo (and product hub) — 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), build/test/lint, verified-commits (signed + roster-known authors), and the pattern gates commit-message / pr-title / pr-template (profile-aware code|hub). 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 | hub>} {action: wire|run} {base: target branch},3-build,yad-implement,,false,demo-repos/<repo>/,checks/*.sh .github/workflows/yad-checks.yml .gitlab-ci.yml
17
+ SDLC Workflow,yad-pr-template,PR/MR Template,PT,"Build-half Step D: detect a code repo's platform and commit the matching PR/MR template (.github/pull_request_template.md or .gitlab/merge_request_templates/Default.md) with an Impact & Risk block. A high risk level (or a touched contract/auth/payments surface) routes the review to domain owners — the same escalation yad-review-gate applies. Ships the routing helper risk-route.sh plus the pattern-gate scripts pr-title.sh and pr-template.sh (used by yad-checks). Never auto-advances.",,{repo: <one of an epic's repos | hub>} {action: wire|route} {body: PR description file},3-build,yad-checks,,false,demo-repos/<repo>/,.github/pull_request_template.md .gitlab/merge_request_templates/Default.md checks/risk-route.sh checks/pr-title.sh checks/pr-template.sh
18
+ SDLC Workflow,yad-commit,Commit by Convention,CM,"Build-half helper: commit ONE staged atomic change by the conventions — a Conventional-Commits subject, the fixed trailer block (Task -> Contract-Change -> Co-Authored-By), and the <=3-file atomic guard. The human git author owns the commit; an assisting AI is recorded only as a Co-Authored-By footer chosen per-commit with --ai (claude|copilot|cursor|coderabbit|none, default none). Drives the yad commit CLI. Never auto-advances.",,{type: feat|fix|...} {message: subject} {ai: <tool|none>} {task: <id>} {contract-change: true|false},3-build,yad-implement,,false,<repo>/,one commit
19
+ SDLC Workflow,yad-open-pr,Open PR/MR,OP,"Build-half helper: open a code-repo task PR/MR from the committed platform template — detect GitHub/GitLab, push the task branch, create the PR/MR with the body prefilled (Summary / Story-task / Impact & Risk) and the title defaulting to the commit subject. Auto-assigns from the hub roster (assignee = committer, reviewers = repo reviewers + domain-owners); high risk / contract surface routes to domain owners (risk-route.sh). Drives the yad open-pr CLI. Never merges; never auto-advances.",,{repo: <name>} {risk: low|medium|high} {contract-change: true|false},3-build,yad-commit,,false,<repo>/,one PR/MR
20
+ SDLC Workflow,yad-ship,Commit + Open PR/MR,SP2,"Build-half helper: commit AND open the task PR/MR in one step — a thin orchestration over yad-commit then yad-open-pr. Commits the staged atomic change by the conventions, then pushes the branch and opens the PR/MR from the committed template with the roster auto-assigned. The PR step runs ONLY if the commit lands (a failed commit, tripped guard, or --dry-run stops before pushing). Drives the yad ship CLI. Never merges; never auto-advances.",,{type: feat|fix|...} {message: subject} {ai: <tool|none>} {repo: <name>} {risk: low|medium|high} {contract-change: true|false},3-build,yad-pr-template,,false,<repo>/,one commit + one PR/MR
18
21
  SDLC Workflow,yad-review-comments,Review Comment Templates,RC,"Install platform-matched PR/MR review-comment scaffolds (committed REVIEW_COMMENTS.md) into a code repo or the product hub, so reviewers leave structured, attributable feedback whose **name (role)** headers map cleanly into the SDLC file ledger. GitHub Saved Replies / GitLab comment templates are per-user, so a committed doc is the repo-level mechanism. Never auto-advances.",,{repo: <one of an epic's repos | hub>} {action: wire},3-build,,,false,<repo>/.github|.gitlab/,REVIEW_COMMENTS.md
19
22
  SDLC Workflow,yad-hub-bridge,Hub Review Bridge,HB,"The templated PR/MR bridge for the front-half review gate: when the product hub has a platform (.sdlc/hub.json), open a review PR/MR on the hub for an authored artifact, set required reviewers/labels from the routing rule, and provide the read-only gh/glab recipes yad-review-gate's sync uses to pull platform comments + approvals into the file ledger. Local-user auth, no stored tokens; file ledger stays the source of truth; degrades to file-only when no platform/CLI. Never auto-advances.",,{epic: EP-<slug>} {artifact: epic.md|architecture.md|ui-design.md|stories/} {action: open|route},1-front,yad-review-gate,yad-review-gate,false,epics/EP-<slug>/.sdlc/,hub-prs.json
20
- SDLC Workflow,yad-ship,Review & Ship,SH,"Build-half Step E: wire an advisory AI first-pass (CodeRabbit) on the PR, record the human engineer review with the same human_approve discipline as the front gates (owner + 1 reviewer, escalating to domain owners on high risk / contract / auth / payments), and on merge record the ship in epics/<epic>/.sdlc/build-log.json and update the story state. AI review is advisory, never the authority; the human owns the merge. Never auto-advances.",,{epic: EP-<slug>} {story: EP-<slug>-S0N} {task: T0N} {repo: <repo>} {action: ai-review|approve|ship},3-build,yad-pr-template,,false,epics/EP-<slug>/.sdlc/,build-log.json story-status
23
+ SDLC Workflow,yad-engineer-review,Engineer Review & Merge,ER,"Build-half Step E: wire an advisory AI first-pass (CodeRabbit) on the PR, record the human engineer review with the same human_approve discipline as the front gates (owner + 1 reviewer, escalating to domain owners on high risk / contract / auth / payments), and on merge record the ship in epics/<epic>/.sdlc/build-log.json and update the story state. AI review is advisory, never the authority; the human owns the merge. Never auto-advances.",,{epic: EP-<slug>} {story: EP-<slug>-S0N} {task: T0N} {repo: <repo>} {action: ai-review|approve|ship},3-build,yad-ship,,false,epics/EP-<slug>/.sdlc/,build-log.json story-status
21
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
22
- 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-ship,false,epics/EP-<slug>/.sdlc/,build-state/<story-id>.json trust-log.json
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
23
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
@@ -12,6 +12,9 @@ repo uses. Each reads conventions established by earlier steps — it invents no
12
12
  | contract-check | changed files under `specs/<story>/contracts/`; the `Contract-Change: yes` trailer; `link.md`'s pinned `contract-lock`; the product repo's `contract-lock.json` | `yad-architecture` (lock), `yad-spec` (slice + link), `yad-implement` (trailer) |
13
13
  | build/test/lint | the repo's `npm run lint` / `npm run build` / `npm test` | the repo |
14
14
  | verified-commits | each commit's platform signature-verification status; the author email vs `.sdlc/verified-authors` | hub roster `email` fields (`yad check --fix` generates the allowlist) |
15
+ | commit-message | each non-merge commit's subject + trailer block | `yad-commit` / `CONTRIBUTING.md` (`config.yaml build.commit_subject_style`) |
16
+ | pr-title | the PR/MR title (from the CI event payload) | `yad-pr-template` (`config.yaml build.pr_title_style`) |
17
+ | pr-template | the PR/MR body (from the CI event payload) | `yad-pr-template` (the committed PR/MR template) |
15
18
 
16
19
  ## 1. spec-link (`templates/checks/spec-link.sh`)
17
20
 
@@ -84,16 +87,54 @@ Note the deliberate split with the gate-sync bot: this gate runs on **PRs/MRs on
84
87
  not subject to it. Do **not** replace it with a platform-level "reject unsigned pushes" rule on the
85
88
  default branch — that would break the event-driven gate sync (and GitLab push rules are Premium-only).
86
89
 
90
+ ## 5. commit-message (`templates/checks/commit-message.sh`)
91
+
92
+ The commit *pattern* gate (the presence-only `Task:` check is spec-link's; this checks SHAPE). For each
93
+ non-merge commit in `<base>..HEAD`:
94
+
95
+ - **Subject** must be `<type>: <description>` with `<type>` a known Conventional-Commits type
96
+ (`feat|fix|docs|refactor|test|perf|build|ci|chore|revert` — keep in sync with `cli/manifest.mjs`
97
+ `COMMIT_TYPES`) and **no trailing period** — mirroring `cli/commit.mjs` `buildCommitMessage`.
98
+ - **Trailers**, when present, appear in the fixed order `Task → Contract-Change → Co-Authored-By`.
99
+ - Merge/squash commits (2+ parents) are skipped — their platform-generated subjects are not authored.
100
+ - **Profiles** (`--profile code|hub`): the subject rule is identical on both; the gate never requires
101
+ the `Task:` trailer (spec-link owns that on code repos; hub commits are not task-scoped).
102
+ - **Fails closed** when `<base>` can't be resolved.
103
+
104
+ ## 6. pr-title (`templates/checks/pr-title.sh`)
105
+
106
+ The PR/MR title must follow the convention for the repo kind (title passed as the arg, injected by CI
107
+ from the event payload):
108
+
109
+ - `--profile code` (default) → a Conventional-Commits subject `<type>: <description>`, no trailing
110
+ period (`config.yaml build.pr_title_style: same_as_commit_subject` — one task = one PR, the title is
111
+ the squash-merge subject).
112
+ - `--profile hub` → a front-half artifact-review title `review: <artifact> (EP-<slug>)`, the shape
113
+ `yad gate open` creates.
114
+
115
+ ## 7. pr-template (`templates/checks/pr-template.sh`)
116
+
117
+ The PR/MR body must actually USE the committed template (body passed as a file, injected by CI) — this
118
+ catches a free-form description that bypassed it:
119
+
120
+ - `--profile code` (default) → requires `## Summary`, `## Impact & Risk`, `## Checklist`, and a filled
121
+ `Risk level:` (`low|medium|high`).
122
+ - `--profile hub` → requires `## Artifact under review`, `## Impact & Risk (front-half)`, `## Checklist`,
123
+ and a `Risk tags:` line.
124
+
87
125
  ## CI wiring (both platforms)
88
126
 
89
127
  The gates run identically under either CI; the config just invokes the scripts with the PR/MR base.
90
128
 
91
129
  - **GitHub Actions** — `templates/github/yad-checks.yml` → `.github/workflows/yad-checks.yml`. The
92
130
  jobs run on `pull_request` with `fetch-depth: 0`, passing `origin/${{ github.base_ref }}` as base
93
- (verified-commits also gets a read-only `GH_TOKEN` for the Verified-badge lookup).
131
+ (verified-commits also gets a read-only `GH_TOKEN` for the Verified-badge lookup). The pattern jobs
132
+ read the title/body from the event payload: `pr-title` takes `${{ github.event.pull_request.title }}`
133
+ and `pr-template` writes `${{ github.event.pull_request.body }}` to a temp file. All `--profile code`.
94
134
  - **GitLab CI** — `templates/gitlab/yad-checks.gitlab-ci.yml` → `.gitlab/ci/yad-checks.yml`, pulled in
95
135
  by the root `.gitlab-ci.yml`'s `include:`. The jobs run on `merge_request_event` with `GIT_DEPTH: 0`,
96
- passing `origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME`.
136
+ passing `origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME`; the pattern jobs read `$CI_MERGE_REQUEST_TITLE`
137
+ and `$CI_MERGE_REQUEST_DESCRIPTION`. All `--profile code`.
97
138
 
98
139
  ## Sync with existing CI (merge, never clobber)
99
140
 
@@ -148,6 +189,14 @@ plus a standalone workflow (`templates/github/yad-verified-commits.yml` →
148
189
  its one include line) whenever `.sdlc/hub.json` has a platform with the bridge enabled. So the
149
190
  front-half review PRs are held to the same rule as code-repo PRs: signed, known authors only.
150
191
 
192
+ The hub **also** runs the three pattern gates (`commit-message`, `pr-title`, `pr-template`) with
193
+ `--profile hub`, so the front-half review PRs follow the hub conventions — Conventional-Commits commit
194
+ subjects, a `review: <artifact> (EP-<slug>)` title, and a body that uses the hub artifact-review
195
+ template. `yad check --fix` installs the same `checks/*.sh` scripts plus a standalone hub workflow
196
+ (`templates/github/yad-hub-checks.yml` → `.github/workflows/yad-hub-checks.yml`, or the GitLab fragment
197
+ `templates/gitlab/yad-hub-checks.gitlab-ci.yml` → `.gitlab/ci/yad-hub-checks.yml` + its one include
198
+ line). Code repos run the same three with `--profile code` inside the main `yad-checks` workflow.
199
+
151
200
  ## Running by hand (Phase 3 is manual)
152
201
 
153
202
  From inside the code repo, against the PR/MR base (e.g. `master`):
@@ -157,6 +206,13 @@ bash checks/spec-link.sh master
157
206
  bash checks/contract-check.sh master
158
207
  bash checks/build-test-lint.sh
159
208
  bash checks/verified-commits.sh master # uses your own gh/glab auth for the signature lookup
209
+ bash checks/commit-message.sh --profile code master
210
+ # pr-title / pr-template validate the actual PR/MR metadata (in CI they come from the event payload).
211
+ # By hand, pass the title, and a FILE holding the PR/MR description (the rendered/filled body, not the
212
+ # template source):
213
+ bash checks/pr-title.sh --profile code "feat: add the inquiry endpoint"
214
+ # save the PR/MR description to a file first (e.g. `gh pr view <n> --json body -q .body > /tmp/pr-body.md`)
215
+ bash checks/pr-template.sh --profile code /tmp/pr-body.md
160
216
  ```
161
217
 
162
218
  ## Proven behavior (demo: `demo-repos/backend`, story EP-istifta-inquiries-S01)
@@ -0,0 +1,82 @@
1
+ #!/usr/bin/env bash
2
+ # commit-message gate.
3
+ # Every non-merge commit in the range under review must follow the project commit convention
4
+ # (CONTRIBUTING.md / config.yaml build — mirrors cli/commit.mjs buildCommitMessage):
5
+ # - subject is "<type>: <description>" where <type> is a known Conventional-Commits type,
6
+ # - the subject does NOT end with a period,
7
+ # - any trailers appear in the fixed order Task -> Contract-Change -> Co-Authored-By.
8
+ # Keep the type list in sync with cli/manifest.mjs COMMIT_TYPES and config.yaml build.commit_subject_style.
9
+ #
10
+ # Profiles (--profile code|hub, default code): the subject rule is identical on the product hub and on
11
+ # code repos (both follow CONTRIBUTING). The Task trailer is NOT required here (the spec-link gate owns
12
+ # that on code repos; hub commits are not task-scoped) — this gate only checks SHAPE and ORDER.
13
+ #
14
+ # Merge/squash commits (2+ parents) are skipped: their platform-generated subjects are not authored.
15
+ set -euo pipefail
16
+
17
+ PROFILE=code
18
+ ARGS=()
19
+ while [ $# -gt 0 ]; do
20
+ case "$1" in
21
+ --profile) PROFILE="${2:-code}"; shift 2 ;;
22
+ --profile=*) PROFILE="${1#*=}"; shift ;;
23
+ *) ARGS+=("$1"); shift ;;
24
+ esac
25
+ done
26
+ case "$PROFILE" in code|hub) ;; *) echo "FAIL [commit-message]: unknown --profile '$PROFILE' (code|hub)."; exit 1 ;; esac
27
+
28
+ BASE="${ARGS[0]:-${SDLC_BASE:-origin/main}}"
29
+
30
+ # Fail closed if the base ref can't be resolved (shallow clone / wrong base branch / unfetched ref).
31
+ if ! git rev-parse --verify --quiet "${BASE}^{commit}" >/dev/null; then
32
+ echo "FAIL [commit-message]: base ref '${BASE}' not found — fetch full history / check the base branch."
33
+ exit 1
34
+ fi
35
+ RANGE="${BASE}..HEAD"
36
+
37
+ # Conventional-Commits types — keep in sync with cli/manifest.mjs COMMIT_TYPES.
38
+ TYPES='feat|fix|docs|refactor|test|perf|build|ci|chore|revert'
39
+
40
+ commits="$(git rev-list --no-merges "$RANGE")"
41
+ if [ -z "$commits" ]; then
42
+ echo "PASS [commit-message]: no non-merge commits in ${RANGE} (profile: ${PROFILE})"
43
+ exit 0
44
+ fi
45
+
46
+ rc=0
47
+ while IFS= read -r sha; do
48
+ [ -z "$sha" ] && continue
49
+ short="$(git log -1 --format=%h "$sha")"
50
+ subject="$(git log -1 --format=%s "$sha")"
51
+
52
+ # 1) subject shape: "<type>(<optional scope>)<optional !>: <non-empty description>"
53
+ # (scope + breaking-change `!` are allowed per CONTRIBUTING.md).
54
+ if ! printf '%s' "$subject" | grep -qE "^(${TYPES})(\([a-z0-9._-]+\))?!?: .+"; then
55
+ echo "FAIL [commit-message]: ${short} subject '${subject}' is not '<type>(<scope>)?!?: <description>' (type one of: ${TYPES//|/, })."
56
+ rc=1
57
+ # 2) no trailing period on the subject
58
+ elif printf '%s' "$subject" | grep -qE '\.$'; then
59
+ echo "FAIL [commit-message]: ${short} subject '${subject}' must not end with a period."
60
+ rc=1
61
+ else
62
+ echo "PASS [commit-message]: ${short} '${subject}'"
63
+ fi
64
+
65
+ # 3) trailer order Task -> Contract-Change -> Co-Authored-By (only among those present).
66
+ # Parse ONLY the trailing trailer block (git interpret-trailers) so a body prose line that
67
+ # happens to start with a trailer key is never mistaken for a trailer.
68
+ trailers="$(git log -1 --format=%B "$sha" | git interpret-trailers --parse 2>/dev/null || true)"
69
+ lt="$(printf '%s\n' "$trailers" | grep -niE '^Task:' | head -1 | cut -d: -f1 || true)"
70
+ lc="$(printf '%s\n' "$trailers" | grep -niE '^Contract-Change:' | head -1 | cut -d: -f1 || true)"
71
+ lo="$(printf '%s\n' "$trailers" | grep -niE '^Co-Authored-By:' | head -1 | cut -d: -f1 || true)"
72
+ if { [ -n "$lt" ] && [ -n "$lc" ] && [ "$lt" -gt "$lc" ]; } \
73
+ || { [ -n "$lt" ] && [ -n "$lo" ] && [ "$lt" -gt "$lo" ]; } \
74
+ || { [ -n "$lc" ] && [ -n "$lo" ] && [ "$lc" -gt "$lo" ]; }; then
75
+ echo "FAIL [commit-message]: ${short} trailers out of order — expected Task -> Contract-Change -> Co-Authored-By."
76
+ rc=1
77
+ fi
78
+ done <<EOF
79
+ $commits
80
+ EOF
81
+
82
+ exit "$rc"