yadflow 2.18.0 → 3.0.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/cli/docs.mjs CHANGED
@@ -121,6 +121,7 @@ export function docsStale(manifest, { artifactHash, repoHeads = {}, templateVers
121
121
  const BUILD_PUBLIC = [
122
122
  'mkdir -p public',
123
123
  'if [ -d docs/sdlc-site ]; then (cd docs/sdlc-site && npm ci && npm run build) && mkdir -p public/app && cp -r docs/sdlc-site/dist/. public/app/ && cp docs/sdlc-site/public/report.html public/index.html && cp docs/sdlc-site/public/report.html public/report.html; fi',
124
+ 'if [ -d docs/tutorial-site ]; then (cd docs/tutorial-site && npm ci && npm run build) && mkdir -p public/tutorial && cp -r docs/tutorial-site/dist/. public/tutorial/; fi',
124
125
  'for d in epics/*/docs-site; do [ -d "$d" ] || continue; id=$(basename "$(dirname "$d")"); (cd "$d" && npm ci && npm run build) && mkdir -p "public/epics/$id" && cp -r "$d/dist/." "public/epics/$id/"; done',
125
126
  ];
126
127
  export function pagesWorkflow(platform) {
@@ -160,19 +161,19 @@ jobs:
160
161
  name: github-pages
161
162
  url: \${{ steps.deployment.outputs.page_url }}
162
163
  steps:
163
- - uses: actions/checkout@v4
164
- - uses: actions/setup-node@v4
164
+ - uses: actions/checkout@v7
165
+ - uses: actions/setup-node@v6
165
166
  with:
166
167
  node-version: 20
167
168
  - name: Build the overview + per-epic sites into ./public
168
169
  run: |
169
170
  ${BUILD_PUBLIC.map((l) => ` ${l}`).join('\n')}
170
- - uses: actions/configure-pages@v5
171
- - uses: actions/upload-pages-artifact@v3
171
+ - uses: actions/configure-pages@v6
172
+ - uses: actions/upload-pages-artifact@v5
172
173
  with:
173
174
  path: public
174
175
  - id: deployment
175
- uses: actions/deploy-pages@v4
176
+ uses: actions/deploy-pages@v5
176
177
  `;
177
178
  }
178
179
  export function pagesWorkflowPath(platform) {
package/cli/manifest.mjs CHANGED
@@ -33,7 +33,6 @@ export const SKILLS = [
33
33
  'yad-implement',
34
34
  'yad-checks',
35
35
  'yad-pr-template',
36
- 'yad-review-comments',
37
36
  'yad-hub-bridge',
38
37
  'yad-commit',
39
38
  'yad-open-pr',
@@ -43,6 +42,10 @@ export const SKILLS = [
43
42
  'yad-run',
44
43
  'yad-review-gate',
45
44
  'yad-status',
45
+ 'yad-change',
46
+ 'yad-timeline',
47
+ 'yad-defects',
48
+ 'yad-reconcile',
46
49
  ];
47
50
 
48
51
  // Pre-2.0 skill names (the sdlc-* -> yad-* rename). `check`/`update` migrate any install
@@ -58,7 +61,6 @@ export const LEGACY_SKILLS = {
58
61
  'yad-implement': 'sdlc-implement',
59
62
  'yad-checks': 'sdlc-checks',
60
63
  'yad-pr-template': 'sdlc-pr-template',
61
- 'yad-review-comments': 'sdlc-review-comments',
62
64
  'yad-hub-bridge': 'sdlc-hub-bridge',
63
65
  // Step E ("ship") was renamed to yad-engineer-review (the yad-ship name now belongs to the new
64
66
  // commit+open-PR combined skill). Pre-2.0 installs carry sdlc-ship → migrate it to yad-engineer-review.
@@ -71,6 +73,14 @@ export const LEGACY_SKILLS = {
71
73
  'yad-status': 'sdlc-status',
72
74
  };
73
75
 
76
+ // Skills removed in a later release that must be PURGED from existing installs. Unlike
77
+ // LEGACY_SKILLS (a rename: drop old name, install new), these have no replacement — a rerun of
78
+ // `yad update` / `yad setup` deletes any installed copy from every IDE target so a breaking
79
+ // removal actually takes effect. List every install basename to purge, including any pre-rename
80
+ // alias the skill ever shipped under.
81
+ // yad-review-comments — removed in 2.x (was sdlc-review-comments pre-2.0).
82
+ export const REMOVED_SKILLS = ['yad-review-comments', 'sdlc-review-comments'];
83
+
74
84
  // Pre-2.0 wired-file dests replaced by renamed ones (old dest -> new dest, per platform).
75
85
  // An old file is removed ONLY when its first line carries the old ownership marker —
76
86
  // a same-named file the user authored themselves is never touched.
@@ -181,12 +191,10 @@ export const REPO_WIRING = {
181
191
  github: [
182
192
  { src: 'skills/yad-checks/templates/github/yad-checks.yml', dest: '.github/workflows/yad-checks.yml' },
183
193
  { src: 'skills/yad-pr-template/templates/github/pull_request_template.md', dest: '.github/pull_request_template.md' },
184
- { src: 'skills/yad-review-comments/templates/github/REVIEW_COMMENTS.md', dest: '.github/REVIEW_COMMENTS.md' },
185
194
  ],
186
195
  gitlab: [
187
196
  { src: 'skills/yad-checks/templates/gitlab/yad-checks.gitlab-ci.yml', dest: '.gitlab/ci/yad-checks.yml' },
188
197
  { src: 'skills/yad-pr-template/templates/gitlab/merge_request_templates/Default.md', dest: '.gitlab/merge_request_templates/Default.md' },
189
- { src: 'skills/yad-review-comments/templates/gitlab/REVIEW_COMMENTS.md', dest: '.gitlab/REVIEW_COMMENTS.md' },
190
198
  ],
191
199
  };
192
200
 
package/cli/plan.mjs CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  } from './lib.mjs';
9
9
  import {
10
10
  SKILLS, IDE_FOLDER_TARGETS, IDE_OPENCODE_DIR, MODULE_FILES, wiringFor, HUB_WIRING, PROJECT_FILES,
11
- LEGACY_SKILLS, LEGACY_MARKER, LEGACY_REPO_FILES, LEGACY_HUB_FILES,
11
+ LEGACY_SKILLS, REMOVED_SKILLS, LEGACY_MARKER, LEGACY_REPO_FILES, LEGACY_HUB_FILES,
12
12
  } from './manifest.mjs';
13
13
 
14
14
  // status: 'ok' | 'missing' | 'outdated'
@@ -105,6 +105,39 @@ export function legacyModuleActions(root, ideTargets = ideTargetsFor(root)) {
105
105
  return actions;
106
106
  }
107
107
 
108
+ // Purge of skills removed in a later release (REMOVED_SKILLS). Status is 'removed' — like 'legacy'
109
+ // it is applied by `yad update` (--scope=changed) too, because the skill IS installed and a
110
+ // breaking removal must actually delete it. An action is emitted ONLY when a copy is present, so a
111
+ // clean tree yields nothing and the purge is idempotent. apply() just deletes the install (no
112
+ // replacement — that is what makes this a removal, not a rename).
113
+ export function removedModuleActions(root, ideTargets = ideTargetsFor(root)) {
114
+ const actions = [];
115
+ for (const ide of ideTargets) {
116
+ for (const skill of REMOVED_SKILLS) {
117
+ if (ide === '.opencode') {
118
+ const dest = path.join(root, IDE_OPENCODE_DIR, `${skill}.md`);
119
+ if (!exists(dest)) continue;
120
+ actions.push({
121
+ scope: ide,
122
+ item: `${skill}.md (removed)`,
123
+ status: 'removed',
124
+ apply: () => fs.rmSync(dest, { force: true }),
125
+ });
126
+ } else {
127
+ const dest = path.join(root, ide, 'skills', skill);
128
+ if (!exists(dest)) continue;
129
+ actions.push({
130
+ scope: ide,
131
+ item: `${skill} (removed)`,
132
+ status: 'removed',
133
+ apply: () => fs.rmSync(dest, { recursive: true, force: true }),
134
+ });
135
+ }
136
+ }
137
+ }
138
+ return actions;
139
+ }
140
+
108
141
  // True only for a file WE installed pre-2.0: its first line carries the old ownership marker
109
142
  // (`# sdlc-managed:` / `# sdlc-managed-include:`). A same-named user-authored file is never ours.
110
143
  function ownedByOldInstall(p) {
@@ -152,7 +185,7 @@ export function legacyHubActions(root) {
152
185
  return legacyFileActions('hub', root, LEGACY_HUB_FILES[hub.platform], wiring);
153
186
  }
154
187
 
155
- // Per-repo wiring (gate scripts, CI, PR template, comment scaffold).
188
+ // Per-repo wiring (gate scripts, CI, PR template).
156
189
  export function repoActions(root, repo) {
157
190
  const repoRoot = path.resolve(root, repo.path);
158
191
  return wiringFor(repo.platform).map((w) =>
package/cli/reconcile.mjs CHANGED
@@ -8,11 +8,11 @@ import {
8
8
  import { VERSION, PROJECT_FILES } from './manifest.mjs';
9
9
  import {
10
10
  moduleActions, repoActions, hubActions, authorsActions,
11
- legacyModuleActions, legacyRepoActions, legacyHubActions,
11
+ legacyModuleActions, removedModuleActions, legacyRepoActions, legacyHubActions,
12
12
  } from './plan.mjs';
13
13
  import { gitHead, packRepo } from './setup.mjs';
14
14
 
15
- const MARK = { missing: c.red('missing'), outdated: c.yellow('outdated'), stale: c.yellow('stale'), legacy: c.yellow('legacy'), ok: c.green('ok') };
15
+ const MARK = { missing: c.red('missing'), outdated: c.yellow('outdated'), stale: c.yellow('stale'), legacy: c.yellow('legacy'), removed: c.yellow('removed'), ok: c.green('ok') };
16
16
 
17
17
  export async function reconcile(root, { fix = false, scope = 'all', force = false } = {}) {
18
18
  log(c.bold(`\nSDLC reconcile ${c.dim('v' + VERSION)}`));
@@ -26,9 +26,10 @@ export async function reconcile(root, { fix = false, scope = 'all', force = fals
26
26
  if (!exists(path.join(root, PROJECT_FILES.reposRegistry))) gaps.push('no repos registered (.sdlc/repos.json absent)');
27
27
 
28
28
  // --- deterministic file actions (module + hub CI + author allowlists + every registered repo),
29
- // plus pre-2.0 sdlc-* -> yad-* migrations ('legacy': old name installed; rename in place) ---
29
+ // plus pre-2.0 sdlc-* -> yad-* migrations ('legacy': old name installed; rename in place)
30
+ // and purge of skills removed in a later release ('removed': delete the lingering install) ---
30
31
  const actions = [
31
- ...moduleActions(root), ...legacyModuleActions(root),
32
+ ...moduleActions(root), ...legacyModuleActions(root), ...removedModuleActions(root),
32
33
  ...hubActions(root), ...legacyHubActions(root),
33
34
  ...authorsActions(root, registry.repos),
34
35
  ];
@@ -50,7 +51,7 @@ export async function reconcile(root, { fix = false, scope = 'all', force = fals
50
51
  if (!byScope.has(a.scope)) byScope.set(a.scope, []);
51
52
  byScope.get(a.scope).push(a);
52
53
  }
53
- const counts = { missing: 0, outdated: 0, stale: 0, legacy: 0, ok: 0 };
54
+ const counts = { missing: 0, outdated: 0, stale: 0, legacy: 0, removed: 0, ok: 0 };
54
55
  for (const [scopeName, items] of byScope) {
55
56
  const notOk = items.filter((i) => i.status !== 'ok');
56
57
  items.forEach((i) => counts[i.status]++);
@@ -64,7 +65,7 @@ export async function reconcile(root, { fix = false, scope = 'all', force = fals
64
65
  a.status !== 'ok' && (scope === 'all' ? true : a.status !== 'missing'),
65
66
  );
66
67
  log('');
67
- log(c.dim(`summary: ${counts.missing} missing, ${counts.outdated} outdated, ${counts.stale} stale, ${counts.legacy} legacy, ${counts.ok} ok`));
68
+ log(c.dim(`summary: ${counts.missing} missing, ${counts.outdated} outdated, ${counts.stale} stale, ${counts.legacy} legacy, ${counts.removed} removed, ${counts.ok} ok`));
68
69
 
69
70
  if (!fix) {
70
71
  if (fixable.length || gaps.length) hand('run `yad check --fix` to reconcile (or `yad setup` for missing one-time setup).');
package/cli/setup.mjs CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  import { VERSION, IDE_FOLDER_TARGETS, PROJECT_FILES, DESIGN_TOOLS, DESIGN_PRIMARY, TESTING_TOOLS, TESTING_PRIMARY, LEARNING_TOOLS, LEARNING_PRIMARY } from './manifest.mjs';
10
10
  import {
11
11
  moduleActions, repoActions, hubActions, authorsActions,
12
- legacyModuleActions, legacyRepoActions, legacyHubActions,
12
+ legacyModuleActions, removedModuleActions, legacyRepoActions, legacyHubActions,
13
13
  } from './plan.mjs';
14
14
  import { validateLogin, rolesForScope } from './platform.mjs';
15
15
 
@@ -399,6 +399,9 @@ export async function runSetup(root, opts = {}) {
399
399
  // IDE targets and install their yad-* renames. Without this, setup only ADDED yad-* and left
400
400
  // stale sdlc-* sitting next to them (the rename only ran under `yad update` / `yad check --fix`).
401
401
  applyActions(legacyModuleActions(root, ideTargets), { force: true });
402
+ // Purge any skill removed in a later release (REMOVED_SKILLS) that a prior install left behind —
403
+ // setup only ADDS current skills, so without this a breaking removal would linger next to them.
404
+ applyActions(removedModuleActions(root, ideTargets), { force: true });
402
405
  ok(`module installed into: ${ideTargets.join(', ')}`);
403
406
 
404
407
  // Global leftovers: a pre-2.0 install may have put sdlc-* skills in the user's global
@@ -416,6 +419,18 @@ export async function runSetup(root, opts = {}) {
416
419
  }
417
420
  }
418
421
 
422
+ // Same opt-in pass for skills removed in a later release (REMOVED_SKILLS) that linger in the
423
+ // global ~/.claude/skills — purge them so a global install also drops the dead command.
424
+ const globalRemoved = process.env.SDLC_NONINTERACTIVE ? [] : removedModuleActions(os.homedir(), ['.claude']);
425
+ if (globalRemoved.length) {
426
+ if (await askYesNo(`Found ${globalRemoved.length} removed skill(s) in your global ~/.claude/skills. Delete them?`, true)) {
427
+ applyActions(globalRemoved, { force: true });
428
+ ok('purged removed skill(s) from global ~/.claude/skills');
429
+ } else {
430
+ info('left global ~/.claude/skills untouched — re-run `yad setup` or purge later with `yad update`');
431
+ }
432
+ }
433
+
419
434
  // Detect hub platform + roster
420
435
  S(solo ? 'Hub platform (solo — no roster)' : 'Hub platform & reviewer roster');
421
436
  guide(solo
@@ -619,7 +634,7 @@ export async function runSetup(root, opts = {}) {
619
634
  }
620
635
 
621
636
  // Wire each connected repo + the hub itself
622
- S('Wire connected repos + the hub (CI gates, PR template, comment scaffold, gate-sync)');
637
+ S('Wire connected repos + the hub (CI gates, PR template, gate-sync)');
623
638
  guide(['Installs the CI safety gates, PR/MR template, and gate-sync — automatic, no input needed.']);
624
639
  if (registry.repos.length === 0) info('no repos to wire');
625
640
  for (const repo of registry.repos) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "yadflow",
3
- "version": "2.18.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, thread, reconcile). A BMAD module + 35 yad-* skills.",
3
+ "version": "3.0.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, thread, reconcile). A BMAD module + 34 yad-* skills.",
5
5
  "type": "module",
6
6
  "author": "AbdelRahman Nasr",
7
7
  "license": "MIT",
@@ -11,10 +11,27 @@ set -euo pipefail
11
11
  ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
12
12
  cd "$ROOT"
13
13
 
14
- SKILLS=(yad-discovery yad-analysis yad-epic yad-architecture yad-ui yad-stories yad-test-cases yad-connect-repos yad-sync-repos yad-connect-design yad-connect-testing yad-connect-learning yad-connect-docs yad-docs yad-docs-overview yad-docs-sync yad-learn yad-spec yad-implement yad-checks yad-pr-template yad-review-comments yad-hub-bridge yad-commit yad-open-pr yad-ship yad-engineer-review yad-backfill yad-run yad-review-gate yad-status yad-change yad-timeline yad-defects yad-reconcile)
14
+ SKILLS=(yad-discovery yad-analysis yad-epic yad-architecture yad-ui yad-stories yad-test-cases yad-connect-repos yad-sync-repos yad-connect-design yad-connect-testing yad-connect-learning yad-connect-docs yad-docs yad-docs-overview yad-docs-sync yad-learn yad-spec yad-implement yad-checks yad-pr-template yad-hub-bridge yad-commit yad-open-pr yad-ship yad-engineer-review yad-backfill yad-run yad-review-gate yad-status yad-change yad-timeline yad-defects yad-reconcile)
15
+
16
+ # Skills removed in a later release: this installer only refreshes names still in SKILLS, so a
17
+ # rerun would otherwise leave a dropped skill sitting in the IDE dirs. Purge any lingering copy
18
+ # (current name + any pre-rename alias) so the breaking removal takes effect for existing installs.
19
+ # Mirrors the CLI's REMOVED_SKILLS purge in `yad update`.
20
+ REMOVED_SKILLS=(yad-review-comments sdlc-review-comments)
15
21
 
16
22
  echo "Installing sdlc module from $ROOT/skills ..."
17
23
 
24
+ for ide in .claude .agents .zencoder; do
25
+ for s in "${REMOVED_SKILLS[@]}"; do
26
+ rm -rf "$ide/skills/$s"
27
+ done
28
+ done
29
+ if [ -d ".opencode/commands" ]; then
30
+ for s in "${REMOVED_SKILLS[@]}"; do
31
+ rm -f ".opencode/commands/$s.md"
32
+ done
33
+ fi
34
+
18
35
  # 1. Folder-per-skill IDEs: .claude, .agents, .zencoder
19
36
  for ide in .claude .agents .zencoder; do
20
37
  for s in "${SKILLS[@]}"; do
@@ -20,7 +20,6 @@ SDLC Workflow,yad-pr-template,PR/MR Template,PT,"Build-half Step D: detect a cod
20
20
  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
21
21
  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
22
22
  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
23
- 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
24
23
  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
25
24
  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
26
25
  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
@@ -56,7 +56,7 @@ git work tree). Generate and commit on it.
56
56
  Map the pipeline onto the same data structures `yad-docs` uses (concrete mapping in
57
57
  `references/pipeline-model.md`):
58
58
 
59
- - **Flow paths** = the **phases** — `Setup`, `Front half`, `Build half`, `Automation`.
59
+ - **Flow paths** = the **phases** — `Setup`, `Front-zero` (discovery), `Front half`, `Build half`, `Automation`, `Change management` (feature threads).
60
60
  - **Flow steps** = the **skills/gates in order** (from `module-help.csv` `preceded-by`/`followed-by`),
61
61
  each step's `messages` = the skill's `outputs`, and `sideEffects` = the `.sdlc/` files it writes.
62
62
  - **System components** = the **durable state objects** — the product hub, each `.sdlc/*.json`
@@ -10,7 +10,7 @@ ordering source of truth is `skills/sdlc/module-help.csv` (`phase`, `preceded-by
10
10
 
11
11
  | Shell primitive | Overview meaning |
12
12
  |-----------------|------------------|
13
- | `FlowPath` (`paths.ts`) | a **phase**: Setup, Front half, Build half, Automation. |
13
+ | `FlowPath` (`paths.ts`) | a **phase**: Setup, Front-zero (discovery), Front half, Build half, Automation, Change management (feature threads). |
14
14
  | `FlowStep` (within a path) | a **skill or gate** in order; `messages` = its `outputs`; `sideEffects` = the `.sdlc/` files it writes; `status`/`bookingStatus` annotate gated vs. enrichment vs. earned. |
15
15
  | `SystemComponent` (`components.ts`) | a **durable state object** (the hub, each `.sdlc/*.json`, code repos, the design/testing/learning tools, the platform). |
16
16
  | `RoleConfig` (`roles.ts`) | a **lens** → its relevant sections + paths. |
@@ -57,7 +57,6 @@ The gated authoring chain + the reusable review gate (10 steps, or 12 with the o
57
57
  | `yad-test-cases` | → `test-cases-review` (parallel, non-blocking) | `test-cases.md`, `test-links.json` |
58
58
  | `yad-review-gate` | the shared gate | `reviews/*.md`, `approvals.json`, `comments.json` |
59
59
  | `yad-hub-bridge` | the platform PR/MR bridge | `hub-prs.json` |
60
- | `yad-review-comments` | comment scaffolds | repo comment templates |
61
60
 
62
61
  ### Path: Build half (`phase: 3-build`)
63
62
  Per-story, per-repo: `spec → tasks → implement → checks → engineer-review`, plus the commit/PR helpers.
@@ -66,7 +65,7 @@ Per-story, per-repo: `spec → tasks → implement → checks → engineer-revie
66
65
  |--------------|------------------------|
67
66
  | `yad-spec` | `specs/<story-id>/` (Spec Kit layout), `link.md` |
68
67
  | `yad-implement` | a branch + commit per atomic task |
69
- | `yad-checks` | `checks/*.sh`, CI workflows |
68
+ | `yad-checks` | `checks/*.sh`, CI workflows — the gate set: `spec-link · contract-check · build-test-lint · verified-commits · commit-message · pr-title · pr-template · lineage-check · epic-open · reconcile-debt` |
70
69
  | `yad-pr-template` | PR/MR template + routing helpers |
71
70
  | `yad-commit` / `yad-open-pr` / `yad-ship` | one commit / one PR/MR |
72
71
  | `yad-engineer-review` | engineer review + ship recorded in `build-log.json` |
@@ -83,11 +82,30 @@ node classes from the diagram.
83
82
  | `yad-status` | read-only view (no writes) |
84
83
  | `yad-docs` / `yad-docs-overview` / `yad-docs-sync` | the docs sites + their `docs-build.json` manifests |
85
84
 
85
+ ### Path: Change management — feature threads (`phase: 6-change`)
86
+ The post-lock evolution layer. Once an epic is **sealed** (all stories shipped), behaviour can no longer
87
+ be mutated in place — a change request becomes a **new epic threaded to its parent** (genesis → change →
88
+ defect), inheriting unchanged front artifacts **by reference** and re-authoring only what changes, so
89
+ locked artifacts are never mutated and never go stale, only superseded. Three CI gates enforce the thread
90
+ (`lineage-check`, `epic-open`, `reconcile-debt`). This path never gates the front chain; `yad-change` is
91
+ the intake, then it hands off to the normal authoring skills + the review gate.
92
+
93
+ | Step (skill) | Outputs / sideEffects |
94
+ |--------------|------------------------|
95
+ | `yad-change` | intake + triage depth (defect-fix / behavioral-no-surface / contract-surface / new-capability); seeds a threaded change-epic — lineage frontmatter, inherited-step `state.json`, pointer-lock `contract-lock.json`, `change.json`, and `reconcile-debt.json` for hotfixes |
96
+ | `yad-timeline` | `TIMELINE.md` (the thread evolution view) + `thread-resolved.md` (the resolved current truth) |
97
+ | `yad-defects` | `DEFECTS.md` (quality-gap report by `escape_stage` + root cause) |
98
+ | `yad-reconcile` | advisory drift / orphan / debt sweep (read-only; mirrors `yad-docs-sync`) |
99
+
100
+ CLI: `yad thread [<epic>]` prints a thread + its resolved current truth + open debt; `yad reconcile`
101
+ runs the sweep. The three thread gates ride in the build-half `yad-checks` set above.
102
+
86
103
  ## System components = the durable state objects
87
104
 
88
105
  `components.ts` renders these on the canvas (deterministic positions): the **product hub**; each
89
106
  `.sdlc/*.json` (`state.json`, `approvals.json`, `comments.json`, `hub.json`, `repos.json`, `design.json`,
90
- `testing.json`, `learning.json`, `docs.json`, `contract-lock.json`, `build-state/*`, `trust-log.json`);
107
+ `testing.json`, `learning.json`, `docs.json`, `contract-lock.json`, `build-state/*`, `trust-log.json`,
108
+ and the change-thread ledgers `change.json`, `reconcile-debt.json`, `build-log.json`);
91
109
  the **connected code repos**; the **design / testing / learning tools**; and the **platform**
92
110
  (GitHub/GitLab + Pages). A skill's `sideEffects` link its step to the component it writes.
93
111
 
@@ -1,63 +0,0 @@
1
- ---
2
- name: yad-review-comments
3
- description: 'Installs platform-matched PR/MR review-comment scaffolds into a repo so reviewers leave structured, attributable feedback that maps cleanly into the SDLC file ledger. Works for code repos and the product hub. GitHub has no repo-level multi-comment template, so the scaffold is a committed REVIEW_COMMENTS.md reviewers copy from (saved as Saved Replies on GitHub / comment templates on GitLab). Each canned comment carries an attributable `**<name> (<role>)**` header that matches the `## <name> (<role>)` headings yad-review-gate writes. Use when the user says "add the comment templates" or "set up review comments" for a repo.'
4
- ---
5
-
6
- # SDLC — Review Comment Templates
7
-
8
- **Goal:** Give reviewers a consistent, **attributable** set of canned PR/MR comments so review feedback
9
- reads the same across repos and **maps cleanly into the file ledger** the gate keeps. The headers match
10
- the `## <name> (<role>)` shape `yad-review-gate` writes into `reviews/<artifact>--<date>--comments.md`
11
- and the per-commenter record in `comments.json`, so a synced or copy-pasted comment lands without
12
- reformatting.
13
-
14
- ## Platform reality (why this is a committed doc, not a config file)
15
-
16
- Neither GitHub nor GitLab has a **repo-level** multi-comment template convention: GitHub *Saved Replies*
17
- are per-account and GitLab *comment templates* are per-user/group, both set in the UI — there is no
18
- `.github/comment_templates/` or `.gitlab/comment_templates/` the repo can ship. The pragmatic mechanism
19
- on **both** is a single committed doc reviewers copy from (and optionally paste once into their personal
20
- Saved Replies / comment templates). This skill ships that doc.
21
-
22
- ## Conventions
23
-
24
- - `{project-root}` resolves from the project working directory — the **product** repo (holds the
25
- canonical templates under this skill).
26
- - Canonical sources live in this skill's `templates/`:
27
- - `templates/github/REVIEW_COMMENTS.md` → installs to `<repo>/.github/REVIEW_COMMENTS.md`
28
- - `templates/gitlab/REVIEW_COMMENTS.md` → installs to `<repo>/.gitlab/REVIEW_COMMENTS.md`
29
- - The two variants are identical except a footer line (GitHub: "save these as Saved Replies"; GitLab:
30
- "save these as comment templates").
31
-
32
- ## Inputs
33
-
34
- - `repo` — the repo to add the scaffold to (one of an epic's repos), or `hub` for the product hub.
35
- - `action` — `wire` (install the matching scaffold). Default `wire`.
36
-
37
- ## On Activation
38
-
39
- ### Step 1 — Resolve the repo and detect the platform
40
- Map `repo` → its path (`{project-root}/demo-repos/<repo>/` or the registry `path`); for `repo: hub` the
41
- target is `{project-root}` and the platform comes from `.sdlc/hub.json`. Detect the platform: a GitHub
42
- remote or `.github/` → GitHub; a GitLab remote or `.gitlab/` → GitLab. If ambiguous, ask.
43
-
44
- ### Step 2 — `wire` (drop only the matching scaffold)
45
- Copy the matching `templates/<platform>/REVIEW_COMMENTS.md` into the repo's `.github/` or `.gitlab/`.
46
- Do not clobber an existing non-SDLC file of the same name — back it up / ask. Commit on the repo's
47
- default branch (shared infrastructure, not a task diff). Idempotent — re-running refreshes in place.
48
-
49
- ### Step 3 — Stop (no auto-advance)
50
- Report what was committed. The scaffold is an aid to human review; it approves nothing and touches no
51
- `.sdlc/` state.
52
-
53
- ## Hard rules
54
-
55
- - **Drop only the matching scaffold** for the detected platform.
56
- - **Attributable headers** — every canned comment keeps the `**<name> (<role>)**` form so the gate's
57
- ledger and the PR thread agree.
58
- - **Nothing auto-advances.** Comments feed the human review and (via the bridge) `yad-review-gate`.
59
-
60
- ## Reference
61
- - Comment conventions + the full scaffold contents: `references/comment-conventions.md`.
62
- - The ledger headings these match: `../yad-review-gate/SKILL.md` (comment action) and `references/gating.md`.
63
- - The review bridge that syncs platform comments into the ledger: `../yad-hub-bridge/SKILL.md`.
@@ -1,55 +0,0 @@
1
- # Review comment conventions
2
-
3
- The scaffold (`REVIEW_COMMENTS.md`) groups canned comments into **blocking** (a gate would fail / must
4
- be fixed before merge) and **non-blocking** (suggestions, nits, questions). Every comment starts with an
5
- attributable header:
6
-
7
- ```
8
- **<name> (<role>)**
9
- ```
10
-
11
- `<role>` is one of `owner | reviewer | domain-owner` — the same roles `yad-review-gate` records. This
12
- header matches the `## <name> (<role>)` headings the gate writes into
13
- `reviews/<artifact>--<date>--comments.md`, so a comment copied from the PR thread (or synced by
14
- `yad-hub-bridge`) needs no reformatting to land in the ledger.
15
-
16
- ## Blocking comments (a gate or rule says stop)
17
-
18
- These map to the code-repo check gates (`yad-checks`) and the file-boundary / contract rules:
19
-
20
- - **spec-link** — "This commit has no `Task: <story>-<task>` trailer; the spec-link gate will fail. Add
21
- the trailer (and ensure `specs/<story>/link.md` exists)."
22
- - **contract-check** — "This diff changes the contract surface without `Contract-Change: yes` + a
23
- re-locked contract. Route back to the architecture gate and re-lock before this can merge."
24
- - **build-test-lint** — "Lint/build/test is red (or the test doesn't exercise the new behavior). The
25
- build-test-lint gate must pass with a test that actually exercises the acceptance criterion."
26
- - **file-boundary** — "This diff touches files the task's `Files:` list didn't declare. Re-scope the
27
- task (re-run `yad-spec`) rather than widening the diff silently."
28
-
29
- ## Escalation / routing comment
30
-
31
- - **routing** — "Risk is `high` / a contract|auth|payments surface is touched — this needs a
32
- domain-owner approval per touched repo (the same escalation `yad-review-gate` applies). Run
33
- `bash checks/risk-route.sh <body>` (code repo) or `bash checks/hub-route.sh <body>` (hub)."
34
-
35
- ## Non-blocking comments
36
-
37
- - **suggestion** — "Suggestion (non-blocking): …"
38
- - **nit** — "Nit: …"
39
- - **question** — "Question: … (not blocking — just want to understand)."
40
-
41
- ## Approval note
42
-
43
- - **approve** — "Approving as `<role>`. Recorded in `approvals.json` (code-repo ship: `build-log.json`).
44
- On the hub, your approval here is pulled in by `yad-review-gate action: sync`."
45
-
46
- ## Hub front-artifact review
47
-
48
- The hub scaffold adds a **Front-artifact review** section whose headers mirror the gate's comment file
49
- exactly, so reviewing an `epic.md` / `architecture.md` / `ui-design.md` / `stories/` PR produces comments
50
- that drop straight into `reviews/<artifact>--<date>--comments.md`:
51
-
52
- ```
53
- **<name> (<role>)**
54
- - <comment about scope / contract surface / acceptance signals / story split>
55
- ```
@@ -1,49 +0,0 @@
1
- # Review comments — canned replies
2
-
3
- Copy a block below into a PR review comment. Keep the `**<name> (<role>)**` header — it matches the
4
- SDLC review ledger (`reviews/<artifact>--<date>--comments.md`) so feedback stays attributable.
5
- `<role>` is one of `owner | reviewer | domain-owner`.
6
-
7
- ## Blocking (a gate or rule says stop — must be fixed before merge)
8
-
9
- **<name> (<role>)**
10
- - spec-link: this range has no `Task: <story>-<task>` trailer — the spec-link gate will fail. Add the
11
- trailer and make sure `specs/<story>/link.md` exists.
12
-
13
- **<name> (<role>)**
14
- - contract-check: this diff changes the contract surface without `Contract-Change: yes` + a re-locked
15
- contract. Route back to the architecture gate and re-lock before merge.
16
-
17
- **<name> (<role>)**
18
- - build-test-lint: lint/build/test is red, or the test doesn't exercise the new behavior. The gate must
19
- pass with a test that actually exercises the acceptance criterion.
20
-
21
- **<name> (<role>)**
22
- - file-boundary: this touches files the task's `Files:` list didn't declare. Re-scope the task (re-run
23
- `yad-spec`) instead of widening the diff.
24
-
25
- ## Routing / escalation
26
-
27
- **<name> (<role>)**
28
- - routing: risk is `high` / a contract|auth|payments surface is touched — needs a domain-owner approval
29
- per touched repo (same escalation as `yad-review-gate`). Run `bash checks/risk-route.sh <body>`.
30
-
31
- ## Non-blocking
32
-
33
- **<name> (<role>)**
34
- - Suggestion (non-blocking): …
35
-
36
- **<name> (<role>)**
37
- - Nit: …
38
-
39
- **<name> (<role>)**
40
- - Question: … (not blocking — just want to understand).
41
-
42
- ## Approval
43
-
44
- **<name> (<role>)**
45
- - Approving as `<role>`. Recorded in `approvals.json` (ship: `build-log.json`).
46
-
47
- ---
48
- > Tip (GitHub): paste the blocks you use most into your personal **Saved Replies**
49
- > (Settings → Saved replies) so they are one click away in any PR.
@@ -1,49 +0,0 @@
1
- # Review comments — canned replies
2
-
3
- Copy a block below into an MR review comment. Keep the `**<name> (<role>)**` header — it matches the
4
- SDLC review ledger (`reviews/<artifact>--<date>--comments.md`) so feedback stays attributable.
5
- `<role>` is one of `owner | reviewer | domain-owner`.
6
-
7
- ## Blocking (a gate or rule says stop — must be fixed before merge)
8
-
9
- **<name> (<role>)**
10
- - spec-link: this range has no `Task: <story>-<task>` trailer — the spec-link gate will fail. Add the
11
- trailer and make sure `specs/<story>/link.md` exists.
12
-
13
- **<name> (<role>)**
14
- - contract-check: this diff changes the contract surface without `Contract-Change: yes` + a re-locked
15
- contract. Route back to the architecture gate and re-lock before merge.
16
-
17
- **<name> (<role>)**
18
- - build-test-lint: lint/build/test is red, or the test doesn't exercise the new behavior. The gate must
19
- pass with a test that actually exercises the acceptance criterion.
20
-
21
- **<name> (<role>)**
22
- - file-boundary: this touches files the task's `Files:` list didn't declare. Re-scope the task (re-run
23
- `yad-spec`) instead of widening the diff.
24
-
25
- ## Routing / escalation
26
-
27
- **<name> (<role>)**
28
- - routing: risk is `high` / a contract|auth|payments surface is touched — needs a domain-owner approval
29
- per touched repo (same escalation as `yad-review-gate`). Run `bash checks/risk-route.sh <body>`.
30
-
31
- ## Non-blocking
32
-
33
- **<name> (<role>)**
34
- - Suggestion (non-blocking): …
35
-
36
- **<name> (<role>)**
37
- - Nit: …
38
-
39
- **<name> (<role>)**
40
- - Question: … (not blocking — just want to understand).
41
-
42
- ## Approval
43
-
44
- **<name> (<role>)**
45
- - Approving as `<role>`. Recorded in `approvals.json` (ship: `build-log.json`).
46
-
47
- ---
48
- > Tip (GitLab): paste the blocks you use most into your personal/group **Comment templates**
49
- > (Preferences → Comment templates) so they are one click away in any MR.