yadflow 3.18.1 → 4.0.0-next.1
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 +355 -0
- package/README.md +79 -26
- package/bin/commands.mjs +41 -0
- package/bin/yad.mjs +437 -124
- package/cli/artifact-status.mjs +34 -15
- package/cli/checkpoint.mjs +69 -49
- package/cli/codeowners-command.mjs +170 -0
- package/cli/codeowners.mjs +397 -0
- package/cli/commit.mjs +13 -9
- package/cli/companion.mjs +2 -2
- package/cli/dial.mjs +183 -0
- package/cli/docs.mjs +88 -32
- package/cli/doctor.mjs +1472 -97
- package/cli/epic-state.mjs +3478 -232
- package/cli/epic.mjs +506 -0
- package/cli/errors.mjs +4 -1
- package/cli/gate.mjs +1002 -209
- package/cli/history.mjs +556 -0
- package/cli/hook.mjs +266 -55
- package/cli/hubcommit.mjs +6 -17
- package/cli/index-command.mjs +87 -0
- package/cli/ledger.mjs +57 -7
- package/cli/lib.mjs +184 -18
- package/cli/manifest.mjs +367 -56
- package/cli/migrate.mjs +726 -53
- package/cli/mode.mjs +170 -0
- package/cli/next.mjs +349 -90
- package/cli/openpr.mjs +191 -39
- package/cli/people.mjs +654 -0
- package/cli/plan.mjs +417 -132
- package/cli/platform.mjs +110 -129
- package/cli/product-index.mjs +287 -0
- package/cli/protection.mjs +706 -0
- package/cli/reconcile.mjs +38 -12
- package/cli/repo-publish.mjs +24 -26
- package/cli/repo.mjs +23 -14
- package/cli/report.mjs +21 -15
- package/cli/review.mjs +24 -27
- package/cli/riskmap-command.mjs +289 -0
- package/cli/riskmap.mjs +373 -0
- package/cli/setup.mjs +139 -287
- package/cli/ship.mjs +7 -6
- package/cli/skill.mjs +180 -0
- package/cli/skip.mjs +211 -30
- package/cli/thread.mjs +42 -17
- package/cli/tidy.mjs +20 -20
- package/cli/update-commit.mjs +22 -22
- package/cli/usage.mjs +115 -109
- package/package.json +3 -3
- package/skills/sdlc/config.yaml +166 -87
- package/skills/sdlc/module-help.csv +35 -35
- package/skills/yad-analysis/SKILL.md +125 -65
- package/skills/yad-architecture/SKILL.md +34 -23
- package/skills/yad-architecture/references/contract-format.md +10 -8
- package/skills/yad-backfill/SKILL.md +14 -8
- package/skills/yad-backfill/references/backfill.md +1 -1
- package/skills/yad-change/SKILL.md +127 -52
- package/skills/yad-change/references/triage.md +42 -28
- package/skills/yad-checks/SKILL.md +89 -45
- package/skills/yad-checks/references/check-gates.md +315 -92
- package/skills/yad-checks/templates/checks/build-test-lint.sh +25 -7
- package/skills/yad-checks/templates/checks/commit-message.sh +17 -3
- package/skills/yad-checks/templates/checks/contract-check.sh +58 -2
- package/skills/yad-checks/templates/checks/epic-open.sh +3 -3
- package/skills/yad-checks/templates/checks/install-deps.sh +46 -0
- package/skills/yad-checks/templates/checks/ledger-guard.sh +94 -18
- package/skills/yad-checks/templates/checks/lineage-check.sh +23 -9
- package/skills/yad-checks/templates/checks/package-manager.sh +140 -0
- package/skills/yad-checks/templates/checks/reconcile-debt-check.sh +4 -4
- package/skills/yad-checks/templates/checks/risk-map-check.sh +438 -0
- package/skills/yad-checks/templates/checks/verified-commits.sh +20 -46
- package/skills/yad-checks/templates/github/yad-checks.yml +37 -5
- package/skills/yad-checks/templates/github/yad-hub-checks.yml +5 -5
- package/skills/yad-checks/templates/github/yad-update-guard.yml +3 -4
- package/skills/yad-checks/templates/github/yad-verified-commits.yml +4 -4
- package/skills/yad-checks/templates/gitlab/.gitlab-ci.yml +7 -1
- package/skills/yad-checks/templates/gitlab/yad-checks.gitlab-ci.yml +22 -4
- package/skills/yad-checks/templates/gitlab/yad-hub-checks.gitlab-ci.yml +5 -5
- package/skills/yad-checks/templates/gitlab/yad-verified-commits.gitlab-ci.yml +4 -4
- package/skills/yad-checks/templates/hooks/ledger-guard-cursor.sh +91 -0
- package/skills/yad-checks/templates/hooks/ledger-guard.sh +38 -7
- package/skills/yad-commit/SKILL.md +6 -6
- package/skills/yad-connect-design/SKILL.md +6 -6
- package/skills/yad-connect-design/references/design-context.md +1 -1
- package/skills/yad-connect-design/references/design-registry.md +2 -2
- package/skills/yad-connect-docs/SKILL.md +12 -12
- package/skills/yad-connect-docs/references/docs-registry.md +1 -1
- package/skills/yad-connect-learning/SKILL.md +5 -5
- package/skills/yad-connect-learning/references/learning-registry.md +2 -2
- package/skills/yad-connect-repos/SKILL.md +92 -54
- package/skills/yad-connect-repos/references/code-context.md +6 -6
- package/skills/yad-connect-repos/references/hub-config.md +68 -58
- package/skills/yad-connect-repos/references/repos-registry.md +10 -9
- package/skills/yad-connect-repos/references/risk-map.md +81 -0
- package/skills/yad-connect-testing/SKILL.md +6 -6
- package/skills/yad-connect-testing/references/testing-context.md +3 -4
- package/skills/yad-connect-testing/references/testing-registry.md +2 -2
- package/skills/yad-defects/SKILL.md +8 -8
- package/skills/yad-discovery/SKILL.md +130 -94
- package/skills/yad-discovery/references/discovery-schema.md +23 -7
- package/skills/yad-discovery/references/foundation-schema.md +374 -0
- package/skills/yad-docs/SKILL.md +16 -11
- package/skills/yad-docs/references/data-mapping.md +9 -7
- package/skills/yad-docs/templates/app/package-lock.json +3 -3
- package/skills/yad-docs-overview/SKILL.md +32 -17
- package/skills/yad-docs-overview/references/pipeline-model.md +47 -28
- package/skills/yad-docs-sync/SKILL.md +10 -5
- package/skills/yad-docs-sync/references/staleness.md +8 -7
- package/skills/yad-engineer-review/SKILL.md +88 -24
- package/skills/yad-engineer-review/references/ship-and-record.md +25 -16
- package/skills/yad-epic/SKILL.md +178 -100
- package/skills/yad-epic/references/state-schema.md +626 -117
- package/skills/yad-hub-bridge/SKILL.md +66 -48
- package/skills/yad-hub-bridge/references/bridge.md +110 -83
- package/skills/yad-hub-bridge/references/login-roster.md +163 -70
- package/skills/yad-hub-bridge/templates/checks/hub-route.sh +22 -19
- package/skills/yad-hub-bridge/templates/github/yad-gate-sync.yml +34 -14
- package/skills/yad-hub-bridge/templates/gitlab/gitlab-ci.include-root.yml +2 -2
- package/skills/yad-hub-bridge/templates/gitlab/yad-gate-sync.gitlab-ci.yml +22 -12
- package/skills/yad-implement/SKILL.md +29 -15
- package/skills/yad-implement/references/implement-conventions.md +2 -2
- package/skills/yad-learn/SKILL.md +9 -9
- package/skills/yad-learn/references/learning-state.md +2 -2
- package/skills/yad-open-pr/SKILL.md +64 -29
- package/skills/yad-pair-review/SKILL.md +18 -16
- package/skills/yad-pair-review/references/session-state.md +4 -4
- package/skills/yad-pr-template/SKILL.md +48 -27
- package/skills/yad-pr-template/references/risk-routing.md +97 -24
- package/skills/yad-pr-template/templates/checks/pr-template.sh +37 -15
- package/skills/yad-pr-template/templates/checks/pr-title.sh +27 -13
- package/skills/yad-pr-template/templates/checks/risk-route.sh +107 -14
- package/skills/yad-pr-template/templates/github/pull_request_template.md +7 -5
- package/skills/yad-pr-template/templates/gitlab/merge_request_templates/Default.md +7 -5
- package/skills/yad-pr-template/templates/hub/github/pull_request_template.md +15 -14
- package/skills/yad-pr-template/templates/hub/gitlab/merge_request_templates/Default.md +15 -13
- package/skills/yad-reconcile/SKILL.md +3 -3
- package/skills/yad-report/SKILL.md +5 -5
- package/skills/yad-review-companion/SKILL.md +12 -9
- package/skills/yad-review-gate/SKILL.md +198 -79
- package/skills/yad-review-gate/references/gating.md +230 -54
- package/skills/yad-run/SKILL.md +86 -56
- package/skills/yad-run/references/run-loop.md +67 -45
- package/skills/yad-ship/SKILL.md +18 -14
- package/skills/yad-spec/SKILL.md +31 -17
- package/skills/yad-spec/references/spec-handoff.md +17 -5
- package/skills/yad-status/SKILL.md +114 -56
- package/skills/yad-stories/SKILL.md +42 -27
- package/skills/yad-stories/references/story-schema.md +10 -9
- package/skills/yad-stub/SKILL.md +59 -48
- package/skills/yad-sync-repos/SKILL.md +3 -3
- package/skills/yad-test-cases/SKILL.md +37 -30
- package/skills/yad-test-cases/references/test-cases-schema.md +8 -5
- package/skills/yad-timeline/SKILL.md +8 -7
- package/skills/yad-ui/SKILL.md +46 -25
- package/cli/roster.mjs +0 -164
- package/skills/sdlc/install.sh +0 -68
|
@@ -1,24 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# verified-commits gate.
|
|
3
|
-
# Every commit in the range under review must
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
# roster's `email` fields + hub.json `verified_authors`). Unsigned commits and commits from unknown
|
|
8
|
-
# authors do not reach merge — on the product hub and on every connected repo alike.
|
|
3
|
+
# Every commit in the range under review must carry a signature the PLATFORM marks as verified (the
|
|
4
|
+
# GitHub/GitLab "Verified" badge — proves the commit was signed by a key registered to the account that
|
|
5
|
+
# owns the author email). Unsigned commits do not reach merge — on the Product and on every connected
|
|
6
|
+
# repo alike.
|
|
9
7
|
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
8
|
+
# There is no author allowlist. It used to be `.sdlc/verified-authors`, generated from the Product
|
|
9
|
+
# roster's emails; yadflow removed the roster (E62) and keeps no list of people. Who may author a commit
|
|
10
|
+
# is who has write access to the repository — the platform's own answer, which never goes stale — and
|
|
11
|
+
# the signature proves which platform account made the commit. A `.sdlc/verified-authors` file an older
|
|
12
|
+
# release generated is no longer read, and the gate says so when it finds one.
|
|
13
13
|
#
|
|
14
14
|
# Merge-commit signature exemption: a merge that introduces NO content of its own (its combined diff
|
|
15
15
|
# is empty — no conflict-resolution / evil-merge hunks) is signature-waived, because every change it
|
|
16
|
-
# carries already lives in its individually
|
|
16
|
+
# carries already lives in its individually signature-checked parents. This unblocks self-hosted
|
|
17
17
|
# GitLab, which does not sign UI-created merge commits (the signature API returns 404). A merge that
|
|
18
18
|
# DOES introduce content of its own still requires a verified signature (fail-closed).
|
|
19
19
|
#
|
|
20
20
|
# Degradation is explicit, never silent:
|
|
21
|
-
# - no allowlist file -> author check SKIPPED with a warning (configure emails, re-wire)
|
|
22
21
|
# - no GitHub/GitLab remote -> signature check SKIPPED with a warning (no platform, no badge)
|
|
23
22
|
# - platform API unreachable -> FAIL closed (a security gate must not pass on a broken check)
|
|
24
23
|
#
|
|
@@ -64,13 +63,10 @@ if [ -z "$commits" ]; then
|
|
|
64
63
|
exit 0
|
|
65
64
|
fi
|
|
66
65
|
|
|
67
|
-
# ---- author allowlist
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
authors_on=1
|
|
72
|
-
else
|
|
73
|
-
echo "WARN [verified-commits]: ${ALLOWLIST} not found — author allowlist NOT enforced. Add 'email' to the hub roster (or hub.json 'verified_authors'), then run \`yad check --fix\`."
|
|
66
|
+
# ---- the author allowlist is gone (E62) ------------------------------------------------------------
|
|
67
|
+
# Said out loud when an older one is still on disk, so nobody edits it believing it decides something.
|
|
68
|
+
if [ -f .sdlc/verified-authors ]; then
|
|
69
|
+
echo "note [verified-commits]: .sdlc/verified-authors is no longer read — write access to the repo decides who can author; the signature is still required."
|
|
74
70
|
fi
|
|
75
71
|
|
|
76
72
|
# ---- platform for the signature check (override with SDLC_PLATFORM=github|gitlab|none) -----------
|
|
@@ -120,7 +116,7 @@ signature_verified() {
|
|
|
120
116
|
# 0 when a merge commit introduced NO content of its own. The combined diff (--cc) lists only hunks
|
|
121
117
|
# that differ from ALL parents, so empty output means every change lives in an individually-checked
|
|
122
118
|
# parent — there is no conflict-resolution or evil-merge content unique to the merge commit, hence
|
|
123
|
-
# nothing an
|
|
119
|
+
# nothing an unsigned change could smuggle in past the per-parent signature checks.
|
|
124
120
|
merge_introduces_no_content() {
|
|
125
121
|
# `local` on its own line: `local out=$(...)` would mask the substitution's exit status (local
|
|
126
122
|
# always returns 0). A git error (e.g. a parent tree missing in a shallow clone) must fail closed —
|
|
@@ -134,37 +130,15 @@ rc=0
|
|
|
134
130
|
while IFS= read -r sha; do
|
|
135
131
|
[ -z "$sha" ] && continue
|
|
136
132
|
short="$(git log -1 --format=%h "$sha")"
|
|
137
|
-
author="$(git log -1 --format=%ae "$sha" | tr '[:upper:]' '[:lower:]')"
|
|
138
|
-
# The gate-sync bot is a machine identity, not a roster human — waive the allowlist for it. Its
|
|
139
|
-
# commits are still held to the SIGNATURE check below, so a contributor cannot spoof the bot author
|
|
140
|
-
# to dodge the allowlist (a forged-author commit is not platform-Verified). Mirrors how the
|
|
141
|
-
# platform-committer merge commits are allowlist-exempt but signature-covered.
|
|
142
|
-
is_bot=0
|
|
143
|
-
case "${author}|$(git log -1 --format=%an "$sha" | tr '[:upper:]' '[:lower:]')" in *yad-gate-sync*) is_bot=1 ;; esac
|
|
144
133
|
|
|
145
|
-
# A merge commit (2+ parents) is
|
|
146
|
-
#
|
|
147
|
-
#
|
|
148
|
-
#
|
|
149
|
-
#
|
|
134
|
+
# A merge commit (2+ parents) whose content is all in its parents may go unsigned — see
|
|
135
|
+
# merge_introduces_no_content. Every other commit, merges with content of their own included, needs
|
|
136
|
+
# the signature: a platform merge commit is Verified, so a locally-forged merge pushed
|
|
137
|
+
# direct-to-default cannot dodge the check. This matters for the push-on-default yad-update-guard,
|
|
138
|
+
# which (unlike the PR-triggered gates) sees merge commits.
|
|
150
139
|
is_merge=0
|
|
151
140
|
[ "$(git log -1 --format=%P "$sha" | wc -w | tr -d '[:space:]')" -ge 2 ] && is_merge=1
|
|
152
141
|
|
|
153
|
-
if [ "$authors_on" = 1 ] && [ "$is_bot" = 0 ] && [ "$is_merge" = 0 ]; then
|
|
154
|
-
# tolerate CRLF / stray surrounding whitespace in a hand-edited allowlist
|
|
155
|
-
if grep -vE '^[[:space:]]*(#|$)' "$ALLOWLIST" | tr -d '\r' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' \
|
|
156
|
-
| tr '[:upper:]' '[:lower:]' | grep -qxF "$author"; then
|
|
157
|
-
echo "PASS [verified-commits]: ${short} author <${author}> is a known identity"
|
|
158
|
-
else
|
|
159
|
-
echo "FAIL [verified-commits]: ${short} author <${author}> is not in ${ALLOWLIST} — unverified user."
|
|
160
|
-
rc=1
|
|
161
|
-
fi
|
|
162
|
-
elif [ "$is_bot" = 1 ]; then
|
|
163
|
-
echo "PASS [verified-commits]: ${short} gate-sync bot — allowlist waived (signature still required)"
|
|
164
|
-
elif [ "$is_merge" = 1 ]; then
|
|
165
|
-
echo "PASS [verified-commits]: ${short} merge commit — allowlist waived (signature still required)"
|
|
166
|
-
fi
|
|
167
|
-
|
|
168
142
|
if [ -n "$platform" ]; then
|
|
169
143
|
if signature_verified "$sha"; then
|
|
170
144
|
echo "PASS [verified-commits]: ${short} signature verified by ${platform}"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# yad-managed: yad-checks
|
|
2
|
-
# SDLC check gates (Phase 3 build plan §C). The
|
|
2
|
+
# SDLC check gates (Phase 3 build plan §C). The gates below run on every PR and must pass before
|
|
3
3
|
# merge. They are CI-agnostic bash in checks/ — this workflow just invokes them with the PR base.
|
|
4
4
|
# This workflow file is OWNED by yad-checks (the marker on line 1 identifies it). It runs
|
|
5
5
|
# independently of any other workflow in .github/workflows/, so wiring never edits a foreign workflow.
|
|
@@ -29,17 +29,49 @@ jobs:
|
|
|
29
29
|
with: { fetch-depth: 0 }
|
|
30
30
|
- run: bash checks/contract-check.sh "origin/${{ github.base_ref }}"
|
|
31
31
|
|
|
32
|
+
# E65/E66 — the risk map (.sdlc/risk-map: a risk level per directory, no names). Warnings, and the
|
|
33
|
+
# approver count read from the base branch's map. The script always exits 0: the count is reported,
|
|
34
|
+
# not enforced, and a stale map never blocks a merge.
|
|
35
|
+
risk-map:
|
|
36
|
+
runs-on: ubuntu-latest
|
|
37
|
+
if: github.event.action != 'edited'
|
|
38
|
+
steps:
|
|
39
|
+
- uses: actions/checkout@v4
|
|
40
|
+
with: { fetch-depth: 0 }
|
|
41
|
+
- run: bash checks/risk-map-check.sh "origin/${{ github.base_ref }}"
|
|
42
|
+
|
|
32
43
|
build-test-lint:
|
|
33
44
|
runs-on: ubuntu-latest
|
|
34
45
|
if: github.event.action != 'edited'
|
|
35
46
|
env:
|
|
36
47
|
YAD_TEST_MAX_WORKERS: "2" # cap jest/vitest test workers in CI; ignored by other runners
|
|
48
|
+
YAD_NODE_VERSION: ${{ vars.YAD_NODE_VERSION || '22' }} # repo variable; a current 20, 22 or 24 line (Node 25+ has no Corepack)
|
|
49
|
+
NX_BASE: ${{ github.event.pull_request.base.sha }}
|
|
50
|
+
NX_HEAD: ${{ github.event.pull_request.head.sha }}
|
|
51
|
+
COREPACK_HOME: ${{ github.workspace }}/.corepack-cache # pinned-manager downloads, cached below
|
|
37
52
|
steps:
|
|
38
53
|
- uses: actions/checkout@v4
|
|
39
|
-
with:
|
|
54
|
+
with:
|
|
55
|
+
fetch-depth: 0
|
|
56
|
+
filter: blob:none
|
|
40
57
|
- uses: actions/setup-node@v4
|
|
41
|
-
with:
|
|
42
|
-
|
|
58
|
+
with:
|
|
59
|
+
node-version: ${{ env.YAD_NODE_VERSION }}
|
|
60
|
+
# Dependency cache for either manager: npm's ~/.npm, pnpm's content-addressable store, and the
|
|
61
|
+
# Corepack home where `corepack prepare` keeps the pinned manager (otherwise re-downloaded from
|
|
62
|
+
# the registry on every job, even when the dependency cache hits). Keyed on the lockfiles AND
|
|
63
|
+
# package.json, since a packageManager bump changes no lockfile. setup-node's own `cache:` must
|
|
64
|
+
# name the manager up front (and needs pnpm on PATH before it runs), which package.json only
|
|
65
|
+
# decides later — so cache the directories directly.
|
|
66
|
+
- uses: actions/cache@v4
|
|
67
|
+
with:
|
|
68
|
+
path: |
|
|
69
|
+
~/.npm
|
|
70
|
+
~/.local/share/pnpm/store
|
|
71
|
+
${{ env.COREPACK_HOME }}
|
|
72
|
+
key: yad-deps-${{ runner.os }}-node${{ env.YAD_NODE_VERSION }}-${{ hashFiles('package-lock.json', 'npm-shrinkwrap.json', 'pnpm-lock.yaml', 'package.json') }}
|
|
73
|
+
restore-keys: yad-deps-${{ runner.os }}-node${{ env.YAD_NODE_VERSION }}-
|
|
74
|
+
- run: bash checks/install-deps.sh
|
|
43
75
|
- run: bash checks/build-test-lint.sh
|
|
44
76
|
|
|
45
77
|
# Phase 6 — feature-thread gates. lineage-check: the change links a real threaded epic. epic-open:
|
|
@@ -99,7 +131,7 @@ jobs:
|
|
|
99
131
|
body="$(mktemp)"; printf '%s' "$PR_BODY" > "$body"
|
|
100
132
|
bash checks/pr-template.sh --profile code "$body"
|
|
101
133
|
|
|
102
|
-
# No unverified commits
|
|
134
|
+
# No unverified commits: a platform-Verified signature on every commit (write access decides who authors).
|
|
103
135
|
# Reads the commit range, not the title/body — skip it on a bare `edited` event.
|
|
104
136
|
verified-commits:
|
|
105
137
|
runs-on: ubuntu-latest
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# yad-managed: yad-checks
|
|
2
|
-
# Pattern gates for the PRODUCT
|
|
3
|
-
# review/EP-* PRs are
|
|
4
|
-
# and the
|
|
5
|
-
# every other PR is a tooling/code change to the
|
|
2
|
+
# Pattern gates for the PRODUCT. They run with `--profile hub` and split by head branch:
|
|
3
|
+
# review/EP-* PRs are Shape artifact-review vehicles — a `review: <artifact> (EP-<slug>)` title
|
|
4
|
+
# and the Product artifact-review template body.
|
|
5
|
+
# every other PR is a tooling/code change to the Product itself — a Conventional-Commits title and the
|
|
6
6
|
# code task template body (same convention the code repos use).
|
|
7
7
|
# The head ref is passed via --head so the gate can tell the two apart. Commit subjects are always
|
|
8
8
|
# Conventional-Commits. Standalone workflow so it never collides with the code-repo yad-checks workflow.
|
|
@@ -27,7 +27,7 @@ jobs:
|
|
|
27
27
|
|
|
28
28
|
# Pass the title via env (never interpolate untrusted ${{ }} into a run line — injection-safe).
|
|
29
29
|
# fetch-depth: 0 + the base ref give us the PR's changed paths, so the gate can reject an artifact
|
|
30
|
-
# change (epics/**) riding a non-review head past the
|
|
30
|
+
# change (epics/**) riding a non-review head past the Shape review with a plain code title.
|
|
31
31
|
pr-title:
|
|
32
32
|
runs-on: ubuntu-latest
|
|
33
33
|
env:
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
# yad-managed: yad-checks
|
|
2
2
|
# yad-update-guard — integrity gate for DIRECT pushes to the default branch.
|
|
3
3
|
#
|
|
4
|
-
# `yad update --push` commits the applied SDLC drift (skills, gate scripts, CI wiring
|
|
5
|
-
#
|
|
4
|
+
# `yad update --push` commits the applied SDLC drift (skills, gate scripts, CI wiring) and pushes it
|
|
5
|
+
# straight to the default branch with NO pull request — so the pull_request
|
|
6
6
|
# gate suite never fires. This workflow is the "skipped from CI EXCEPT verified-commits + the pattern
|
|
7
7
|
# gate" contract: on every push to the default branch it runs ONLY those two gates over the pushed
|
|
8
8
|
# range. It is not scoped to yad-update commits on purpose — ANY direct-to-main commit (a hotfix, a
|
|
9
9
|
# force-push) then gets signature + subject-format checked, which is a strictly-good invariant. A
|
|
10
|
-
# normal PR merge sails through: a GitHub merge/squash commit is platform-Verified,
|
|
11
|
-
# waives the allowlist for merge commits (whose author is whoever clicked merge), and commit-message.sh
|
|
10
|
+
# normal PR merge sails through: a GitHub merge/squash commit is platform-Verified, and commit-message.sh
|
|
12
11
|
# skips merges. CAVEAT: a *rebase-merge* recreates the PR commits WITHOUT GitHub's signature, so they
|
|
13
12
|
# lose the Verified badge and fail the signature check on the direct push — teams using rebase-merge
|
|
14
13
|
# should sign their commits or not wire this guard.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# yad-managed: yad-checks
|
|
2
|
-
# verified-commits gate for the PRODUCT
|
|
3
|
-
# must contain only commits whose signature the platform marks Verified
|
|
4
|
-
#
|
|
5
|
-
# Standalone workflow so it never collides with the
|
|
2
|
+
# verified-commits gate for the PRODUCT: every PR (including the Shape review/EP-* PRs)
|
|
3
|
+
# must contain only commits whose signature the platform marks Verified. Who may author them is who has
|
|
4
|
+
# write access to the repo — yadflow keeps no author list.
|
|
5
|
+
# Standalone workflow so it never collides with the Product-flavored yad-checks workflow.
|
|
6
6
|
name: yad-verified-commits
|
|
7
7
|
on:
|
|
8
8
|
pull_request:
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
stages: [spec-link, contract-check, build-test-lint, verified-commits]
|
|
5
5
|
|
|
6
6
|
default:
|
|
7
|
-
image: node
|
|
7
|
+
image: node:${YAD_NODE_VERSION}
|
|
8
8
|
|
|
9
9
|
.mr_only:
|
|
10
10
|
rules:
|
|
@@ -12,6 +12,9 @@ default:
|
|
|
12
12
|
|
|
13
13
|
variables:
|
|
14
14
|
GIT_DEPTH: "0" # full history so the gates can diff against the target branch
|
|
15
|
+
YAD_NODE_VERSION: "22" # override with a project/group CI variable (a current 20, 22 or 24 line; Node 25+ has no Corepack)
|
|
16
|
+
NX_BASE: $CI_MERGE_REQUEST_DIFF_BASE_SHA
|
|
17
|
+
NX_HEAD: $CI_COMMIT_SHA
|
|
15
18
|
|
|
16
19
|
spec-link:
|
|
17
20
|
stage: spec-link
|
|
@@ -28,7 +31,10 @@ contract-check:
|
|
|
28
31
|
build-test-lint:
|
|
29
32
|
stage: build-test-lint
|
|
30
33
|
extends: .mr_only
|
|
34
|
+
variables:
|
|
35
|
+
YAD_TEST_MAX_WORKERS: "2" # cap jest/vitest test workers in CI; ignored by other runners
|
|
31
36
|
script:
|
|
37
|
+
- bash checks/install-deps.sh
|
|
32
38
|
- bash checks/build-test-lint.sh
|
|
33
39
|
|
|
34
40
|
# Needs a CI/CD variable GITLAB_TOKEN (or SDLC_API_TOKEN) with read_api scope — CI_JOB_TOKEN cannot
|
|
@@ -14,10 +14,19 @@
|
|
|
14
14
|
# top-level `default:` merges into the HOST root pipeline; if the host already sets a top-level
|
|
15
15
|
# `image:`, GitLab rejects the whole config ("image is defined in top-level and 'default:' entry").
|
|
16
16
|
# Putting them on the anchor scopes them to the gate jobs (which all `extends: .sdlc_mr_only`) and
|
|
17
|
-
# never touches the host root.
|
|
17
|
+
# never touches the host root. The same goes for the gate jobs' own variables (`YAD_NODE_VERSION`,
|
|
18
|
+
# `NX_BASE`, `NX_HEAD`): an included top-level `variables:` merges into the host's globals, where a
|
|
19
|
+
# generic name like NX_BASE can collide with one the host already computes for its own Nx jobs.
|
|
20
|
+
# Only `GIT_DEPTH` stays global — the gates need the full history and a host that sets its own wins.
|
|
18
21
|
|
|
19
22
|
.sdlc_mr_only:
|
|
20
|
-
image: node
|
|
23
|
+
image: node:${YAD_NODE_VERSION}
|
|
24
|
+
variables:
|
|
25
|
+
# A project/group CI/CD variable overrides a YAML value, so YAD_NODE_VERSION can be changed in
|
|
26
|
+
# project settings without editing the managed fragment (a current 20, 22 or 24 line; Node 25+ has no Corepack).
|
|
27
|
+
YAD_NODE_VERSION: "22"
|
|
28
|
+
NX_BASE: $CI_MERGE_REQUEST_DIFF_BASE_SHA
|
|
29
|
+
NX_HEAD: $CI_COMMIT_SHA
|
|
21
30
|
# Runner selection. These jobs need a docker/dind executor; on instances whose runners are all
|
|
22
31
|
# tag-locked (run_untagged: false) an untagged image job never starts. Set the YAD_RUNNER_TAGS
|
|
23
32
|
# CI/CD variable (Settings → CI/CD → Variables, e.g. `dind_runner`) to route them — it lives in
|
|
@@ -44,13 +53,22 @@ yad-contract-check:
|
|
|
44
53
|
script:
|
|
45
54
|
- bash checks/contract-check.sh "origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME"
|
|
46
55
|
|
|
56
|
+
# E65/E66 — the risk map (.sdlc/risk-map: a risk level per directory, no names). Warnings, and the
|
|
57
|
+
# approver count read from the base branch's map. It never fails the pipeline (the count is reported,
|
|
58
|
+
# not enforced), so the job needs no allow_failure.
|
|
59
|
+
yad-risk-map:
|
|
60
|
+
extends: .sdlc_mr_only
|
|
61
|
+
needs: []
|
|
62
|
+
script:
|
|
63
|
+
- bash checks/risk-map-check.sh "origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME"
|
|
64
|
+
|
|
47
65
|
yad-build-test-lint:
|
|
48
66
|
extends: .sdlc_mr_only
|
|
49
67
|
needs: []
|
|
50
68
|
variables:
|
|
51
69
|
YAD_TEST_MAX_WORKERS: "2" # cap jest/vitest test workers in CI; ignored by other runners
|
|
52
70
|
script:
|
|
53
|
-
-
|
|
71
|
+
- bash checks/install-deps.sh
|
|
54
72
|
- bash checks/build-test-lint.sh
|
|
55
73
|
|
|
56
74
|
# Phase 6 — feature-thread gates (lineage / seal / hotfix-debt). Build on spec-link's story->epic
|
|
@@ -93,7 +111,7 @@ yad-pr-template:
|
|
|
93
111
|
- body="$(mktemp)"; printf '%s' "$CI_MERGE_REQUEST_DESCRIPTION" > "$body"
|
|
94
112
|
- bash checks/pr-template.sh --profile code "$body"
|
|
95
113
|
|
|
96
|
-
# No unverified commits
|
|
114
|
+
# No unverified commits: a platform-Verified signature on every commit (write access decides who authors).
|
|
97
115
|
# Needs a CI/CD variable GITLAB_TOKEN (or SDLC_API_TOKEN) with read_api scope — CI_JOB_TOKEN cannot
|
|
98
116
|
# read the commit-signature API. Without it the job FAILS closed with guidance.
|
|
99
117
|
yad-verified-commits:
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# yad-managed-include: yad-checks
|
|
2
|
-
# Pattern gates for the PRODUCT
|
|
2
|
+
# Pattern gates for the PRODUCT, as an INCLUDABLE fragment. Pulled into the Product's root
|
|
3
3
|
# .gitlab-ci.yml via:
|
|
4
4
|
# include:
|
|
5
5
|
# - local: '.gitlab/ci/yad-hub-checks.yml'
|
|
6
|
-
# MRs run with `--profile hub` and split by source branch: review/EP-* MRs are
|
|
7
|
-
# review vehicles (a `review: <artifact> (EP-<slug>)` title + the
|
|
8
|
-
# every other MR is a tooling/code change to the
|
|
6
|
+
# MRs run with `--profile hub` and split by source branch: review/EP-* MRs are Shape artifact-
|
|
7
|
+
# review vehicles (a `review: <artifact> (EP-<slug>)` title + the Product artifact-review template body);
|
|
8
|
+
# every other MR is a tooling/code change to the Product itself and follows the code convention (a
|
|
9
9
|
# Conventional-Commits title + the code task template body). The source branch is passed via --head so
|
|
10
10
|
# the gate can tell the two apart. Commit subjects are always Conventional-Commits.
|
|
11
11
|
# Job names are yad-hub-prefixed so they coexist with any code-repo fragment in one pipeline; jobs use
|
|
@@ -28,7 +28,7 @@ yad-hub-commit-message:
|
|
|
28
28
|
- bash checks/commit-message.sh --profile hub "origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME"
|
|
29
29
|
|
|
30
30
|
# GIT_DEPTH: 0 (above) gives the changed paths, so the gate can reject an artifact change (epics/**)
|
|
31
|
-
# riding a non-review source branch past the
|
|
31
|
+
# riding a non-review source branch past the Shape review with a plain code title/template.
|
|
32
32
|
yad-hub-pr-title:
|
|
33
33
|
extends: .yad_hub_mr_only
|
|
34
34
|
needs: []
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# yad-managed-include: yad-checks
|
|
2
|
-
# verified-commits gate for the PRODUCT
|
|
2
|
+
# verified-commits gate for the PRODUCT, as an INCLUDABLE fragment. Pulled into the Product's root
|
|
3
3
|
# .gitlab-ci.yml via:
|
|
4
4
|
# include:
|
|
5
5
|
# - local: '.gitlab/ci/yad-verified-commits.yml'
|
|
6
|
-
# Every MR (including the
|
|
7
|
-
# platform marks Verified
|
|
6
|
+
# Every MR (including the Shape review/EP-* MRs) must contain only commits whose signature the
|
|
7
|
+
# platform marks Verified. Who may author them is who has write access to the repo — no author list.
|
|
8
8
|
#
|
|
9
9
|
# Needs a CI/CD variable GITLAB_TOKEN (or SDLC_API_TOKEN) with read_api scope — CI_JOB_TOKEN cannot
|
|
10
|
-
# read the commit-signature API. The
|
|
10
|
+
# read the commit-signature API. The Product's SDLC_GATE_TOKEN (from the gate-sync wiring) also works:
|
|
11
11
|
# set GITLAB_TOKEN: $SDLC_GATE_TOKEN in the job if you prefer one token.
|
|
12
12
|
# Job name is yad-hub-prefixed so it can coexist with the code-repo fragment in one pipeline.
|
|
13
13
|
yad-hub-verified-commits:
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ledger-guard CURSOR ADAPTER — the same guard, answered in the protocol Cursor requires (E11).
|
|
3
|
+
#
|
|
4
|
+
# Cursor's `preToolUse` is a PERMISSION hook, and its docs are explicit: for a permission hook,
|
|
5
|
+
# "invalid JSON or a response that doesn't match the hook's schema blocks the action". Empty stdout is
|
|
6
|
+
# invalid JSON. The plain `ledger-guard.sh` prints nothing when it allows — so wiring it directly
|
|
7
|
+
# would have blocked EVERY file write in a verified project, which is the exact opposite of this
|
|
8
|
+
# guard's fail-open design.
|
|
9
|
+
#
|
|
10
|
+
# This wrapper guarantees one thing: a permission answer on stdout, always, whatever happens below.
|
|
11
|
+
#
|
|
12
|
+
# allow {"permission":"allow"} exit 0
|
|
13
|
+
# deny {"permission":"deny","user_message":"<reason>","agent_message":"<reason>"} exit 0
|
|
14
|
+
#
|
|
15
|
+
# A deny exits 0 because the JSON is the authoritative answer and exit 0 is what tells Cursor to read
|
|
16
|
+
# it; exit 2 blocks too, but is documented as the code for "no JSON to read" and would discard the
|
|
17
|
+
# reason — and naming the command that owns the transition is the whole point of speaking at edit time.
|
|
18
|
+
#
|
|
19
|
+
# It takes NO arguments, deliberately. Cursor's `hooks.json` holds one command STRING and does not
|
|
20
|
+
# document whether it is run through a shell, so a command with a flag in it might never be split into
|
|
21
|
+
# program and argument — a command not found, silently, on every call.
|
|
22
|
+
set -uo pipefail
|
|
23
|
+
|
|
24
|
+
# `CDPATH=` is load-bearing, and the Cursor entry is what makes it so: that entry invokes the script
|
|
25
|
+
# by a RELATIVE path, so `${BASH_SOURCE[0]}` is relative and `cd` consults $CDPATH before the current
|
|
26
|
+
# directory. An exported CDPATH in a login profile — an ordinary thing to have, and inherited by the
|
|
27
|
+
# harness — would resolve `hooks` to some other tree entirely, and the guard would fail open on every
|
|
28
|
+
# write for as long as that profile lived.
|
|
29
|
+
#
|
|
30
|
+
# The loop resolves a symlinked SCRIPT, which `cd -P` does not: `-P` resolves the directory path, and
|
|
31
|
+
# a script reached through a link in another directory would otherwise look for its sibling beside the
|
|
32
|
+
# LINK and not find it. `readlink` without `-f` because `-f` is not portable to macOS.
|
|
33
|
+
_src="${BASH_SOURCE[0]}"
|
|
34
|
+
while [ -L "$_src" ]; do
|
|
35
|
+
_dir="$(CDPATH= cd -P -- "$(dirname -- "$_src")" && pwd)"
|
|
36
|
+
_src="$(readlink -- "$_src")"
|
|
37
|
+
case "$_src" in /*) ;; *) _src="$_dir/$_src" ;; esac
|
|
38
|
+
done
|
|
39
|
+
HOOK_DIR="$(CDPATH= cd -P -- "$(dirname -- "$_src")" && pwd)"
|
|
40
|
+
ALLOW='{"permission":"allow"}'
|
|
41
|
+
|
|
42
|
+
# Every failure below this line resolves to ALLOW, because this guard fails open by design and CI is
|
|
43
|
+
# what fails closed. A wrapper that blocked when it could not reach `yad` would stop an agent editing
|
|
44
|
+
# anything at all the moment an install went sideways.
|
|
45
|
+
if [ ! -x "$HOOK_DIR/ledger-guard.sh" ]; then
|
|
46
|
+
echo " • yad hook: $HOOK_DIR/ledger-guard.sh is missing or not executable — allowing (run \`yad check --fix\`)" >&2
|
|
47
|
+
echo "$ALLOW"
|
|
48
|
+
exit 0
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
# stdout is CAPTURED (the JSON verdict), stderr is not (it flows through to Cursor's log). stdin is
|
|
52
|
+
# inherited by the command substitution, so the tool-call payload still reaches the guard.
|
|
53
|
+
raw="$("$HOOK_DIR/ledger-guard.sh" --format cursor)"
|
|
54
|
+
rc=$?
|
|
55
|
+
|
|
56
|
+
# THE VERDICT IS THE LAST NON-EMPTY LINE, not the whole of stdout, and it is recognised by containing
|
|
57
|
+
# a `"permission"` key rather than by starting with one.
|
|
58
|
+
#
|
|
59
|
+
# Both halves of that matter. `yad`'s own top-level failure handler prints to STDOUT, so a usage error
|
|
60
|
+
# or a crash banner can land in front of the JSON; a prefix test would then fail to recognise our own
|
|
61
|
+
# deny and fall through to the allow below, turning a real refusal into a permitted write with nothing
|
|
62
|
+
# on stderr to say so. And a containment test does not care what order the JSON's properties are in,
|
|
63
|
+
# so the deny answer's shape on the other side of this language boundary is not silently load-bearing.
|
|
64
|
+
verdict="$(printf '%s\n' "$raw" | awk 'NF { line = $0 } END { print line }')"
|
|
65
|
+
|
|
66
|
+
# `ledger-guard.sh` has its own fail-open branches that exit 0 with EMPTY stdout — no `yad` on PATH,
|
|
67
|
+
# an install it cannot resolve — and each one of those would otherwise reach Cursor as invalid JSON
|
|
68
|
+
# and block the write.
|
|
69
|
+
case "$verdict" in
|
|
70
|
+
*'"permission"'*) echo "$verdict"; exit 0 ;;
|
|
71
|
+
esac
|
|
72
|
+
|
|
73
|
+
# EXIT 2 IS A DENY, even with nothing on stdout — and this branch is reached in a real, ordinary case.
|
|
74
|
+
# `ledger-guard.sh` resolves `yad` from the Product's own `node_modules/yadflow` BEFORE `PATH`, so a
|
|
75
|
+
# project pinned to a yadflow older than `--format` parses the flag, ignores it, and answers in the
|
|
76
|
+
# exit protocol: exit 2, empty stdout. Falling through to the generic allow below would turn a real
|
|
77
|
+
# refusal into a permitted write, and print "exited 2 — allowing" while doing it.
|
|
78
|
+
#
|
|
79
|
+
# The message is a FIXED literal with nothing interpolated. The reason the older `yad` printed has
|
|
80
|
+
# already gone to stderr, where Cursor logs it; escaping arbitrary multi-line text into JSON in bash
|
|
81
|
+
# is exactly the sort of thing that produces an off-schema response, and there is no need to risk it.
|
|
82
|
+
if [ "$rc" -eq 2 ]; then
|
|
83
|
+
echo '{"permission":"deny","user_message":"yad: this edit touches CI-owned gate state.","agent_message":"This edit touches CI-owned gate state, which CI is the sole writer of. Do not edit the ledger by hand: use `yad gate open` for the transition, and commit only the artifact. The installed yadflow is older than this hook, so the full reason is in the hook log rather than here — `yad doctor` will name the version gap."}'
|
|
84
|
+
exit 0
|
|
85
|
+
fi
|
|
86
|
+
|
|
87
|
+
if [ "$rc" -ne 0 ]; then
|
|
88
|
+
echo " • yad hook: ledger-guard.sh exited $rc — allowing (run \`yad doctor\` to check the install)" >&2
|
|
89
|
+
fi
|
|
90
|
+
echo "$ALLOW"
|
|
91
|
+
exit 0
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# ledger-guard HARNESS HOOK — the local half of the CI gate of the same name (#171).
|
|
3
3
|
#
|
|
4
|
-
# The gate ledger is CI-owned in
|
|
5
|
-
# that changes `epics/*/.sdlc/{state,approvals,comments,hub-prs}.json` or `epics/*/reviews/*.md
|
|
4
|
+
# The gate ledger is CI-owned in verified mode: `checks/ledger-guard.sh` rejects any non-bot commit
|
|
5
|
+
# that changes `epics/*/.sdlc/{state,approvals,comments,product-prs,hub-prs}.json` or `epics/*/reviews/*.md`,
|
|
6
|
+
# and the same files for the Foundation in `foundation/.sdlc/` and `foundation/reviews/` (E75).
|
|
6
7
|
# This hook says so at the moment an agent tries the edit, instead of twenty minutes later in a
|
|
7
8
|
# failed pipeline, and names the command that owns the transition (`yad gate open`).
|
|
8
9
|
#
|
|
@@ -14,21 +15,51 @@
|
|
|
14
15
|
# exit 0 allow
|
|
15
16
|
# exit 2 deny, reason on stderr
|
|
16
17
|
#
|
|
17
|
-
# Wired
|
|
18
|
-
#
|
|
18
|
+
# Wired by `yad check --fix` for every harness that can refuse a write BEFORE it lands:
|
|
19
|
+
# Claude Code a `PreToolUse` hook in `.claude/settings.json`, pointing straight at THIS script
|
|
20
|
+
# Cursor a `preToolUse` hook in `.cursor/hooks.json`, pointing at `ledger-guard-cursor.sh`
|
|
21
|
+
#
|
|
22
|
+
# DO NOT WIRE THIS SCRIPT INTO CURSOR DIRECTLY, and check the same thing before wiring it into any
|
|
23
|
+
# other harness by hand. Cursor's `preToolUse` is a PERMISSION hook: it wants a JSON verdict on
|
|
24
|
+
# stdout, and treats an empty or off-schema answer as a refusal. This script prints NOTHING when it
|
|
25
|
+
# allows — so wired there directly it would block every file write in the project, which is the
|
|
26
|
+
# opposite of the fail-open stance below. `ledger-guard-cursor.sh` exists to speak that protocol.
|
|
27
|
+
#
|
|
28
|
+
# A harness that reads the EXIT CODE can use this script as it stands. A hook that only fires AFTER
|
|
29
|
+
# the write — Cursor's `afterFileEdit`, for one — is deliberately not used: it could report the edit
|
|
30
|
+
# but never refuse it, and reporting is what the CI gate already does.
|
|
31
|
+
#
|
|
32
|
+
# The project root is resolved from this script's own location below, so it does not matter which
|
|
33
|
+
# variable the harness sets ($CLAUDE_PROJECT_DIR, $CURSOR_PROJECT_DIR) or where it runs the command
|
|
34
|
+
# from; those variables appear only in the wiring, to point at this file.
|
|
19
35
|
#
|
|
20
36
|
# FAIL-OPEN: if no `yad` can be found, this ALLOWS and says why on stderr. A guardrail that blocked
|
|
21
37
|
# every edit the moment an install went sideways would be worse than the problem. The CI gate fails
|
|
22
38
|
# CLOSED and is what actually protects the ledger.
|
|
23
39
|
set -uo pipefail
|
|
24
40
|
|
|
25
|
-
# The
|
|
41
|
+
# The Product root is this script's grandparent — hooks/ledger-guard.sh — so the resolution below does
|
|
26
42
|
# not depend on the harness's working directory.
|
|
27
|
-
|
|
43
|
+
# `CDPATH=` is load-bearing, and the Cursor entry is what makes it so: that entry invokes the script
|
|
44
|
+
# by a RELATIVE path, so `${BASH_SOURCE[0]}` is relative and `cd` consults $CDPATH before the current
|
|
45
|
+
# directory. An exported CDPATH in a login profile — an ordinary thing to have, and inherited by the
|
|
46
|
+
# harness — would resolve `hooks` to some other tree entirely, and the guard would fail open on every
|
|
47
|
+
# write for as long as that profile lived.
|
|
48
|
+
#
|
|
49
|
+
# The loop resolves a symlinked SCRIPT, which `cd -P` does not: `-P` resolves the directory path, and
|
|
50
|
+
# a script reached through a link in another directory would otherwise look for its sibling beside the
|
|
51
|
+
# LINK and not find it. `readlink` without `-f` because `-f` is not portable to macOS.
|
|
52
|
+
_src="${BASH_SOURCE[0]}"
|
|
53
|
+
while [ -L "$_src" ]; do
|
|
54
|
+
_dir="$(CDPATH= cd -P -- "$(dirname -- "$_src")" && pwd)"
|
|
55
|
+
_src="$(readlink -- "$_src")"
|
|
56
|
+
case "$_src" in /*) ;; *) _src="$_dir/$_src" ;; esac
|
|
57
|
+
done
|
|
58
|
+
HOOK_DIR="$(CDPATH= cd -P -- "$(dirname -- "$_src")" && pwd)"
|
|
28
59
|
HUB_ROOT="$(dirname -- "$HOOK_DIR")"
|
|
29
60
|
|
|
30
61
|
# Resolution order, cheapest and most specific first: an explicit override, then the copy installed
|
|
31
|
-
# in this
|
|
62
|
+
# in this Product, then whatever is on PATH, then a network-free npx. `--no-install` matters — a hook
|
|
32
63
|
# runs on every tool call and must never pause an agent to download a package.
|
|
33
64
|
CMD=()
|
|
34
65
|
_yad_bin="${YAD_BIN:-}"
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: yad-commit
|
|
3
|
-
description: 'Build
|
|
3
|
+
description: 'Build 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
|
-
# SDLC — Commit by Convention (
|
|
6
|
+
# SDLC — Commit by Convention (Build 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
|
|
@@ -14,7 +14,7 @@ and `yad-ship` use. It **never auto-advances**; it just commits.
|
|
|
14
14
|
## Conventions
|
|
15
15
|
|
|
16
16
|
- Run **inside the repo holding the staged change** — a code repo under
|
|
17
|
-
`{project-root}/demo-repos/<repo>/`, or the
|
|
17
|
+
`{project-root}/demo-repos/<repo>/`, or the Product itself. Use absolute paths.
|
|
18
18
|
- **Stage first.** Only the staged (`git add`) atomic change is committed. The guard refuses more than
|
|
19
19
|
`ATOMIC_FILE_LIMIT` (3) staged files unless `--force` — split the change instead.
|
|
20
20
|
- **Subject** — `<type>: <lowercase imperative description, no trailing period>`; types are
|
|
@@ -22,8 +22,8 @@ and `yad-ship` use. It **never auto-advances**; it just commits.
|
|
|
22
22
|
- **Task trailer** — required on a code repo (anchors the `spec-link` + `commit-message` gates). Given
|
|
23
23
|
with `--task` (a `<story>-T<NN>` id — an explicit value is validated against the `spec-link`
|
|
24
24
|
contract, so a malformed id like a bare `EP-x-S01` fails locally instead of after a push), else
|
|
25
|
-
derived from the branch (`feat/<story>-<task>-…`).
|
|
26
|
-
task-scoped, so the trailer is optional there — a missing-Task warning is informational on the
|
|
25
|
+
derived from the branch (`feat/<story>-<task>-…`). Product commits are not
|
|
26
|
+
task-scoped, so the trailer is optional there — a missing-Task warning is informational on the Product
|
|
27
27
|
(`spec-link` is a code-repo gate) and only flags a real gate failure in a code repo.
|
|
28
28
|
- **Contract-Change trailer** — `--contract-change` only when the diff touches the locked contract
|
|
29
29
|
surface; it routes the change back to the architecture gate.
|
|
@@ -70,6 +70,6 @@ the architecture gate. To also open the PR/MR in the same step, use `yad-ship`.
|
|
|
70
70
|
|
|
71
71
|
## Reference
|
|
72
72
|
- Branch/commit conventions + the file-boundary rule: `../yad-implement/references/implement-conventions.md`.
|
|
73
|
-
- The full convention text: `CONTRIBUTING.md`; the config:
|
|
73
|
+
- The full convention text: `CONTRIBUTING.md`; the config: `.sdlc/config.yaml` `build`.
|
|
74
74
|
- The gate that enforces the subject pattern: `../yad-checks/references/check-gates.md` (`commit-message`).
|
|
75
75
|
- Open the PR/MR after committing: `../yad-open-pr/SKILL.md`; both at once: `../yad-ship/SKILL.md`.
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: yad-connect-design
|
|
3
|
-
description: 'Connects a design tool (Figma, or another tool — pluggable) to the
|
|
3
|
+
description: 'Connects a design tool (Figma, or another tool — pluggable) to the Product so the UI design step can materialize the full feature design (mobile screens / web pages) inside it, not just Markdown. Registers the tool into the project-wide .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 it is not. Run at setup or any time the design tool changes. Reusable, idempotent, refreshable. Use when the user says "connect Figma", "connect a design tool", "refresh the design connection", or "list the design connection".'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# SDLC — Connect a Design Tool (make the UI step design-tool aware)
|
|
7
7
|
|
|
8
8
|
**Goal:** Let the UI design step (`yad-ui`) produce the **actual feature design** — the mobile screens
|
|
9
9
|
and/or web pages — inside a design tool such as **Figma**, alongside the Markdown artifacts
|
|
10
|
-
(`ui-design.md` / `DESIGN.md`). This skill **connects** a design tool to the
|
|
10
|
+
(`ui-design.md` / `DESIGN.md`). This skill **connects** a design tool to the Product and records
|
|
11
11
|
*how* to reach it (the tool, the project/file references, which MCP renders it) — never a credential.
|
|
12
12
|
|
|
13
|
-
This is **setup/maintenance**, not a gated
|
|
13
|
+
This is **setup/maintenance**, not a gated Shape step — it never touches `.sdlc/state.json` or any
|
|
14
14
|
epic's approvals. It only writes the project-wide design registry. `yad-ui` consumes it: when a tool is
|
|
15
15
|
connected and its MCP is available, the `ux-designer` lens **generates** screens into the tool (or
|
|
16
16
|
**links** an existing human-made design and reads it back); when nothing is connected, `yad-ui` runs
|
|
@@ -18,7 +18,7 @@ markdown-only exactly as before.
|
|
|
18
18
|
|
|
19
19
|
## Conventions
|
|
20
20
|
|
|
21
|
-
- `{project-root}` resolves from the project working directory (the **
|
|
21
|
+
- `{project-root}` resolves from the project working directory (the **Product**).
|
|
22
22
|
- The integration is **Figma-first but pluggable** (`config.yaml` `design.tools`): a design-tool
|
|
23
23
|
*adapter*, like the GitHub/GitLab platform adapter. Figma is the primary provider; `pencil`
|
|
24
24
|
(the `.pen` web/mobile editor) is a second, write-capable provider; `none` → markdown-only.
|
|
@@ -29,7 +29,7 @@ markdown-only exactly as before.
|
|
|
29
29
|
the sibling of `.sdlc/repos.json` and `.sdlc/hub.json`.
|
|
30
30
|
- Per-epic screen→frame links are written later by `yad-ui` (`epics/EP-<slug>/.sdlc/design-links.json`),
|
|
31
31
|
not here.
|
|
32
|
-
- Speak in the
|
|
32
|
+
- Speak in the `communication_language` set in `{project-root}/.sdlc/config.yaml`; write documents in `document_output_language`.
|
|
33
33
|
|
|
34
34
|
## Inputs
|
|
35
35
|
|
|
@@ -94,7 +94,7 @@ Nothing auto-advances; this is setup.
|
|
|
94
94
|
**available/unavailable** flag for the MCP (best-effort, the user's own session). No design tool
|
|
95
95
|
connected ⇒ "markdown-only".
|
|
96
96
|
- **`disconnect`** — remove the registry file (or set `tool: "none"`). The design tool's own
|
|
97
|
-
project/files are **never touched** — only the
|
|
97
|
+
project/files are **never touched** — only the Product's record of them.
|
|
98
98
|
|
|
99
99
|
## Hard rules
|
|
100
100
|
|
|
@@ -46,7 +46,7 @@ a screen, capture its node id + URL, and map components/tokens into `DESIGN.md`.
|
|
|
46
46
|
|
|
47
47
|
Either direction ends by writing `epics/EP-<slug>/.sdlc/design-links.json` — the machine-readable
|
|
48
48
|
screen→frame map — and a `## Design (<tool>)` section in `ui-design.md` linking each screen to its frame
|
|
49
|
-
URL. The design itself lives in the tool; the
|
|
49
|
+
URL. The design itself lives in the tool; the Product keeps the *links* and the Markdown spec beside the
|
|
50
50
|
other epic artifacts.
|
|
51
51
|
|
|
52
52
|
## Degrade path (no MCP / no tool)
|