yadflow 2.4.2 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -2
- package/README.md +44 -22
- package/bin/yad.mjs +5 -0
- package/cli/doctor.mjs +14 -2
- package/cli/gate.mjs +8 -3
- package/cli/manifest.mjs +18 -2
- package/cli/openpr.mjs +12 -2
- package/cli/platform.mjs +105 -11
- package/cli/setup.mjs +61 -7
- package/cli/ship.mjs +37 -0
- package/docs/index.html +47 -16
- package/package.json +2 -2
- package/skills/sdlc/config.yaml +7 -2
- package/skills/sdlc/install.sh +1 -1
- package/skills/sdlc/module-help.csv +7 -4
- package/skills/yad-checks/references/check-gates.md +58 -2
- package/skills/yad-checks/templates/checks/commit-message.sh +82 -0
- package/skills/yad-checks/templates/github/yad-checks.yml +27 -0
- package/skills/yad-checks/templates/github/yad-hub-checks.yml +36 -0
- package/skills/yad-checks/templates/gitlab/yad-checks.gitlab-ci.yml +20 -0
- package/skills/yad-checks/templates/gitlab/yad-hub-checks.gitlab-ci.yml +39 -0
- package/skills/yad-commit/SKILL.md +66 -0
- package/skills/yad-connect-repos/SKILL.md +14 -7
- package/skills/yad-connect-repos/references/hub-config.md +20 -11
- package/skills/yad-connect-repos/references/repos-registry.md +2 -1
- package/skills/yad-engineer-review/SKILL.md +86 -0
- package/skills/{yad-ship → yad-engineer-review}/references/ship-and-record.md +2 -2
- package/skills/{yad-ship → yad-engineer-review}/templates/.coderabbit.yaml +1 -1
- package/skills/yad-epic/references/state-schema.md +1 -1
- package/skills/yad-hub-bridge/references/login-roster.md +32 -5
- package/skills/yad-implement/SKILL.md +1 -1
- package/skills/yad-implement/references/implement-conventions.md +1 -1
- package/skills/yad-open-pr/SKILL.md +72 -0
- package/skills/yad-pr-template/SKILL.md +5 -0
- package/skills/yad-pr-template/templates/checks/pr-template.sh +62 -0
- package/skills/yad-pr-template/templates/checks/pr-title.sh +51 -0
- package/skills/yad-review-gate/references/gating.md +8 -2
- package/skills/yad-run/SKILL.md +2 -2
- package/skills/yad-run/references/run-loop.md +4 -4
- package/skills/yad-ship/SKILL.md +44 -66
- package/skills/yad-spec/SKILL.md +1 -1
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
|
|
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>
|
|
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-
|
|
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
|
|
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>
|
|
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>
|
|
@@ -475,7 +475,7 @@ each code repo/
|
|
|
475
475
|
<div class="term"><strong><code>approvals.json</code></strong><span class="badge b-file">files</span><br>Who approved which review, with name + role. The gate reads this to decide whether the rule is met.</div>
|
|
476
476
|
<div class="term"><strong><code>repos.json</code></strong><span class="badge b-file">files</span><br>The project-wide registry of connected code repos (path/URL, platform, domain owner, freshness sha).</div>
|
|
477
477
|
<div class="term"><strong><code>hub.json</code></strong><span class="badge b-file">files</span><br>The hub’s platform record (GitHub or GitLab) plus the reviewer roster, used to run front-half reviews on real PRs/MRs.</div>
|
|
478
|
-
<div class="term"><strong>Roster</strong><span class="badge b-role">roles</span><br>The mapping from each
|
|
478
|
+
<div class="term"><strong>Roster</strong><span class="badge b-role">roles</span><br>The mapping from each person’s GitHub/GitLab login + commit email to their SDLC name and a <strong>per-scope roles map</strong> (<code>roles: { hub: […], <repo>: […] }</code>). One person can be owner, reviewer <em>and</em> domain owner at once; one repo can have several people per role. Logins are validated against GitHub/GitLab at setup (warn-only). When a PR/MR is opened, the committer becomes the <strong>assignee</strong> and the scope’s reviewers + domain owners are <strong>auto-requested</strong>.</div>
|
|
479
479
|
<div class="term"><strong>Code-map</strong><span class="badge b-file">files</span><br>A lightweight, AI-readable summary of a connected code repo — its existing endpoints, events, data models and modules — cached under <code>.sdlc/code-context/</code> so the front-half steps don’t contradict code that already exists.</div>
|
|
480
480
|
<div class="term"><strong>Repomix pack</strong><span class="badge b-tool">tools</span><br>A compressed, secret-scanned snapshot of a repo’s code produced by the Repomix tool (<code>npx repomix</code>), cached alongside the code-map so AI can read the codebase.</div>
|
|
481
481
|
<div class="term"><strong>Fresh / stale</strong><span class="badge b-file">files</span><br>A connected repo’s cached picture is <em>fresh</em> if the repo hasn’t moved since it was packed (tracked by HEAD sha) and <em>stale</em> if it has. Refreshing is a deliberate human decision (<code>yad repo refresh</code>) — skills warn about staleness but never silently re-pack.</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><type>: …</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: <artifact> (EP-<slug>)</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
|
|
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 & AI roles</h3>
|
|
@@ -681,7 +684,7 @@ each code repo/
|
|
|
681
684
|
|
|
682
685
|
<!-- ======================================================= -->
|
|
683
686
|
<section id="skills">
|
|
684
|
-
<h2>6. The
|
|
687
|
+
<h2>6. The 25 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-
|
|
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>
|
|
@@ -718,12 +724,12 @@ each code repo/
|
|
|
718
724
|
<h4><code>yad-connect-repos</code></h4>
|
|
719
725
|
<div class="row"><span class="lbl lbl-what">What</span> Introduces your code repos to the product hub so the “brain” knows what is already built. It registers each repo in <code>repos.json</code> and caches an AI-readable picture of it (a Repomix pack + a code-map of existing endpoints, events, data models and modules). Also sets up the hub’s own platform record and reviewer roster.</div>
|
|
720
726
|
<div class="row"><span class="lbl lbl-how">How</span> Run it in the product hub, once per repo:</div>
|
|
721
|
-
<pre><code>yad-connect-repos action: connect repo: backend path: ../backend-repo
|
|
727
|
+
<pre><code>yad-connect-repos action: connect repo: backend path: ../backend-repo domain_owners: carol,dave
|
|
722
728
|
yad-connect-repos action: list # see every repo as fresh / stale
|
|
723
729
|
yad-connect-repos action: refresh repo: backend # re-pack after the code moved
|
|
724
730
|
yad-connect-repos action: disconnect repo: backend
|
|
725
731
|
yad-connect-repos action: detect-hub # record the hub's GitHub/GitLab platform
|
|
726
|
-
yad-connect-repos action: roster login: alice-gh name: alice
|
|
732
|
+
yad-connect-repos action: roster login: alice-gh name: alice email: alice@x.com roles: hub=owner,reviewer # validated vs the hub</code></pre>
|
|
727
733
|
<div class="row"><span class="lbl lbl-when">When</span> During one-time setup, and again any time you add a new code repo. Re-run <code>refresh</code> when a skill warns a repo is stale. <strong>Greenfield with no code yet? Skip it entirely</strong> — the brain proceeds without it.</div>
|
|
728
734
|
</div>
|
|
729
735
|
|
|
@@ -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>,
|
|
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 & 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 & 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
|
|
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-
|
|
902
|
-
yad-
|
|
903
|
-
yad-
|
|
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
|
|
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>
|
|
952
983
|
<div class="row"><span class="lbl lbl-how">How</span></div>
|
|
953
984
|
<pre><code>cd <product-hub-repo>
|
|
954
985
|
npx yadflow setup</code></pre>
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "yadflow",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "Yadflow — the gated, team, multi-repo SDLC: author → review → build with a PR-driven review gate and a zero-dependency `yad` CLI (setup, gate, commit, open-pr, repo). A BMAD module +
|
|
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.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "AbdelRahman Nasr",
|
|
7
7
|
"license": "MIT",
|
package/skills/sdlc/config.yaml
CHANGED
|
@@ -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
|
|
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-
|
|
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
|
package/skills/sdlc/install.sh
CHANGED
|
@@ -11,7 +11,7 @@ set -euo pipefail
|
|
|
11
11
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
12
12
|
cd "$ROOT"
|
|
13
13
|
|
|
14
|
-
SKILLS=(yad-analysis yad-epic yad-architecture yad-ui yad-stories yad-test-cases yad-connect-repos yad-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-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,14 @@ 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
|
|
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
|
|
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-
|
|
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-
|
|
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,,
|
|
@@ -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"
|
|
@@ -32,6 +32,33 @@ jobs:
|
|
|
32
32
|
with: { node-version: "20" }
|
|
33
33
|
- run: bash checks/build-test-lint.sh
|
|
34
34
|
|
|
35
|
+
# Pattern gates: commit subject + PR title + PR body all follow the convention (profile: code).
|
|
36
|
+
commit-message:
|
|
37
|
+
runs-on: ubuntu-latest
|
|
38
|
+
steps:
|
|
39
|
+
- uses: actions/checkout@v4
|
|
40
|
+
with: { fetch-depth: 0 }
|
|
41
|
+
- run: bash checks/commit-message.sh --profile code "origin/${{ github.base_ref }}"
|
|
42
|
+
|
|
43
|
+
# Pass the title via env (never interpolate untrusted ${{ }} into a run line — injection-safe).
|
|
44
|
+
pr-title:
|
|
45
|
+
runs-on: ubuntu-latest
|
|
46
|
+
env:
|
|
47
|
+
PR_TITLE: ${{ github.event.pull_request.title }}
|
|
48
|
+
steps:
|
|
49
|
+
- uses: actions/checkout@v4
|
|
50
|
+
- run: bash checks/pr-title.sh --profile code "$PR_TITLE"
|
|
51
|
+
|
|
52
|
+
pr-template:
|
|
53
|
+
runs-on: ubuntu-latest
|
|
54
|
+
env:
|
|
55
|
+
PR_BODY: ${{ github.event.pull_request.body }}
|
|
56
|
+
steps:
|
|
57
|
+
- uses: actions/checkout@v4
|
|
58
|
+
- run: |
|
|
59
|
+
body="$(mktemp)"; printf '%s' "$PR_BODY" > "$body"
|
|
60
|
+
bash checks/pr-template.sh --profile code "$body"
|
|
61
|
+
|
|
35
62
|
# No unverified commits from unverified users: platform-Verified signature + allowlisted author.
|
|
36
63
|
verified-commits:
|
|
37
64
|
runs-on: ubuntu-latest
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# yad-managed: yad-checks
|
|
2
|
+
# Pattern gates for the PRODUCT HUB: every PR (including the front-half review/EP-* PRs) must follow
|
|
3
|
+
# the hub conventions — Conventional-Commits commit subjects, a `review: <artifact> (EP-<slug>)` PR
|
|
4
|
+
# title, and a PR body that uses the hub artifact-review template. They run with `--profile hub`.
|
|
5
|
+
# Standalone workflow so it never collides with the code-repo yad-checks workflow.
|
|
6
|
+
name: yad-hub-checks
|
|
7
|
+
on:
|
|
8
|
+
pull_request:
|
|
9
|
+
branches: ["**"]
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
commit-message:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
with: { fetch-depth: 0 }
|
|
17
|
+
- run: bash checks/commit-message.sh --profile hub "origin/${{ github.base_ref }}"
|
|
18
|
+
|
|
19
|
+
# Pass the title via env (never interpolate untrusted ${{ }} into a run line — injection-safe).
|
|
20
|
+
pr-title:
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
env:
|
|
23
|
+
PR_TITLE: ${{ github.event.pull_request.title }}
|
|
24
|
+
steps:
|
|
25
|
+
- uses: actions/checkout@v4
|
|
26
|
+
- run: bash checks/pr-title.sh --profile hub "$PR_TITLE"
|
|
27
|
+
|
|
28
|
+
pr-template:
|
|
29
|
+
runs-on: ubuntu-latest
|
|
30
|
+
env:
|
|
31
|
+
PR_BODY: ${{ github.event.pull_request.body }}
|
|
32
|
+
steps:
|
|
33
|
+
- uses: actions/checkout@v4
|
|
34
|
+
- run: |
|
|
35
|
+
body="$(mktemp)"; printf '%s' "$PR_BODY" > "$body"
|
|
36
|
+
bash checks/pr-template.sh --profile hub "$body"
|
|
@@ -45,6 +45,26 @@ yad-build-test-lint:
|
|
|
45
45
|
script:
|
|
46
46
|
- bash checks/build-test-lint.sh
|
|
47
47
|
|
|
48
|
+
# Pattern gates: commit subject + MR title + MR body all follow the convention (profile: code).
|
|
49
|
+
yad-commit-message:
|
|
50
|
+
extends: .sdlc_mr_only
|
|
51
|
+
needs: []
|
|
52
|
+
script:
|
|
53
|
+
- bash checks/commit-message.sh --profile code "origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME"
|
|
54
|
+
|
|
55
|
+
yad-pr-title:
|
|
56
|
+
extends: .sdlc_mr_only
|
|
57
|
+
needs: []
|
|
58
|
+
script:
|
|
59
|
+
- bash checks/pr-title.sh --profile code "$CI_MERGE_REQUEST_TITLE"
|
|
60
|
+
|
|
61
|
+
yad-pr-template:
|
|
62
|
+
extends: .sdlc_mr_only
|
|
63
|
+
needs: []
|
|
64
|
+
script:
|
|
65
|
+
- body="$(mktemp)"; printf '%s' "$CI_MERGE_REQUEST_DESCRIPTION" > "$body"
|
|
66
|
+
- bash checks/pr-template.sh --profile code "$body"
|
|
67
|
+
|
|
48
68
|
# No unverified commits from unverified users: platform-Verified signature + allowlisted author.
|
|
49
69
|
# Needs a CI/CD variable GITLAB_TOKEN (or SDLC_API_TOKEN) with read_api scope — CI_JOB_TOKEN cannot
|
|
50
70
|
# read the commit-signature API. Without it the job FAILS closed with guidance.
|