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/CHANGELOG.md +11 -2
- package/README.md +60 -671
- package/cli/docs.mjs +6 -5
- package/cli/manifest.mjs +12 -4
- package/cli/plan.mjs +35 -2
- package/cli/reconcile.mjs +7 -6
- package/cli/setup.mjs +17 -2
- package/package.json +2 -2
- package/skills/sdlc/install.sh +18 -1
- package/skills/sdlc/module-help.csv +0 -1
- package/skills/yad-docs-overview/SKILL.md +1 -1
- package/skills/yad-docs-overview/references/pipeline-model.md +22 -4
- package/skills/yad-review-comments/SKILL.md +0 -63
- package/skills/yad-review-comments/references/comment-conventions.md +0 -55
- package/skills/yad-review-comments/templates/github/REVIEW_COMMENTS.md +0 -49
- package/skills/yad-review-comments/templates/gitlab/REVIEW_COMMENTS.md +0 -49
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@
|
|
164
|
-
- uses: actions/setup-node@
|
|
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@
|
|
171
|
-
- uses: actions/upload-pages-artifact@
|
|
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@
|
|
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
|
|
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,
|
|
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": "
|
|
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 +
|
|
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",
|
package/skills/sdlc/install.sh
CHANGED
|
@@ -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-
|
|
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.
|