yadflow 2.9.0 → 2.11.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 +2 -2
- package/README.md +8 -2
- package/cli/manifest.mjs +2 -1
- package/cli/repo.mjs +56 -5
- package/package.json +2 -2
- package/skills/sdlc/config.yaml +3 -0
- package/skills/sdlc/install.sh +1 -1
- package/skills/sdlc/module-help.csv +2 -1
- package/skills/yad-checks/SKILL.md +2 -0
- package/skills/yad-checks/references/check-gates.md +5 -0
- package/skills/yad-checks/templates/checks/build-test-lint.sh +13 -1
- package/skills/yad-checks/templates/github/yad-checks.yml +2 -0
- package/skills/yad-checks/templates/gitlab/yad-checks.gitlab-ci.yml +2 -0
- package/skills/yad-commit/SKILL.md +14 -8
- package/skills/yad-implement/SKILL.md +3 -3
- package/skills/yad-ship/SKILL.md +7 -4
- package/skills/yad-sync-repos/SKILL.md +78 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
# [2.
|
|
1
|
+
# [2.11.0](https://github.com/abdelrahmannasr/yadflow/compare/v2.10.0...v2.11.0) (2026-06-15)
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
### Features
|
|
5
5
|
|
|
6
|
-
*
|
|
6
|
+
* add yad-sync-repos — switch every connected repo to its default branch + ff pull ([#67](https://github.com/abdelrahmannasr/yadflow/issues/67)) ([0abdcdf](https://github.com/abdelrahmannasr/yadflow/commit/0abdcdf80c8a0a6bfd8c94129fde90f1d35ec365)), closes [#30](https://github.com/abdelrahmannasr/yadflow/issues/30)
|
|
7
7
|
|
|
8
8
|
# [2.2.0](https://github.com/abdelrahmannasr/yadflow/compare/v2.1.0...v2.2.0) (2026-06-14)
|
|
9
9
|
|
package/README.md
CHANGED
|
@@ -100,6 +100,7 @@ with `npx` from your **product hub** repo — no clone needed.
|
|
|
100
100
|
| `yad open-pr [--repo <name>]` | Open a code-repo **task** PR/MR from the repo's platform template (build half). |
|
|
101
101
|
| `yad ship --type <t> -m <subject>` | Commit **and** open the task PR/MR in one step (`yad commit` then `yad open-pr`). |
|
|
102
102
|
| `yad repo list` / `yad repo refresh [name]` | List connected repos as **fresh / stale**, and re-pack a stale one — staleness is now an explicit human decision, never an automatic skill side-effect. |
|
|
103
|
+
| `yad repo sync [name]` | Switch every connected repo to its **default branch** and fast-forward it from origin (one or all). Dirty repos are skipped, never overwritten; fast-forward only. |
|
|
103
104
|
| `npx yadflow --version` | Print the installed CLI version. |
|
|
104
105
|
|
|
105
106
|
Flags: `--dir <path>` targets a project other than the cwd; `--force` re-copies unchanged files (or
|
|
@@ -134,7 +135,7 @@ simultaneous advancements can be lost; the next event or scheduled sweep re-sync
|
|
|
134
135
|
### What `setup` walks you through (10 steps)
|
|
135
136
|
|
|
136
137
|
1. **Preflight** — confirm the hub is a git repo (offers `git init`); check `git`/`node`/`npx`.
|
|
137
|
-
2. **Install the module** — copy all
|
|
138
|
+
2. **Install the module** — copy all 30 `yad-*` skills into the IDE skill dirs you pick
|
|
138
139
|
(`.claude/`, `.agents/`, `.zencoder/`, `.opencode/`) and register `_bmad/sdlc/`.
|
|
139
140
|
3. **Hub platform & roster** — detect GitHub/GitLab from the remote; record reviewers → `.sdlc/hub.json`.
|
|
140
141
|
Edit the roster any time afterwards with `yad roster` (no need to re-run the whole wizard).
|
|
@@ -191,7 +192,7 @@ with a fix-it hint per finding. Failures carry stable, greppable codes, also pri
|
|
|
191
192
|
|
|
192
193
|
Filing a bug? Attach `yad doctor --json` — it contains no secrets (names, paths, and check results only).
|
|
193
194
|
|
|
194
|
-
## Agent skills (all
|
|
195
|
+
## Agent skills (all 30)
|
|
195
196
|
|
|
196
197
|
The CLI **installs and wires** the module; the skills below are the **agents you invoke by name** in your
|
|
197
198
|
AI IDE (e.g. *“run `yad-epic`”*) to actually do the work. State lives in files you can also edit
|
|
@@ -204,6 +205,11 @@ directly. Each skill stops at a gate and never auto-advances unless a step has *
|
|
|
204
205
|
`.sdlc/repos.json`, then caches an AI-readable picture of each — a compressed Repomix pack and a
|
|
205
206
|
lightweight code-map (existing endpoints/events/data-models/modules), secret-scanned. Idempotent and
|
|
206
207
|
refreshable; staleness tracked by HEAD sha.
|
|
208
|
+
- **`yad-sync-repos`** — Brings every connected repo up to date in one shot: switches each repo in
|
|
209
|
+
`.sdlc/repos.json` to its `default_branch` and fast-forwards it from origin (local-user git, no stored
|
|
210
|
+
tokens). A working-tree-only maintenance op — never a gate, never writes the registry. A dirty repo is
|
|
211
|
+
skipped and reported (never overwritten); a diverged branch is left for manual resolution. Drives
|
|
212
|
+
`yad repo sync [name]`.
|
|
207
213
|
- **`yad-connect-design`** — Connects a design tool (Figma-first, pluggable) so the UI step can
|
|
208
214
|
materialize the actual feature design (mobile screens / web pages) inside it, alongside the Markdown.
|
|
209
215
|
Records the tool + project/file references in `.sdlc/design.json` (local-user / MCP-session auth, no
|
package/cli/manifest.mjs
CHANGED
|
@@ -10,7 +10,7 @@ import { readFileSync } from 'node:fs';
|
|
|
10
10
|
const { version } = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'));
|
|
11
11
|
export const VERSION = version;
|
|
12
12
|
|
|
13
|
-
// The
|
|
13
|
+
// The 30 hand-authored yad-* skills (mirrors skills/sdlc/install.sh).
|
|
14
14
|
export const SKILLS = [
|
|
15
15
|
'yad-analysis',
|
|
16
16
|
'yad-epic',
|
|
@@ -19,6 +19,7 @@ export const SKILLS = [
|
|
|
19
19
|
'yad-stories',
|
|
20
20
|
'yad-test-cases',
|
|
21
21
|
'yad-connect-repos',
|
|
22
|
+
'yad-sync-repos',
|
|
22
23
|
'yad-connect-design',
|
|
23
24
|
'yad-connect-testing',
|
|
24
25
|
'yad-connect-learning',
|
package/cli/repo.mjs
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
// `yad repo list|refresh` — connected-repo
|
|
2
|
-
//
|
|
3
|
-
//
|
|
1
|
+
// `yad repo list|refresh|sync` — connected-repo maintenance as explicit HUMAN decisions.
|
|
2
|
+
// list/refresh: staleness of the cached code-context pack (HEAD != syncedHead). Skill steps no longer
|
|
3
|
+
// silently repack a stale repo; they flag it and point here. (`yad check --fix` refreshes too — also
|
|
4
|
+
// human-invoked.) sync: switch every connected repo to its registry default_branch and fast-forward
|
|
5
|
+
// it to origin — a working-tree-only op (it never writes the registry; a dirty tree is skipped).
|
|
4
6
|
import path from 'node:path';
|
|
5
|
-
import { c, log, ok, info, warn, hand, fail, readJSON, writeJSON } from './lib.mjs';
|
|
7
|
+
import { c, log, ok, info, warn, hand, fail, readJSON, writeJSON, run } from './lib.mjs';
|
|
6
8
|
import { PROJECT_FILES } from './manifest.mjs';
|
|
7
9
|
import { gitHead, packRepo } from './setup.mjs';
|
|
8
10
|
|
|
@@ -18,6 +20,18 @@ function staleness(root, repo) {
|
|
|
18
20
|
return { head, stale: !!stale, unknown: !head };
|
|
19
21
|
}
|
|
20
22
|
|
|
23
|
+
// ---- git helpers for `sync` (local-user auth only — never embed credentials) ----
|
|
24
|
+
const git = (cwd, ...args) => run('git', args, { cwd });
|
|
25
|
+
const hasRemote = (cwd, remote = 'origin') => git(cwd, 'remote', 'get-url', remote).ok;
|
|
26
|
+
const isDirty = (cwd) => { const r = git(cwd, 'status', '--porcelain'); return r.ok && r.stdout.length > 0; };
|
|
27
|
+
const currentBranch = (cwd) => { const r = git(cwd, 'rev-parse', '--abbrev-ref', 'HEAD'); return r.ok ? r.stdout : null; };
|
|
28
|
+
// Registry default_branch wins; else origin/HEAD; else 'main'.
|
|
29
|
+
function defaultBranch(cwd, repo) {
|
|
30
|
+
if (repo.default_branch) return repo.default_branch;
|
|
31
|
+
const r = git(cwd, 'symbolic-ref', '--short', 'refs/remotes/origin/HEAD');
|
|
32
|
+
return r.ok && r.stdout ? r.stdout.replace(/^origin\//, '') : 'main';
|
|
33
|
+
}
|
|
34
|
+
|
|
21
35
|
export async function runRepo(root, { action = 'list', name, today } = {}) {
|
|
22
36
|
const { regPath, registry } = load(root);
|
|
23
37
|
if (!registry.repos.length) { warn('no repos registered (.sdlc/repos.json) — run `yad setup`'); return { repos: 0 }; }
|
|
@@ -55,7 +69,44 @@ export async function runRepo(root, { action = 'list', name, today } = {}) {
|
|
|
55
69
|
return { refreshed };
|
|
56
70
|
}
|
|
57
71
|
|
|
58
|
-
|
|
72
|
+
if (action === 'sync') {
|
|
73
|
+
const targets = name ? registry.repos.filter((r) => r.name === name) : registry.repos;
|
|
74
|
+
if (name && !targets.length) { fail(`unknown repo: ${name}`); process.exitCode = 1; return { synced: 0 }; }
|
|
75
|
+
log(c.bold('\nsync connected repos'));
|
|
76
|
+
let synced = 0, skipped = 0;
|
|
77
|
+
for (const repo of targets) {
|
|
78
|
+
const repoRoot = path.resolve(root, repo.path);
|
|
79
|
+
const tag = `${repo.name} ${c.dim(`(${repo.path})`)}`;
|
|
80
|
+
if (!gitHead(repoRoot)) { warn(`${tag} — not a git repo / HEAD unreadable — skipped`); skipped++; continue; }
|
|
81
|
+
if (isDirty(repoRoot)) { warn(`${repo.name} — ${c.yellow('dirty')} → SKIPPED (commit/stash first)`); skipped++; continue; }
|
|
82
|
+
const branch = defaultBranch(repoRoot, repo);
|
|
83
|
+
const remote = hasRemote(repoRoot);
|
|
84
|
+
if (remote) {
|
|
85
|
+
const f = git(repoRoot, 'fetch', 'origin', branch, '--prune');
|
|
86
|
+
if (!f.ok) { warn(`${repo.name} — fetch failed (${(f.stderr.split('\n')[0]) || 'auth?'}) — skipped`); skipped++; continue; }
|
|
87
|
+
}
|
|
88
|
+
if (currentBranch(repoRoot) !== branch) {
|
|
89
|
+
const co = git(repoRoot, 'checkout', branch);
|
|
90
|
+
if (!co.ok) { warn(`${repo.name} — cannot switch to ${branch} (${co.stderr.split('\n')[0] || 'no such branch'}) — skipped`); skipped++; continue; }
|
|
91
|
+
}
|
|
92
|
+
if (remote) {
|
|
93
|
+
const before = gitHead(repoRoot);
|
|
94
|
+
const m = git(repoRoot, 'merge', '--ff-only', `origin/${branch}`);
|
|
95
|
+
if (!m.ok) { warn(`${repo.name} — ${c.yellow('diverged')} on ${branch} → not fast-forwarded (resolve manually)`); skipped++; continue; }
|
|
96
|
+
ok(`${repo.name} ${c.dim('—')} ${before === gitHead(repoRoot) ? `already current on ${branch}` : `switched to ${branch}, pulled (ff)`}`);
|
|
97
|
+
} else {
|
|
98
|
+
ok(`${repo.name} ${c.dim('—')} on ${branch} (local-only, no remote)`);
|
|
99
|
+
}
|
|
100
|
+
synced++;
|
|
101
|
+
}
|
|
102
|
+
// A pulled repo's HEAD moves, so its cached code-context pack goes stale — point the human at refresh.
|
|
103
|
+
const staleCount = registry.repos.filter((r) => staleness(root, r).stale).length;
|
|
104
|
+
info(`synced ${synced}, skipped ${skipped}`);
|
|
105
|
+
if (staleCount) hand(`${staleCount} repo(s) now have a stale code-context pack — \`yad repo refresh\` to repack`);
|
|
106
|
+
return { synced, skipped, stale: staleCount };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
fail(`unknown repo action: ${action} (list | refresh | sync)`);
|
|
59
110
|
process.exitCode = 1;
|
|
60
111
|
return {};
|
|
61
112
|
}
|
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, ship, repo). A BMAD module +
|
|
3
|
+
"version": "2.11.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 + 30 yad-* skills.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "AbdelRahman Nasr",
|
|
7
7
|
"license": "MIT",
|
package/skills/sdlc/config.yaml
CHANGED
|
@@ -118,6 +118,9 @@ code_context:
|
|
|
118
118
|
# `yad repo refresh <repo>`) — they never silently
|
|
119
119
|
# re-pack. `yad repo list` shows fresh/stale;
|
|
120
120
|
# `yad repo refresh` and `yad check --fix` re-pack.
|
|
121
|
+
# `yad repo sync [<repo>]` (yad-sync-repos) switches every connected repo to its default_branch and
|
|
122
|
+
# fast-forwards it from origin before work starts — working-tree only (never writes the registry);
|
|
123
|
+
# a dirty repo is skipped, a diverged branch is left for manual resolution (fast-forward only).
|
|
121
124
|
load_in_front_phases: true # epic, architecture, ui-design, stories read the maps
|
|
122
125
|
# Auth: `yad-connect-repos` clones/fetches as the LOCAL user (SSH key or git credential helper),
|
|
123
126
|
# works for both github and gitlab (and self-hosted), and stores NO tokens in the registry.
|
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-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)
|
|
14
|
+
SKILLS=(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)
|
|
15
15
|
|
|
16
16
|
echo "Installing sdlc module from $ROOT/skills ..."
|
|
17
17
|
|
|
@@ -6,7 +6,8 @@ SDLC Workflow,yad-architecture,Author Architecture,AA,"Front state 3: with the a
|
|
|
6
6
|
SDLC Workflow,yad-ui,Author UI Design,AU,"Front state 5: with the ux-designer author ui-design.md and DESIGN.md, driving Impeccable slash-commands when installed. Never auto-advances.",,{epic: EP-<slug>},1-front,yad-review-gate,yad-review-gate,true,epics/EP-<slug>/,ui-design.md DESIGN.md state.json
|
|
7
7
|
SDLC Workflow,yad-stories,Author Stories,AS,"Front state 7: with the pm break the epic into repo-tagged stories with stable EP-<slug>-S0N IDs, one file each under stories/. Never auto-advances.",,{epic: EP-<slug>},1-front,yad-review-gate,yad-review-gate,true,epics/EP-<slug>/stories/,stories/EP-<slug>-S0N.md state.json
|
|
8
8
|
SDLC Workflow,yad-test-cases,Author Test Cases,TC,"Front state 9 (PARALLEL, non-blocking): opens when the stories gate passes — the epic is already ready-for-build, so the build half can start at the same time the tester works here. With the test architect (Murat) author test-cases.md covering the approved stories, and — when a testing tool is connected (.sdlc/testing.json) — generate/link the actual automation tests in it, recording test-links.json; otherwise produce the test-case artifact only. Its review never moves currentStep off ready-for-build. Never auto-advances.",,{epic: EP-<slug>},1-front,yad-review-gate,yad-review-gate,true,epics/EP-<slug>/,test-cases.md test-links.json state.json
|
|
9
|
-
SDLC Workflow,yad-connect-repos,Connect Code Repos,CR,"Setup/maintenance: connect code repos to the product hub so the front/brain phases are code-aware. Registers each repo (GitHub or GitLab, local-user auth, no stored tokens) in .sdlc/repos.json and caches a Repomix pack + a lightweight code-map (existing endpoints/events/data-models/modules, secret-scanned). Idempotent and refreshable; staleness tracked by HEAD sha. Run at setup or any time a new repo is added. Not a gated state — never touches epic state or approvals.",,{action: connect|refresh|list|disconnect} {repo: <name>} {path: <path-or-absolute>} {git_url: <ssh-or-https>} {domain_owner: <who>},0-setup,,yad-
|
|
9
|
+
SDLC Workflow,yad-connect-repos,Connect Code Repos,CR,"Setup/maintenance: connect code repos to the product hub so the front/brain phases are code-aware. Registers each repo (GitHub or GitLab, local-user auth, no stored tokens) in .sdlc/repos.json and caches a Repomix pack + a lightweight code-map (existing endpoints/events/data-models/modules, secret-scanned). Idempotent and refreshable; staleness tracked by HEAD sha. Run at setup or any time a new repo is added. Not a gated state — never touches epic state or approvals.",,{action: connect|refresh|list|disconnect} {repo: <name>} {path: <path-or-absolute>} {git_url: <ssh-or-https>} {domain_owner: <who>},0-setup,,yad-sync-repos,false,.sdlc/,repos.json code-context/<repo>/pack.md code-context/<repo>/code-map.md
|
|
10
|
+
SDLC Workflow,yad-sync-repos,Sync Connected Repos,SR,"Setup/maintenance: bring every connected repo up to date in one shot — switch each repo in .sdlc/repos.json to its default_branch and fast-forward it from origin (local-user git, no stored tokens). Working-tree only; never a gate and never writes the registry. A dirty repo is skipped and reported (never overwritten); a diverged branch is left for manual resolution (fast-forward only). After pulling, a repo's cached pack goes stale — points the human at yad repo refresh.",,{action: sync} {repo: <name>},0-setup,yad-connect-repos,yad-connect-design,false,,(none — working-tree only)
|
|
10
11
|
SDLC Workflow,yad-connect-design,Connect Design Tool,CD,"Setup/maintenance: connect a design tool (Figma-first, pluggable) to the product hub so the UI design step can materialize the actual feature design (mobile screens / web pages) inside it, alongside ui-design.md + DESIGN.md. Records the tool + project/file references in .sdlc/design.json (local-user / MCP-session auth, no stored tokens), detecting whether a design-tool MCP is available and degrading to markdown-only when absent. Idempotent and refreshable; one connection per project. Not a gated state — never touches epic state or approvals.",,{action: connect|refresh|list|disconnect} {tool: figma|pencil|none} {project_url: <team/project/file url>} {files: {web,mobile}},0-setup,,yad-ui,false,.sdlc/,design.json
|
|
11
12
|
SDLC Workflow,yad-connect-testing,Connect Testing Tool,CT,"Setup/maintenance: connect a testing tool (Playwright-first, pluggable) to the product hub so the test-cases step can implement the actual automation tests in it, alongside test-cases.md. Records the tool + project/suite references in .sdlc/testing.json (local-user / MCP-session auth, no stored tokens), detecting whether a testing-tool MCP is available and degrading to artifacts-only when absent. Idempotent and refreshable; one connection per project. Not a gated state — never touches epic state or approvals.",,{action: connect|refresh|list|disconnect} {tool: playwright|cypress|pytest|none} {project_url: <project/config reference>} {suites: {<repo>}},0-setup,,yad-test-cases,false,.sdlc/,testing.json
|
|
12
13
|
SDLC Workflow,yad-connect-learning,Connect Learning Tool,CL,"Setup/maintenance: connect a learning/tutoring tool (DeepTutor-first, pluggable) so the cross-cutting learning layer can tutor any team member, at any SDLC stage, in the context of what is being built. Records the tool + an optional grounded knowledge base in .sdlc/learning.json (local-user auth, no stored tokens), detecting whether the DeepTutor CLI is on PATH (a subprocess like Repomix, not an MCP) and degrading to harness-native tutoring when absent. Idempotent and refreshable; one connection per project. Not a gated state — never touches epic state or approvals.",,{action: connect|refresh|list|disconnect} {tool: deeptutor|none} {kb: <name>} {ground: true|false},0-setup,,yad-learn,false,.sdlc/,learning.json
|
|
@@ -16,6 +16,8 @@ in CI on every PR/MR and must pass before merge (build plan §C). Each is a smal
|
|
|
16
16
|
contract upstream, it **FAILS and routes back to the architecture gate**. The shared surface is
|
|
17
17
|
never widened from inside a code repo (Phase 2 contract representation: delimited block + SHA-256 lock).
|
|
18
18
|
3. **build/test/lint** — standard quality stage; tests must actually exercise new behavior, not just pass.
|
|
19
|
+
The CI job sets `YAD_TEST_MAX_WORKERS` (default `2`); the gate caps jest/vitest test concurrency at
|
|
20
|
+
that and is a no-op for other runners (see `references/check-gates.md`).
|
|
19
21
|
4. **verified-commits** — no unverified commits from unverified users: every commit in the range must
|
|
20
22
|
carry a signature the platform marks **Verified** AND be authored by a known identity
|
|
21
23
|
(`.sdlc/verified-authors`, generated from the hub roster's `email` fields). Enforced on the
|
|
@@ -46,6 +46,11 @@ repo uses. Each reads conventions established by earlier steps — it invents no
|
|
|
46
46
|
- Runs `npm run lint`, `npm run build`, `npm test` in order; any non-zero exit fails the gate.
|
|
47
47
|
- Tests must actually exercise behavior (build plan §C) — an empty or trivially-passing suite does not
|
|
48
48
|
satisfy the gate's intent.
|
|
49
|
+
- **Test worker cap.** When the CI job sets `YAD_TEST_MAX_WORKERS` (the templates default it to `2`)
|
|
50
|
+
and the repo's `test` script is jest/vitest, the gate forwards `--maxWorkers=<n>` to bound CI
|
|
51
|
+
concurrency. For any other runner (`node --test`, mocha, …) it is a no-op — the flag is never
|
|
52
|
+
passed, so the gate cannot break on an unknown option. Override it per repo via the
|
|
53
|
+
`YAD_TEST_MAX_WORKERS` CI variable, or unset it to remove the cap.
|
|
49
54
|
|
|
50
55
|
### Canonical `package.json` scripts (Node demo)
|
|
51
56
|
|
|
@@ -8,7 +8,19 @@ echo "[build/test/lint] lint…"
|
|
|
8
8
|
npm run --silent lint
|
|
9
9
|
echo "[build/test/lint] build…"
|
|
10
10
|
npm run --silent build
|
|
11
|
+
|
|
12
|
+
# Worker cap: when YAD_TEST_MAX_WORKERS is set AND the repo's test script is jest/vitest (the
|
|
13
|
+
# runners that accept --maxWorkers), forward it to bound CI test concurrency. For any other runner
|
|
14
|
+
# (node --test, mocha, …) it is a deliberate no-op so the gate never breaks on an unknown flag.
|
|
15
|
+
extra=""
|
|
16
|
+
if [ -n "${YAD_TEST_MAX_WORKERS:-}" ]; then
|
|
17
|
+
case "$(npm pkg get scripts.test 2>/dev/null || true)" in
|
|
18
|
+
*jest*|*vitest*) extra="-- --maxWorkers=${YAD_TEST_MAX_WORKERS}" ;;
|
|
19
|
+
esac
|
|
20
|
+
fi
|
|
11
21
|
echo "[build/test/lint] test…"
|
|
12
|
-
|
|
22
|
+
# Intentional word-splitting: $extra is either empty or `-- --maxWorkers=N`.
|
|
23
|
+
# shellcheck disable=SC2086
|
|
24
|
+
npm run --silent test $extra
|
|
13
25
|
|
|
14
26
|
echo "PASS [build/test/lint]: lint, build, and tests all green."
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: yad-commit
|
|
3
|
-
description: 'Build-half helper of the gated SDLC. Commit ONE staged atomic change by the conventions — a Conventional-Commits subject, the fixed trailer
|
|
3
|
+
description: 'Build-half helper of the gated SDLC. Commit ONE staged atomic change by the conventions — a Conventional-Commits subject, the fixed trailer order (Task → Contract-Change, plus an OPTIONAL Co-Authored-By), and an atomic-file guard (≤3 files). By default the commit carries NO AI footer: the human git author owns it, and a Co-Authored-By trailer is added ONLY when --ai <id> is explicitly passed (claude|copilot|cursor|coderabbit; default none = human-only). The flag is the sole switch — never add the footer on the AI''s own initiative. Drives the zero-dependency `yad commit` CLI; never auto-advances. Use when the user says "commit this", "commit by convention", or "make an atomic commit".'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# SDLC — Commit by Convention (build-half helper)
|
|
7
7
|
|
|
8
8
|
**Goal:** Turn ONE staged atomic change into a single commit that satisfies the project conventions
|
|
9
9
|
(`CONTRIBUTING.md` / `config.yaml` `build`): a Conventional-Commits subject, the fixed trailer order
|
|
10
|
-
`Task → Contract-Change → Co-Authored-By
|
|
11
|
-
step — the same engine `yad-implement`
|
|
10
|
+
`Task → Contract-Change → Co-Authored-By` (the footer **off by default** — added only via an explicit
|
|
11
|
+
`--ai`), and the atomic-file guard. This is the standalone commit step — the same engine `yad-implement`
|
|
12
|
+
and `yad-ship` use. It **never auto-advances**; it just commits.
|
|
12
13
|
|
|
13
14
|
## Conventions
|
|
14
15
|
|
|
@@ -23,14 +24,17 @@ step — the same engine `yad-implement` and `yad-ship` use. It **never auto-adv
|
|
|
23
24
|
task-scoped, so the trailer is optional there.
|
|
24
25
|
- **Contract-Change trailer** — `--contract-change` only when the diff touches the locked contract
|
|
25
26
|
surface; it routes the change back to the architecture gate.
|
|
26
|
-
- **AI co-author footer
|
|
27
|
-
|
|
27
|
+
- **AI co-author footer — OFF by default.** No `Co-Authored-By` trailer is written unless `--ai <id>`
|
|
28
|
+
explicitly names a tool. `--ai none` (the default) produces a clean human-only commit. The flag is the
|
|
29
|
+
**only** switch that adds the footer — never add it on the AI's own initiative just because a tool
|
|
30
|
+
helped author the diff. The human is always the author.
|
|
28
31
|
|
|
29
32
|
## Inputs
|
|
30
33
|
|
|
31
34
|
- `type` — Conventional-Commits type (required).
|
|
32
35
|
- `message` — the subject text (required), `-m "<subject>"`.
|
|
33
|
-
- `ai` — co-author footer: `claude|copilot|cursor|coderabbit|none` (default `none`
|
|
36
|
+
- `ai` — co-author footer: `claude|copilot|cursor|coderabbit|none` (default `none` = **no footer**;
|
|
37
|
+
the `Co-Authored-By` trailer appears only when this flag names a tool).
|
|
34
38
|
- `task` — Task trailer (optional; derived from the branch when omitted).
|
|
35
39
|
- `contractChange` — flag; mark the contract surface touched.
|
|
36
40
|
|
|
@@ -55,8 +59,10 @@ the architecture gate. To also open the PR/MR in the same step, use `yad-ship`.
|
|
|
55
59
|
## Hard rules
|
|
56
60
|
|
|
57
61
|
- **One staged atomic change = one commit.** Never bundle; never exceed the file boundary silently.
|
|
58
|
-
- **
|
|
59
|
-
|
|
62
|
+
- **No AI footer by default.** A `Co-Authored-By` trailer is written ONLY when `--ai <id>` is explicitly
|
|
63
|
+
passed; the default is a clean human-only commit. Never add it on your own initiative.
|
|
64
|
+
- **The human author owns the commit.** The AI is at most a `Co-Authored-By` footer, never the author.
|
|
65
|
+
- **Trailer order is fixed:** `Task → Contract-Change → Co-Authored-By` (the footer only when `--ai` is given).
|
|
60
66
|
- **Never widen the contract here.** A contract touch is flagged (`--contract-change`), not hidden.
|
|
61
67
|
|
|
62
68
|
## Reference
|
|
@@ -96,9 +96,9 @@ A contract change means the diff alters the agreed cross-repo shape itself.
|
|
|
96
96
|
### Step 6 — Commit on the task branch
|
|
97
97
|
Stage only the declared files. Commit with the convention: a conventional subject, a short body, and a
|
|
98
98
|
`Task: <story>-<task>` trailer (plus `Contract-Change: yes` if Step 5 applies). The human author owns the
|
|
99
|
-
commit;
|
|
100
|
-
`config.yaml` `build.ai_coauthor.allowed`
|
|
101
|
-
trailers in one contiguous block. Do not commit sibling tasks' work.
|
|
99
|
+
commit; **no `Co-Authored-By:` footer by default** — add one only when the human explicitly asks
|
|
100
|
+
(`yad commit --ai <id>` / the `.gitmessage` choices from `config.yaml` `build.ai_coauthor.allowed`),
|
|
101
|
+
never on the AI's own initiative. Keep all trailers in one contiguous block. Do not commit sibling tasks' work.
|
|
102
102
|
|
|
103
103
|
### Step 7 — Report; the advance decision belongs to the dial (Phase 4)
|
|
104
104
|
Report: the branch name, the files changed, how the change satisfies the task's acceptance criterion,
|
package/skills/yad-ship/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: yad-ship
|
|
3
|
-
description: 'Build-half helper of the gated SDLC — commit AND open the task PR/MR in one step. A thin orchestration over yad-commit then yad-open-pr: commit the staged atomic change by the conventions (Conventional-Commits subject, Task → Contract-Change
|
|
3
|
+
description: 'Build-half helper of the gated SDLC — commit AND open the task PR/MR in one step. A thin orchestration over yad-commit then yad-open-pr: commit the staged atomic change by the conventions (Conventional-Commits subject, Task → Contract-Change trailers, an OPTIONAL Co-Authored-By footer that is OFF by default and added only when --ai <id> is explicitly passed, ≤3-file atomic guard), then push the branch and open 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. Use when the user says "ship this task", "commit and open the PR", or "commit and raise the MR". (For the engineer review + merge, use yad-engineer-review.)'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# SDLC — Commit + Open PR/MR (build-half helper)
|
|
@@ -16,8 +16,8 @@ its own and **never merges**. The engineer review + merge are Step E (`yad-engin
|
|
|
16
16
|
task branch with the atomic change **already staged** (`git add`).
|
|
17
17
|
- Inherits every convention of the two steps it wraps:
|
|
18
18
|
- Commit: subject `<type>: <lowercase imperative, no trailing period>`, fixed trailer order
|
|
19
|
-
`Task → Contract-Change → Co-Authored-By
|
|
20
|
-
(`../yad-commit/SKILL.md`).
|
|
19
|
+
`Task → Contract-Change → Co-Authored-By` (the footer **off by default** — added only via an explicit
|
|
20
|
+
`--ai`), the ≤3-file atomic guard (`../yad-commit/SKILL.md`).
|
|
21
21
|
- PR/MR: pushed branch, the committed template prefilled, title defaulting to the commit subject,
|
|
22
22
|
roster auto-assign, risk routing (`../yad-open-pr/SKILL.md`).
|
|
23
23
|
- **Order matters:** the PR/MR is opened **only if the commit lands**. A failed commit, a tripped
|
|
@@ -26,7 +26,8 @@ its own and **never merges**. The engineer review + merge are Step E (`yad-engin
|
|
|
26
26
|
## Inputs
|
|
27
27
|
|
|
28
28
|
- `type` / `message` — the commit type + subject (required), `--type <t> -m "<subject>"`.
|
|
29
|
-
- `ai` — co-author footer: `claude|copilot|cursor|coderabbit|none` (default `none`
|
|
29
|
+
- `ai` — co-author footer: `claude|copilot|cursor|coderabbit|none` (default `none` = **no
|
|
30
|
+
footer**; the `Co-Authored-By` trailer appears only when this flag names a tool).
|
|
30
31
|
- `task` — Task trailer (optional; derived from the branch when omitted).
|
|
31
32
|
- `contractChange` — flag; marks the contract surface touched (commit trailer + PR escalation).
|
|
32
33
|
- `repo` / `risk` / `base` / `platform` / `title` — PR/MR options (see `yad-open-pr`).
|
|
@@ -54,6 +55,8 @@ the engineer review and merge are Step E (`yad-engineer-review`).
|
|
|
54
55
|
## Hard rules
|
|
55
56
|
|
|
56
57
|
- **One staged atomic task = one commit = one PR/MR.** Never bundle; never open from the default branch.
|
|
58
|
+
- **No AI footer by default.** The wrapped commit writes a `Co-Authored-By` trailer ONLY when `--ai <id>`
|
|
59
|
+
is explicitly passed; never add it on the AI's own initiative.
|
|
57
60
|
- **No PR without a landed commit.** A failed/`--dry-run` commit stops the step before pushing.
|
|
58
61
|
- **High risk routes to domain owners** — the same escalation as the gate.
|
|
59
62
|
- **Shipping here never merges.** The human owns the merge in `yad-engineer-review`.
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: yad-sync-repos
|
|
3
|
+
description: 'Brings every connected code repo up to date in one shot: switches each repo in .sdlc/repos.json to its registry default_branch and fast-forwards it to the latest from origin (local-user git, no stored tokens). A working-tree-only maintenance op — never a gate; it reads the registry but never writes it. A repo with uncommitted local changes is skipped and reported, never overwritten; a diverged branch is left for manual resolution (fast-forward only). After pulling, a repo''s cached code-context pack goes stale, so it points the human at `yad repo refresh`. Use when the user says "sync the repos", "switch all repos to the default branch", "pull latest on every repo", or "get the latest from the default branch".'
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# SDLC — Sync Connected Repos (one command, every repo on its default branch)
|
|
7
|
+
|
|
8
|
+
**Goal:** Before front/build work starts, get every connected code repo onto its default branch at the
|
|
9
|
+
latest commit, so nobody implements on a stale or wrong branch. This is the deterministic counterpart to
|
|
10
|
+
the `sync-before-implementation` discipline: one command instead of N manual `git checkout && git pull`.
|
|
11
|
+
|
|
12
|
+
This is **setup/maintenance, not a gated state** — it never touches `.sdlc/state.json`, any epic's
|
|
13
|
+
approvals, or the contract lock. It reads the project-wide registry and only mutates each repo's working
|
|
14
|
+
tree (branch + fast-forward). It writes **nothing** back to the registry.
|
|
15
|
+
|
|
16
|
+
## Conventions
|
|
17
|
+
|
|
18
|
+
- `{project-root}` resolves from the project working directory (the **product hub**).
|
|
19
|
+
- Registry: `{project-root}/.sdlc/repos.json` (project-wide; the same file `yad-connect-repos` writes).
|
|
20
|
+
Each repo entry supplies `path` (local path, relative to `{project-root}` or absolute) and
|
|
21
|
+
`default_branch` (the branch to land on).
|
|
22
|
+
- **Local-user auth only.** Fetch as the user running the command — their SSH key or git credential
|
|
23
|
+
helper. Store no tokens. Works for GitHub and GitLab alike. If a fetch fails on auth, the repo is
|
|
24
|
+
skipped (STOP and tell the user to authenticate); never embed a credential.
|
|
25
|
+
- The deterministic half is the **`yad repo sync` CLI command** — `yad repo sync` (all repos) or
|
|
26
|
+
`yad repo sync <name>` (one). This skill drives that command and explains the result.
|
|
27
|
+
|
|
28
|
+
## Inputs
|
|
29
|
+
|
|
30
|
+
- `action` — `sync` (this skill's only action).
|
|
31
|
+
- `repo` — optional short name (the registry `name`, e.g. `backend`) to sync a single repo; omit for all.
|
|
32
|
+
|
|
33
|
+
## On Activation
|
|
34
|
+
|
|
35
|
+
Run `yad repo sync [<repo>]`. For each target repo it performs, in order:
|
|
36
|
+
|
|
37
|
+
### Step 1 — Locate and validate the repo
|
|
38
|
+
Resolve the local path from the registry. If the path is not a git repo (no readable `HEAD`), warn and
|
|
39
|
+
skip — there is nothing to switch.
|
|
40
|
+
|
|
41
|
+
### Step 2 — Skip a dirty working tree (never overwrite local work)
|
|
42
|
+
If `git status --porcelain` is non-empty, the repo has uncommitted changes: **skip it and warn**
|
|
43
|
+
(`<name> dirty → SKIPPED (commit/stash first)`). The skill never stashes, resets, or force-checks-out —
|
|
44
|
+
preserving local work is a hard rule.
|
|
45
|
+
|
|
46
|
+
### Step 3 — Determine the default branch
|
|
47
|
+
Use the registry `default_branch`. If absent, fall back to the remote's `origin/HEAD`, else `main`.
|
|
48
|
+
|
|
49
|
+
### Step 4 — Fetch the latest
|
|
50
|
+
If the repo has an `origin` remote: `git fetch origin <default_branch> --prune`. A local-only repo
|
|
51
|
+
(no remote) skips the fetch and just switches branch.
|
|
52
|
+
|
|
53
|
+
### Step 5 — Switch to the default branch
|
|
54
|
+
If not already on it, `git checkout <default_branch>` (git creates the tracking branch from `origin/`
|
|
55
|
+
when needed).
|
|
56
|
+
|
|
57
|
+
### Step 6 — Fast-forward only
|
|
58
|
+
`git merge --ff-only origin/<default_branch>`. If the branch has diverged (a real merge would be
|
|
59
|
+
required), **do not pull** — warn (`<name> diverged → not fast-forwarded`) and leave it for the human.
|
|
60
|
+
Never create a merge commit, rebase, or force.
|
|
61
|
+
|
|
62
|
+
### Step 7 — Report
|
|
63
|
+
Per repo: `switched to <branch>, pulled (ff)` / `already current` / `SKIPPED (...)`. Pulling moves HEAD,
|
|
64
|
+
so any repo whose `HEAD` now differs from its registry `syncedHead` has a **stale code-context pack** —
|
|
65
|
+
the command ends by pointing the human at `yad repo refresh` to repack (that is a separate human
|
|
66
|
+
decision; this skill never repacks or writes the registry).
|
|
67
|
+
|
|
68
|
+
## Hard rules
|
|
69
|
+
|
|
70
|
+
- **Dirty = skip.** Never stash, reset, or discard uncommitted changes.
|
|
71
|
+
- **Fast-forward only.** A diverged branch is reported and left alone — no merge/rebase/force.
|
|
72
|
+
- **Local-user auth; store no tokens.** Fetch as the user; an auth failure skips that repo.
|
|
73
|
+
- **Working-tree only; not a gate.** Never write `.sdlc/repos.json`, `.sdlc/state.json`, approvals, or
|
|
74
|
+
the contract lock. Refreshing the cached pack is the separate, human-invoked `yad repo refresh`.
|
|
75
|
+
|
|
76
|
+
## Reference
|
|
77
|
+
- Registry schema + the HEAD-sha freshness rule this reports against: `../yad-connect-repos/references/repos-registry.md`.
|
|
78
|
+
- Connecting / refreshing repos and the code-context cache: `../yad-connect-repos/SKILL.md`.
|