yadflow 4.0.0-next.3 → 4.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 +699 -0
- package/README.md +85 -22
- package/bin/commands.mjs +3 -1
- package/bin/yad.mjs +183 -29
- package/cli/capture.mjs +4 -2
- package/cli/checkpoint.mjs +10 -10
- package/cli/claims.mjs +4 -4
- package/cli/commit.mjs +48 -12
- package/cli/docs.mjs +15 -13
- package/cli/doctor.mjs +557 -141
- package/cli/epic-state.mjs +41 -18
- package/cli/errors.mjs +3 -2
- package/cli/find-product.mjs +197 -0
- package/cli/fold.mjs +4 -4
- package/cli/gate-local.mjs +13 -13
- package/cli/gate.mjs +111 -103
- package/cli/history.mjs +13 -13
- package/cli/hook.mjs +201 -30
- package/cli/index-command.mjs +6 -6
- package/cli/lib.mjs +91 -29
- package/cli/manifest.mjs +171 -104
- package/cli/migrate.mjs +208 -17
- package/cli/mode.mjs +22 -22
- package/cli/next.mjs +6 -6
- package/cli/openpr.mjs +32 -24
- package/cli/people.mjs +22 -10
- package/cli/plan.mjs +718 -45
- package/cli/platform.mjs +13 -7
- package/cli/product-index.mjs +4 -4
- package/cli/{hubcommit.mjs → productcommit.mjs} +11 -7
- package/cli/protection.mjs +19 -9
- package/cli/reconcile.mjs +98 -23
- package/cli/repo-publish.mjs +22 -16
- package/cli/repo.mjs +128 -33
- package/cli/report.mjs +4 -2
- package/cli/review.mjs +26 -19
- package/cli/setup.mjs +105 -36
- package/cli/ship.mjs +1 -1
- package/cli/skip.mjs +4 -4
- package/cli/tidy.mjs +7 -7
- package/cli/update-commit.mjs +22 -19
- package/cli/workspace.mjs +495 -0
- package/package.json +7 -4
- package/skills/sdlc/config.yaml +14 -13
- package/skills/sdlc/module-help.csv +8 -8
- package/skills/yad-analysis/SKILL.md +6 -4
- package/skills/yad-architecture/SKILL.md +4 -3
- package/skills/yad-backfill/SKILL.md +11 -5
- package/skills/yad-backfill/references/backfill.md +33 -3
- package/skills/yad-backfill/templates/checks/backfill-check.sh +200 -17
- package/skills/yad-change/SKILL.md +1 -1
- package/skills/yad-checks/SKILL.md +48 -27
- package/skills/yad-checks/references/check-gates.md +213 -53
- package/skills/yad-checks/templates/checks/commit-message.sh +39 -13
- package/skills/yad-checks/templates/checks/contract-check.sh +472 -23
- package/skills/yad-checks/templates/checks/epic-open.sh +398 -8
- package/skills/yad-checks/templates/checks/ledger-guard.sh +75 -25
- package/skills/yad-checks/templates/checks/lineage-check.sh +391 -7
- package/skills/yad-checks/templates/checks/product-checkout.sh +141 -0
- package/skills/yad-checks/templates/checks/reconcile-debt-check.sh +399 -8
- package/skills/yad-checks/templates/checks/risk-map-check.sh +35 -3
- package/skills/yad-checks/templates/checks/spec-link.sh +36 -3
- package/skills/yad-checks/templates/checks/verified-commits.sh +26 -2
- package/skills/yad-checks/templates/github/yad-checks.yml +33 -13
- package/skills/yad-checks/templates/github/{yad-hub-checks.yml → yad-product-checks.yml} +14 -10
- package/skills/yad-checks/templates/github/yad-update-guard.yml +1 -1
- package/skills/yad-checks/templates/github/yad-verified-commits.yml +1 -1
- package/skills/yad-checks/templates/gitlab/yad-checks.gitlab-ci.yml +12 -0
- package/skills/yad-checks/templates/gitlab/{yad-hub-checks.gitlab-ci.yml → yad-product-checks.gitlab-ci.yml} +16 -16
- package/skills/yad-checks/templates/gitlab/yad-verified-commits.gitlab-ci.yml +2 -2
- package/skills/yad-checks/templates/hooks/ledger-guard-cursor.mjs +130 -0
- package/skills/yad-checks/templates/hooks/ledger-guard.mjs +117 -0
- package/skills/yad-checks/templates/hooks/yad-capture.mjs +117 -0
- package/skills/yad-commit/SKILL.md +18 -7
- package/skills/yad-connect-design/SKILL.md +1 -1
- package/skills/yad-connect-design/references/design-registry.md +1 -1
- package/skills/yad-connect-docs/SKILL.md +16 -11
- package/skills/yad-connect-docs/references/docs-registry.md +8 -8
- package/skills/yad-connect-learning/SKILL.md +1 -1
- package/skills/yad-connect-learning/references/learning-registry.md +1 -1
- package/skills/yad-connect-repos/SKILL.md +31 -8
- package/skills/yad-connect-repos/references/code-context.md +1 -1
- package/skills/yad-connect-repos/references/{hub-config.md → product-config.md} +19 -11
- package/skills/yad-connect-repos/references/repos-registry.md +1 -1
- package/skills/yad-connect-testing/SKILL.md +1 -1
- package/skills/yad-connect-testing/references/testing-registry.md +1 -1
- package/skills/yad-discovery/SKILL.md +2 -2
- package/skills/yad-discovery/references/discovery-schema.md +1 -1
- package/skills/yad-docs/SKILL.md +1 -1
- package/skills/yad-docs/references/data-mapping.md +1 -1
- package/skills/yad-docs/templates/app/package-lock.json +8 -8
- package/skills/yad-docs/templates/app/package.json +1 -1
- package/skills/yad-docs/templates/app/src/components/Canvas/FlowCanvas.tsx +2 -2
- package/skills/yad-docs-overview/references/pipeline-model.md +3 -3
- package/skills/yad-engineer-review/SKILL.md +2 -2
- package/skills/yad-engineer-review/references/ship-and-record.md +3 -3
- package/skills/yad-epic/SKILL.md +6 -4
- package/skills/yad-epic/references/state-schema.md +13 -12
- package/skills/yad-open-pr/SKILL.md +1 -1
- package/skills/yad-pair-review/SKILL.md +5 -5
- package/skills/yad-pair-review/references/session-state.md +2 -2
- package/skills/yad-pr-template/SKILL.md +14 -12
- package/skills/yad-pr-template/templates/checks/pr-template.sh +18 -16
- package/skills/yad-pr-template/templates/checks/pr-title.sh +15 -13
- package/skills/yad-pr-template/templates/{hub → product}/github/pull_request_template.md +2 -2
- package/skills/yad-pr-template/templates/{hub → product}/gitlab/merge_request_templates/Default.md +2 -2
- package/skills/{yad-hub-bridge → yad-product-bridge}/SKILL.md +15 -15
- package/skills/{yad-hub-bridge → yad-product-bridge}/references/bridge.md +15 -10
- package/skills/{yad-hub-bridge → yad-product-bridge}/references/login-roster.md +2 -2
- package/skills/{yad-hub-bridge/templates/checks/hub-route.sh → yad-product-bridge/templates/checks/product-route.sh} +2 -2
- package/skills/{yad-hub-bridge → yad-product-bridge}/templates/github/yad-gate-sync.yml +29 -16
- package/skills/{yad-hub-bridge → yad-product-bridge}/templates/gitlab/gitlab-ci.include-root.yml +1 -1
- package/skills/{yad-hub-bridge → yad-product-bridge}/templates/gitlab/yad-gate-sync.gitlab-ci.yml +5 -3
- package/skills/yad-report/SKILL.md +1 -1
- package/skills/yad-review-companion/SKILL.md +3 -3
- package/skills/yad-review-gate/SKILL.md +22 -23
- package/skills/yad-review-gate/references/gating.md +13 -13
- package/skills/yad-run/SKILL.md +2 -2
- package/skills/yad-run/references/run-loop.md +2 -2
- package/skills/yad-spec/SKILL.md +2 -1
- package/skills/yad-spec/references/spec-handoff.md +19 -10
- package/skills/yad-stories/SKILL.md +2 -2
- package/skills/yad-sync-repos/SKILL.md +19 -3
- package/skills/yad-test-cases/SKILL.md +2 -2
- package/skills/yad-ui/SKILL.md +2 -2
- package/skills/yad-checks/templates/hooks/ledger-guard-cursor.sh +0 -91
- package/skills/yad-checks/templates/hooks/ledger-guard.sh +0 -100
- package/skills/yad-checks/templates/hooks/yad-capture.sh +0 -53
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
# that bypassed the template.
|
|
6
6
|
# --profile code (default) — the code-repo task template (yad-pr-template templates/<platform>/):
|
|
7
7
|
# requires `## Summary`, `## Impact & Risk`, `## Checklist`, and a filled `Risk level:` (low|medium|high).
|
|
8
|
-
# --profile hub — the Shape artifact-review template (templates/
|
|
8
|
+
# --profile product (old name: hub) — the Shape artifact-review template (templates/product/<platform>/):
|
|
9
9
|
# requires `## Artifact under review`, `## Impact & Risk (front-half)` (or `(Shape)`), `## Checklist`, and a `Risk tags:` line.
|
|
10
10
|
# BUT only for review/EP-* head branches. Every other Product PR is a tooling/code change to the Product
|
|
11
11
|
# itself and uses the code task template instead; pass the head ref via --head so the gate knows
|
|
@@ -35,20 +35,22 @@ while [ $# -gt 0 ]; do
|
|
|
35
35
|
*) ARGS+=("$1"); shift ;;
|
|
36
36
|
esac
|
|
37
37
|
done
|
|
38
|
-
case "$PROFILE" in code|hub|product) ;; *) echo "FAIL [pr-template]: unknown --profile '$PROFILE' (code|hub
|
|
39
|
-
# `product` is the
|
|
38
|
+
case "$PROFILE" in code|hub|product) ;; *) echo "FAIL [pr-template]: unknown --profile '$PROFILE' (code|product, or hub, the old name for product)."; exit 1 ;; esac
|
|
39
|
+
# `product` is the Product's profile. `hub` is its old name, and BOTH are accepted.
|
|
40
40
|
#
|
|
41
|
-
#
|
|
42
|
-
#
|
|
43
|
-
#
|
|
44
|
-
#
|
|
45
|
-
#
|
|
41
|
+
# This script and the workflow that passes the flag are both in PRODUCT_WIRING (cli/manifest.mjs) and
|
|
42
|
+
# normally land together on one `yad update` — but not always: a copy the team edited is kept. So the
|
|
43
|
+
# order is add-before-remove: the new spelling was accepted first, the workflow templates switched to
|
|
44
|
+
# emit it in E123 (`yad-product-checks.yml`), and the old one is dropped after that. `hub` stays
|
|
45
|
+
# accepted meanwhile, for a workflow of the team's own or one they edited. The other direction — an
|
|
46
|
+
# edited copy of THIS script from before 4.0, which rejects `product`, beside the new workflow — fails
|
|
47
|
+
# every Product PR; `yad update` and `yad doctor` (`profile:`) say so.
|
|
46
48
|
#
|
|
47
|
-
# Normalised to `
|
|
48
|
-
# load-bearing in pr-title.sh and pr-template.sh: leave `$PROFILE` as `
|
|
49
|
+
# Normalised to `product` immediately, so nothing below has to know there are two spellings. That is
|
|
50
|
+
# load-bearing in pr-title.sh and pr-template.sh: leave `$PROFILE` as `hub` and the `= product`
|
|
49
51
|
# branch is skipped, taking the whole review-branch arm with it — including the guard that stops a
|
|
50
52
|
# plain code title carrying an artifact change past its review.
|
|
51
|
-
[ "$PROFILE" =
|
|
53
|
+
[ "$PROFILE" = hub ] && PROFILE=product
|
|
52
54
|
|
|
53
55
|
|
|
54
56
|
# True when the PR changes a Shape artifact (anything under epics/** — or foundation/**, the Product level, E75). Reads the --changed list
|
|
@@ -115,14 +117,14 @@ check_code_body() {
|
|
|
115
117
|
}
|
|
116
118
|
|
|
117
119
|
# The Shape artifact-review template.
|
|
118
|
-
|
|
120
|
+
check_product_body() {
|
|
119
121
|
require_heading '## Artifact under review' '## Artifact under review'
|
|
120
122
|
# Add before you remove (change-safety rule 3). `front-half` is the ORIGINAL spelling and it is
|
|
121
123
|
# still what `yad gate open` emits and what the shipped Product template carries; `Shape` is the
|
|
122
124
|
# replacement, accepted here first so that this script is lenient BEFORE anything starts writing
|
|
123
125
|
# the new wording. That ordering is the whole point: this file is refreshed by `yad update`
|
|
124
126
|
# (PRODUCT_WIRING, cli/manifest.mjs), but the PR template beside it is NOT — it is installed only by
|
|
125
|
-
# the `yad-pr-template repo:
|
|
127
|
+
# the `yad-pr-template repo:product action: wire` skill, which nothing runs automatically. So a Product
|
|
126
128
|
# WILL sit with a refreshed checker next to an old template, and the checker has to accept both.
|
|
127
129
|
# The emitter flips to `(Shape)` in the next major; only after that may `front-half` be dropped.
|
|
128
130
|
require_heading '## Impact & Risk \((front-half|Shape)\)' '## Impact & Risk (front-half)'
|
|
@@ -134,9 +136,9 @@ check_hub_body() {
|
|
|
134
136
|
}
|
|
135
137
|
|
|
136
138
|
KIND="$PROFILE"
|
|
137
|
-
if [ "$PROFILE" =
|
|
139
|
+
if [ "$PROFILE" = product ]; then
|
|
138
140
|
case "$HEADREF" in
|
|
139
|
-
review/EP-*|"")
|
|
141
|
+
review/EP-*|"") check_product_body ;; # artifact-review PR (or unknown head — stay strict)
|
|
140
142
|
*)
|
|
141
143
|
# tooling/code change to the Product itself — UNLESS it changes Shape artifacts (epics/**),
|
|
142
144
|
# which must go through a review/EP-* PR. Without this guard a non-review head could carry an
|
|
@@ -145,7 +147,7 @@ if [ "$PROFILE" = hub ]; then
|
|
|
145
147
|
echo "FAIL [pr-template]: head '${HEADREF}' changes Shape artifacts (epics/** or foundation/**) but is not a review/EP-* branch — artifact changes must go through a review PR."
|
|
146
148
|
rc=1
|
|
147
149
|
else
|
|
148
|
-
check_code_body; KIND="
|
|
150
|
+
check_code_body; KIND="product-tooling"
|
|
149
151
|
fi
|
|
150
152
|
;;
|
|
151
153
|
esac
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# --profile code (default) — a Conventional-Commits subject "<type>: <description>", no trailing
|
|
5
5
|
# period (config.yaml build.pr_title_style: same_as_commit_subject; one task = one PR, the title is
|
|
6
6
|
# the squash-merge subject). Keep <type> in sync with cli/manifest.mjs COMMIT_TYPES.
|
|
7
|
-
# --profile hub — a Shape artifact-review title "review: <artifact> (EP-<slug>)", the shape
|
|
7
|
+
# --profile product (old name: hub) — a Shape artifact-review title "review: <artifact> (EP-<slug>)", the shape
|
|
8
8
|
# `yad gate open` creates (cli/gate.mjs) — BUT only for review/EP-* head branches. Every other
|
|
9
9
|
# Product PR is a tooling/code change to the Product itself and follows the code convention; pass the
|
|
10
10
|
# head ref via --head so the gate can tell the two apart (a tooling PR has no EP artifact to
|
|
@@ -34,20 +34,22 @@ while [ $# -gt 0 ]; do
|
|
|
34
34
|
*) ARGS+=("$1"); shift ;;
|
|
35
35
|
esac
|
|
36
36
|
done
|
|
37
|
-
case "$PROFILE" in code|hub|product) ;; *) echo "FAIL [pr-title]: unknown --profile '$PROFILE' (code|hub
|
|
38
|
-
# `product` is the
|
|
37
|
+
case "$PROFILE" in code|hub|product) ;; *) echo "FAIL [pr-title]: unknown --profile '$PROFILE' (code|product, or hub, the old name for product)."; exit 1 ;; esac
|
|
38
|
+
# `product` is the Product's profile. `hub` is its old name, and BOTH are accepted.
|
|
39
39
|
#
|
|
40
|
-
#
|
|
41
|
-
#
|
|
42
|
-
#
|
|
43
|
-
#
|
|
44
|
-
#
|
|
40
|
+
# This script and the workflow that passes the flag are both in PRODUCT_WIRING (cli/manifest.mjs) and
|
|
41
|
+
# normally land together on one `yad update` — but not always: a copy the team edited is kept. So the
|
|
42
|
+
# order is add-before-remove: the new spelling was accepted first, the workflow templates switched to
|
|
43
|
+
# emit it in E123 (`yad-product-checks.yml`), and the old one is dropped after that. `hub` stays
|
|
44
|
+
# accepted meanwhile, for a workflow of the team's own or one they edited. The other direction — an
|
|
45
|
+
# edited copy of THIS script from before 4.0, which rejects `product`, beside the new workflow — fails
|
|
46
|
+
# every Product PR; `yad update` and `yad doctor` (`profile:`) say so.
|
|
45
47
|
#
|
|
46
|
-
# Normalised to `
|
|
47
|
-
# load-bearing in pr-title.sh and pr-template.sh: leave `$PROFILE` as `
|
|
48
|
+
# Normalised to `product` immediately, so nothing below has to know there are two spellings. That is
|
|
49
|
+
# load-bearing in pr-title.sh and pr-template.sh: leave `$PROFILE` as `hub` and the `= product`
|
|
48
50
|
# branch is skipped, taking the whole review-branch arm with it — including the guard that stops a
|
|
49
51
|
# plain code title carrying an artifact change past its review.
|
|
50
|
-
[ "$PROFILE" =
|
|
52
|
+
[ "$PROFILE" = hub ] && PROFILE=product
|
|
51
53
|
|
|
52
54
|
|
|
53
55
|
# True when the PR changes a Shape artifact (anything under epics/** — or foundation/**, the Product level, E75). Reads the --changed list
|
|
@@ -84,12 +86,12 @@ check_code_title() {
|
|
|
84
86
|
exit 0
|
|
85
87
|
}
|
|
86
88
|
|
|
87
|
-
if [ "$PROFILE" =
|
|
89
|
+
if [ "$PROFILE" = product ]; then
|
|
88
90
|
# review/EP-* head branch (or unknown head ref) => Shape artifact-review PR: 'review: <artifact> (EP-<slug>)'.
|
|
89
91
|
case "$HEADREF" in
|
|
90
92
|
review/EP-*|"")
|
|
91
93
|
if printf '%s' "$TITLE" | grep -qE '^review: .+ \(EP-[a-z0-9-]+\)$'; then
|
|
92
|
-
echo "PASS [pr-title]: '${TITLE}' (profile:
|
|
94
|
+
echo "PASS [pr-title]: '${TITLE}' (profile: product, artifact-review)"
|
|
93
95
|
exit 0
|
|
94
96
|
fi
|
|
95
97
|
echo "FAIL [pr-title]: '${TITLE}' is not a Product review title 'review: <artifact> (EP-<slug>)'."
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!-- SDLC
|
|
1
|
+
<!-- SDLC Product PR template — Shape artifact review (epic / architecture+contract / ui-design / stories). -->
|
|
2
2
|
<!-- This PR is a REVIEW VEHICLE on the Product, not a code merge. The file gate (yad-review-gate)
|
|
3
3
|
advances the step when this PR is MERGED with the approvals met and every thread resolved. Reviewers
|
|
4
4
|
approve/comment here; `yad gate sync` (or CI at merge, with a verified ledger) pulls that into the file ledger. -->
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
`auth`/`payments`, 1 otherwise (the largest step, never the sum). No approval should be the author’s own.
|
|
23
23
|
- Enforced: **1 approval** (the base). The risk step is **advisory**. `yad gate status <epic>`
|
|
24
24
|
prints the count capped at the active people less one (one seat is left for the author), and
|
|
25
|
-
`yad-
|
|
25
|
+
`yad-product-bridge`'s `product-route.sh` run on this description prints the full count.
|
|
26
26
|
- No reviewers are requested automatically — ask them on this PR. The `domain:<repo>` labels show the touched repos.
|
|
27
27
|
|
|
28
28
|
## How to review (this drives the gate)
|
package/skills/yad-pr-template/templates/{hub → product}/gitlab/merge_request_templates/Default.md
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!-- SDLC
|
|
1
|
+
<!-- SDLC Product MR template — Shape artifact review (epic / architecture+contract / ui-design / stories). -->
|
|
2
2
|
<!-- This MR is a REVIEW VEHICLE on the Product, not a code merge. The file gate (yad-review-gate)
|
|
3
3
|
advances the step when this MR is MERGED with the approvals met and every thread resolved. Reviewers
|
|
4
4
|
approve/comment here; `yad gate sync` (or CI at merge, with a verified ledger) pulls that into the file ledger. -->
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
`auth`/`payments`, 1 otherwise (the largest step, never the sum). No approval should be the author’s own.
|
|
32
32
|
- Enforced: **1 approval** (the base). The risk step is **advisory**. `yad gate status <epic>`
|
|
33
33
|
prints the count capped at the active people less one (one seat is left for the author), and
|
|
34
|
-
`yad-
|
|
34
|
+
`yad-product-bridge`'s `product-route.sh` run on this description prints the full count.
|
|
35
35
|
- No reviewers are requested automatically — ask them on this MR. The `domain:<repo>` labels show the touched repos.
|
|
36
36
|
|
|
37
37
|
## How to review (this drives the gate)
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: yad-
|
|
3
|
-
description: 'The templated PR/MR bridge for the Shape review gate. When the Product has a platform (.sdlc/
|
|
2
|
+
name: yad-product-bridge
|
|
3
|
+
description: 'The templated PR/MR bridge for the Shape review gate. When the Product has a platform (.sdlc/product.json), it opens a review PR/MR on the Product for an authored artifact (analysis / epic / architecture+contract / ui-design / stories / test-cases), applies the touched-domain labels (it requests no reviewers), and provides the read-only gh/glab recipes that yad-review-gate uses to pull platform comments + approvals back into the file ledger. Can also wire merge-time sync on the Product: a CI workflow that runs `yad gate ci` when a human merges a review PR/MR — CI is the sole ledger writer and writes only at merge, on the default branch (during review the platform PR/MR is the source of truth; CI never touches the review branch). Local-user auth only — no stored tokens. The file ledger stays the source of truth; degrades to the local gate when there is no platform / no CLI. Use when the user says "open the review PR", "route the review", "wire the gate sync", or it is invoked by yad-review-gate open/sync.'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# SDLC —
|
|
6
|
+
# SDLC — Product Review Bridge (the templated PR/MR bridge)
|
|
7
7
|
|
|
8
8
|
**Goal:** Run the Shape review/comment/approval cycle through a **real PR/MR on the Product**,
|
|
9
9
|
without changing the gate's predicate or making the file ledger optional. The verified ledger is an **alternate
|
|
@@ -17,12 +17,12 @@ keeps platform mechanics out of the gate). `yad-review-gate` *calls* it; it neve
|
|
|
17
17
|
|
|
18
18
|
## Conventions
|
|
19
19
|
|
|
20
|
-
- `{project-root}` is the **Product**. The Product platform lives in `.sdlc/hub.json` — it holds no
|
|
21
|
-
list of people (`config.yaml` `product.config` (older projects: `hub.config`); schema in `../yad-connect-repos/references/
|
|
20
|
+
- `{project-root}` is the **Product**. The Product platform lives in `.sdlc/product.json` (written together with its older name `.sdlc/hub.json`, same bytes, until v5; read from `hub.json` only when `product.json` is absent) — it holds no
|
|
21
|
+
list of people (`config.yaml` `product.config` (older projects: `hub.config`); schema in `../yad-connect-repos/references/product-config.md`).
|
|
22
22
|
- Per-step review-PR record: `epics/EP-<slug>/.sdlc/product-prs.json`, written together with its
|
|
23
23
|
older name `hub-prs.json` (`config.yaml` `product.pr_ledger` (older projects: `hub.pr_ledger`)) — a
|
|
24
24
|
sibling ledger to `approvals.json`, so `state.json`'s locked step shape is untouched.
|
|
25
|
-
- The review-PR body is the Product template from `yad-pr-template` (`templates/
|
|
25
|
+
- The review-PR body is the Product template from `yad-pr-template` (`templates/product/<platform>/…`).
|
|
26
26
|
- Branch per artifact: `review/EP-<slug>/<artifact-base>` (`config.yaml` `product.artifact_branch` (older projects: `hub.artifact_branch`)).
|
|
27
27
|
- **Local-user auth only; store no tokens.** Use the user's own `gh`/`glab`. If neither is installed/
|
|
28
28
|
authenticated, STOP this path and tell the gate to fall back to local — never embed a credential.
|
|
@@ -36,20 +36,20 @@ keeps platform mechanics out of the gate). `yad-review-gate` *calls* it; it neve
|
|
|
36
36
|
|
|
37
37
|
## Preconditions (the verified ledger runs only when all hold)
|
|
38
38
|
|
|
39
|
-
`.sdlc/hub.json` exists with a non-null `platform`, `ledger: "verified"` (or, before `yad migrate`, `bridge_enabled: true`), `config.yaml` `product.bridge: true`
|
|
39
|
+
`.sdlc/product.json` (or, on an older Product, `.sdlc/hub.json`) exists with a non-null `platform`, `ledger: "verified"` (or, before `yad migrate`, `bridge_enabled: true`), `config.yaml` `product.bridge: true`
|
|
40
40
|
(older projects: `hub.bridge: true`), and `gh` (GitHub) / `glab` (GitLab) installed **and authenticated**. If any fails, report that the
|
|
41
41
|
gate proceeds **local** (no error) and stop.
|
|
42
42
|
|
|
43
43
|
## On Activation
|
|
44
44
|
|
|
45
45
|
### Step 1 — Resolve platform + routing
|
|
46
|
-
Read `.sdlc/hub.json` for the platform, `epics/<epic>/epic.md` for `repos` + `owner` (the artifact's
|
|
46
|
+
Read `.sdlc/product.json` (`.sdlc/hub.json` on an older Product that has only that name) for the platform, `epics/<epic>/epic.md` for `repos` + `owner` (the artifact's
|
|
47
47
|
author), and the matching `review+approve` step's `risk_tags` from `.sdlc/state.json`. Compute the
|
|
48
48
|
**count** with `route` (below) — the same rule `yad-review-gate` enforces: `needed = base 1 + risk step`
|
|
49
49
|
distinct approvers, where `contract` adds 2 and `auth`/`payments` add 1 (the highest tag, never the sum).
|
|
50
50
|
Only the base (1 distinct approver, who should not be the author) holds the gate; the risk step is
|
|
51
51
|
advisory. The gate caps the count at the active people less one and prints it (E72), but does
|
|
52
|
-
not enforce it yet; when it cannot count the people — as in Product CI, which checks out only the
|
|
52
|
+
not enforce it yet; when it cannot count the people — as in Product CI, which checks out only the Product —
|
|
53
53
|
no cap is shown. Compute the **touched domains** too: the union of story `repos` for
|
|
54
54
|
`stories-review`, the epic's `repos` for a step tagged `contract`, `auth` or `payments`, none otherwise.
|
|
55
55
|
They become `domain:<repo>` labels and
|
|
@@ -63,7 +63,7 @@ are a hint for whom to ask. There is no roster to turn them into people.
|
|
|
63
63
|
(`epic/…`, `change/…`, `analysis/…`, `discovery/…`) instead, so it carries the `.sdlc/` **seed** —
|
|
64
64
|
that PR/MR is the only way a new epic's ledger reaches the default branch, and `ledger-guard`
|
|
65
65
|
exempts it (see step 4).
|
|
66
|
-
2. Open the PR/MR with `gh`/`glab` using the Product body template (`yad-pr-template` `templates/
|
|
66
|
+
2. Open the PR/MR with `gh`/`glab` using the Product body template (`yad-pr-template` `templates/product/…`),
|
|
67
67
|
filled with the epic, artifact, gate step, owner, `epic.repos`, and the step's risk tags.
|
|
68
68
|
3. **Request no reviewers.** Assign the person opening it — `@me` on GitHub (`gh` resolves it), the
|
|
69
69
|
login `glab api user` reports on GitLab (no assignee when that lookup fails) — and add a
|
|
@@ -72,7 +72,7 @@ are a hint for whom to ask. There is no roster to turn them into people.
|
|
|
72
72
|
reviewer suggestion: E68's suggestion reads a code change, so only `yad open-pr` (and `yad ship`) on a code repo prints it.)
|
|
73
73
|
4. **Do not write the ledger.** CI is the sole writer and writes only at merge. During review nothing
|
|
74
74
|
is recorded in the ledger — the platform PR/MR holds the review state (native approvals + threads).
|
|
75
|
-
At merge, CI records the
|
|
75
|
+
At merge, CI records the PR-ledger entry in the shape below (in `product-prs.json` and `hub-prs.json`) and advances on the default
|
|
76
76
|
branch:
|
|
77
77
|
```json
|
|
78
78
|
{ "step": "<review step id>", "artifact": "<artifact>", "platform": "github|gitlab",
|
|
@@ -88,7 +88,7 @@ are a hint for whom to ask. There is no roster to turn them into people.
|
|
|
88
88
|
act on the platform; CI (`yad gate ci`) reconciles it onto the default branch at merge.
|
|
89
89
|
|
|
90
90
|
### Step 3 — `route` (print the count)
|
|
91
|
-
Compute and print the count as above. Use `templates/checks/
|
|
91
|
+
Compute and print the count as above. Use `templates/checks/product-route.sh <body>` to parse a PR/MR
|
|
92
92
|
body's Impact & Risk block when given one; it prints e.g. `ROUTE: 3 approvers = base 1 + contract risk 2
|
|
93
93
|
(risk tag: contract)`, says only the base holds the gate (1 approval), that the risk step is advisory
|
|
94
94
|
and `yad gate status` prints it capped by the active people, and lists the touched repos as a hint for whom
|
|
@@ -103,7 +103,7 @@ writer** of the ledger. There is no pre-merge CI write — during review the pla
|
|
|
103
103
|
source of truth (native approvals + threads). On merge, CI re-reads approvals from the platform,
|
|
104
104
|
advances the step, and flips the artifact `status:` on the **default branch** (the only place CI ever
|
|
105
105
|
commits). Also install the `ledger-guard` check (yad-checks) so humans cannot commit gate-state files,
|
|
106
|
-
and its local counterpart `hooks/ledger-guard.
|
|
106
|
+
and its local counterpart `hooks/ledger-guard.mjs` — the harness hook that refuses an **agent** the
|
|
107
107
|
same write at the moment it tries it, instead of letting it surface as a CI failure later (#171).
|
|
108
108
|
Revoke-on-change is enforced at merge: on **GitHub** in code (an approval whose commit ≠ the merged
|
|
109
109
|
head is dropped — no setting needed); on **GitLab** it has no per-approval commit SHA, so enabling the
|
|
@@ -115,14 +115,14 @@ a maintainer can force it with `yad gate ci --branch <review-branch> --pr <n> --
|
|
|
115
115
|
default branch. (local mode keeps `yad gate sync` as the local writer.)
|
|
116
116
|
|
|
117
117
|
1. Run `yad check --fix` (the wiring is manifest-driven, like `yad-checks`): with a platform +
|
|
118
|
-
enabled bridge in `.sdlc/
|
|
118
|
+
enabled bridge in `.sdlc/product.json` it installs
|
|
119
119
|
- GitHub → `.github/workflows/yad-gate-sync.yml` (from `templates/github/yad-gate-sync.yml`)
|
|
120
120
|
- GitLab → `.gitlab/ci/yad-gate-sync.yml` (from `templates/gitlab/yad-gate-sync.gitlab-ci.yml`)
|
|
121
121
|
- plus the Product-side **verified-commits** gate (`checks/verified-commits.sh` + its workflow/fragment,
|
|
122
122
|
owned by `yad-checks`) so review PRs accept only platform-Verified (signed) commits — there is no
|
|
123
123
|
author allowlist
|
|
124
124
|
- the wired job runs an **exact** version, resolved at run time from the repo: the `YAD_VERSION`
|
|
125
|
-
variable, else `
|
|
125
|
+
variable, else `product.json` `gate_sync_version` (`hub.json` when it is absent), else the `.sdlc/cli-version.json` stamp, else the
|
|
126
126
|
floating major the fragment ships with (`YAD_MAJOR`, `4` in this release) — a committed pin is used
|
|
127
127
|
only when it is an exact release of that major, so `yad update` moves the fragment and the stamp
|
|
128
128
|
to a new major together. It stopped floating on the major because that let a release change a scheduled job's
|
|
@@ -102,7 +102,7 @@ repository access decides who may approve. A review with no login is not counted
|
|
|
102
102
|
(there is no per-comment id). A new round is opened only when the commenters or their counts change;
|
|
103
103
|
an unchanged re-read rewrites the latest round in place, keeping its date. Comment rounds are recorded
|
|
104
104
|
for an **open** step only, so re-visiting a merged review does not append a new round per pass.
|
|
105
|
-
- Update the step's `
|
|
105
|
+
- Update the step's PR-ledger `lastSyncedAt` (`product-prs.json` + `hub-prs.json`, same bytes) when the sync **learned something** — every sync on
|
|
106
106
|
an open step, and on a closed one only when the approval record actually changed (a re-opened review
|
|
107
107
|
that was re-approved). An identical re-sync leaves it alone, so the ledger does not churn.
|
|
108
108
|
- **Write the ledgers in a canonical order.** `approvals.json`, `comments.json` and the PR ledger
|
|
@@ -124,7 +124,7 @@ permanently. On an older yadflow the workaround is to disable the pipeline sched
|
|
|
124
124
|
advance gates via the push path; only the catch-up for squash merges and bare approvals is lost.
|
|
125
125
|
Since the wired job now runs an exact pin rather than floating on the major, that upgrade is a
|
|
126
126
|
deliberate act: `yad update` (which re-stamps `.sdlc/cli-version.json`), or a `gate_sync_version` in
|
|
127
|
-
|
|
127
|
+
`.sdlc/product.json` — see the version table below.
|
|
128
128
|
|
|
129
129
|
## Contract re-lock invalidates prior platform approvals too
|
|
130
130
|
|
|
@@ -204,7 +204,7 @@ and fall back to the major the fragment ships with — `YAD_MAJOR` in its `yad-p
|
|
|
204
204
|
| # | Source | Set it in |
|
|
205
205
|
|---|---|---|
|
|
206
206
|
| 1 | `YAD_VERSION` — used **verbatim**, the operator's override | GitHub: Settings → Secrets and variables → Actions → **Variables**. GitLab: Settings → CI/CD → **Variables** (beside `SDLC_GATE_TOKEN`) |
|
|
207
|
-
| 2 | `.sdlc/
|
|
207
|
+
| 2 | `.sdlc/product.json` → `gate_sync_version` (`.sdlc/hub.json` when `product.json` is absent) — this Product's committed pin | edit `product.json`, run `yad migrate --apply --keep product` (it copies it over `hub.json`; two that differ are refused, YAD-STATE-008), commit both. A Product that has only `hub.json`: edit that, and never create a `product.json` beside it by hand |
|
|
208
208
|
| 3 | `.sdlc/cli-version.json` → `version` — the yadflow that last wired the Product | `yad update` re-stamps it |
|
|
209
209
|
| 4 | `$YAD_MAJOR` — floating major, only when nothing above resolves. While only prereleases of that major exist (`4.0.0-next.N`) npm cannot resolve a bare major, so the job says so on stderr first; a Product wired by that release always has a stamp that resolves | — |
|
|
210
210
|
|
|
@@ -217,8 +217,9 @@ so anything that is not an exact release of this major is skipped, loudly, in fa
|
|
|
217
217
|
|
|
218
218
|
**A new major moves both files at once.** `yad update` rewrites the fragment from the release's template
|
|
219
219
|
and re-stamps `.sdlc/cli-version.json` in the same run, so a Product that updates to 4.x gets a fragment
|
|
220
|
-
with `YAD_MAJOR=4` and a `4.x` stamp together. A `gate_sync_version` pin in `
|
|
221
|
-
is then SKIPPED (the job logs `ignoring pin … not an exact 4.x release`) — update it or remove it
|
|
220
|
+
with `YAD_MAJOR=4` and a `4.x` stamp together. A `gate_sync_version` pin in `product.json` from the old major
|
|
221
|
+
is then SKIPPED (the job logs `ignoring pin … not an exact 4.x release`) — update it or remove it (edit
|
|
222
|
+
`product.json`, then `yad migrate --apply --keep product`; on a Product that has only `hub.json`, edit that). A
|
|
222
223
|
fragment the team EDITED is kept by `yad update` while the stamp still moves, so an edited fragment on
|
|
223
224
|
the old major would skip the new stamp; `yad update` names that case, and `--overwrite-local` replaces
|
|
224
225
|
the fragment (saving the edit beside it). The
|
|
@@ -287,13 +288,17 @@ which is exactly the intended revoke-on-change. Merge advances serialize on the
|
|
|
287
288
|
push retries with a rebase.
|
|
288
289
|
|
|
289
290
|
**Tokens.**
|
|
290
|
-
- GitHub: the ephemeral `github.token`
|
|
291
|
-
|
|
291
|
+
- GitHub: the ephemeral `github.token` — nothing stored. The workflow default is `contents: read`; the
|
|
292
|
+
two jobs that push (merge and reconcile) each ask for `contents: write` + `pull-requests: read`, and
|
|
293
|
+
push only the default branch.
|
|
292
294
|
- GitLab: a masked `SDLC_GATE_TOKEN` project access token (`read_api` + `write_repository`) — the one
|
|
293
295
|
documented bend of the no-stored-tokens rule; `CI_JOB_TOKEN` can neither read the approvals API nor
|
|
294
296
|
push. Used only for the merge-time default-branch push + the API reads.
|
|
295
|
-
- Protected default branch (GitHub): the merge advance needs to push it
|
|
296
|
-
|
|
297
|
+
- Protected default branch (GitHub): the merge advance needs to push it, and the built-in Actions token
|
|
298
|
+
cannot bypass branch protection or a ruleset (a ruleset's bypass list takes actors such as roles,
|
|
299
|
+
teams and GitHub Apps, never GitHub Actions). Pass a token whose owner may bypass to both jobs' `actions/checkout` — a GitHub
|
|
300
|
+
App on the bypass list, or a fine-grained PAT as `SDLC_GATE_TOKEN`. See yadflow's
|
|
301
|
+
`docs/branch-protection.md`.
|
|
297
302
|
|
|
298
303
|
**Manual sync & recovery.** In verified mode `yad gate sync` is **advisory** (read-only) — it prints the
|
|
299
304
|
predicate (and any `! may not be met:` / `! if the risk step were enforced:` warning, E73) but writes
|
|
@@ -304,7 +309,7 @@ the default branch: `yad gate ci --branch <review-branch> --pr <n> --merged` (th
|
|
|
304
309
|
unlike advisory `yad gate sync`). Local mode (no platform) keeps `yad gate sync` as the local writer.
|
|
305
310
|
The file ledger is still the source of truth.
|
|
306
311
|
|
|
307
|
-
Because CI records the `hub-prs.json`
|
|
312
|
+
Because CI records the PR-ledger pointer (`product-prs.json` + `hub-prs.json`) only at merge, a review the ledger has never seen still
|
|
308
313
|
has to be nameable — otherwise `sync` refuses a PR that is sitting merged on the platform. With no
|
|
309
314
|
recorded pointer, `yad gate sync <epic> <artifact>` resolves the PR/MR from the review branch
|
|
310
315
|
(`review/<epic>/<artifact>`) itself, and `--pr <n>` names it outright:
|
|
@@ -153,10 +153,10 @@ None of these decides anything any more. `yad doctor` warns so the team can dele
|
|
|
153
153
|
|
|
154
154
|
| Warning | What is left on disk |
|
|
155
155
|
|---|---|
|
|
156
|
-
| `people:roster-unused` | a non-empty `roster` in `.sdlc/
|
|
156
|
+
| `people:roster-unused` | a non-empty `roster` in `.sdlc/product.json` — still read for its name → login pairs only (see "Older records"); the hint counts the older records still waiting for a gate write to record their login, and says when it is safe to delete |
|
|
157
157
|
| `people:roster-ambiguous` | a roster name given to more than one login (only checked when the roster is non-empty) |
|
|
158
158
|
| `people:domain-owners-unused` | a repo in `.sdlc/repos.json` that lists `domain_owner` / `domain_owners` |
|
|
159
|
-
| `people:verified-authors-unused` | a `verified_authors` list in `.sdlc/
|
|
159
|
+
| `people:verified-authors-unused` | a `verified_authors` list in `.sdlc/product.json`, or a `.sdlc/verified-authors` file in the Product or a connected repo |
|
|
160
160
|
| `people:allowlist-gate-stale` | an older `checks/verified-commits.sh` that still enforces the author list (a local-ledger Product's CI files are not refreshed by `yad check --fix`) |
|
|
161
161
|
|
|
162
162
|
`yad roster` is removed (typing it prints a notice and exits 1), and `yad setup` collects no reviewers,
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
# review; it does not approve or merge.
|
|
11
11
|
set -euo pipefail
|
|
12
12
|
|
|
13
|
-
BODY="${1:?usage:
|
|
14
|
-
[ -f "$BODY" ] || { echo "
|
|
13
|
+
BODY="${1:?usage: product-route.sh <product-pr-description-file>}"
|
|
14
|
+
[ -f "$BODY" ] || { echo "product-route: file not found: $BODY" >&2; exit 2; }
|
|
15
15
|
|
|
16
16
|
# Value side of the FIRST line matching a label regex, comments + markdown markers stripped. Tolerant:
|
|
17
17
|
# a missing label yields empty (never aborts) — an advisory helper must still print for a half-filled body.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# yad-managed: yad-
|
|
1
|
+
# yad-managed: yad-product-bridge
|
|
2
2
|
# Merge-time gate sync for the PRODUCT (Path B). CI is the SOLE writer of the ledger, and it
|
|
3
3
|
# writes ONLY at merge, ONLY to the default branch.
|
|
4
4
|
#
|
|
@@ -30,11 +30,13 @@
|
|
|
30
30
|
#
|
|
31
31
|
# CI never approves and never merges — the merge click is the human approval act.
|
|
32
32
|
#
|
|
33
|
-
# Protected default branch? Only these jobs push there
|
|
34
|
-
#
|
|
35
|
-
#
|
|
36
|
-
#
|
|
37
|
-
#
|
|
33
|
+
# Protected default branch? Only these jobs push there, and the built-in Actions token cannot get past
|
|
34
|
+
# branch protection or a ruleset: a ruleset's bypass list takes actors such as roles, teams and GitHub
|
|
35
|
+
# Apps, never GitHub Actions. So give the push a token whose owner may bypass, and pass it to actions/checkout
|
|
36
|
+
# below via `with: { token: ${{ secrets.SDLC_GATE_TOKEN }} }`:
|
|
37
|
+
# a) a GitHub App on the bypass list (its installation token), or
|
|
38
|
+
# b) a fine-grained PAT of someone on the bypass list, stored as the SDLC_GATE_TOKEN secret.
|
|
39
|
+
# See docs/branch-protection.md in the yadflow repo, and references/bridge.md.
|
|
38
40
|
name: yad-gate-sync
|
|
39
41
|
on:
|
|
40
42
|
pull_request:
|
|
@@ -42,9 +44,10 @@ on:
|
|
|
42
44
|
schedule:
|
|
43
45
|
- cron: "*/15 * * * *"
|
|
44
46
|
|
|
47
|
+
# Read-only by default. Each job below asks for the write access it needs, so a job added later
|
|
48
|
+
# does not inherit a token that can push.
|
|
45
49
|
permissions:
|
|
46
|
-
contents:
|
|
47
|
-
pull-requests: read # gh pr view + reviewThreads GraphQL
|
|
50
|
+
contents: read
|
|
48
51
|
|
|
49
52
|
env:
|
|
50
53
|
# The manual override for which yadflow these jobs run: set a repository (or organization) Actions
|
|
@@ -71,15 +74,18 @@ jobs:
|
|
|
71
74
|
concurrency:
|
|
72
75
|
group: yad-gate-mergesync
|
|
73
76
|
cancel-in-progress: false
|
|
77
|
+
permissions:
|
|
78
|
+
contents: write # push the merge-advance ledger commit to the default branch
|
|
79
|
+
pull-requests: read # gh pr view + reviewThreads GraphQL
|
|
74
80
|
env:
|
|
75
81
|
GH_TOKEN: ${{ github.token }}
|
|
76
82
|
steps:
|
|
77
83
|
# Check out the default branch — the merge already landed the artifact here.
|
|
78
|
-
- uses: actions/checkout@v7
|
|
84
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
79
85
|
with:
|
|
80
86
|
ref: ${{ github.event.pull_request.base.ref }}
|
|
81
87
|
fetch-depth: 0
|
|
82
|
-
- uses: actions/setup-node@v7
|
|
88
|
+
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
|
83
89
|
with:
|
|
84
90
|
node-version: "20"
|
|
85
91
|
- name: Resolve the yadflow pin
|
|
@@ -87,7 +93,7 @@ jobs:
|
|
|
87
93
|
# >>> yad-pin
|
|
88
94
|
# Which yadflow this job runs, most explicit source first:
|
|
89
95
|
# 1. $YAD_VERSION — the platform variable, a human's deliberate override, verbatim
|
|
90
|
-
# 2. .sdlc/
|
|
96
|
+
# 2. .sdlc/product.json — "gate_sync_version" (.sdlc/hub.json when it is absent), the committed pin for this Product
|
|
91
97
|
# 3. .sdlc/cli-version.json — the version that last wired it (`yad update` re-stamps this)
|
|
92
98
|
# 4. $YAD_MAJOR — floating major, only when nothing above resolves
|
|
93
99
|
# Sources 2 and 3 are machine-written files a stale or hand-edited checkout can get wrong, so
|
|
@@ -108,9 +114,11 @@ jobs:
|
|
|
108
114
|
tr -d '\n' < "$1" 2>/dev/null | sed -nE "s/.*\"$2\"[[:space:]]*:[[:space:]]*\"([^\"]*)\".*/\1/p"
|
|
109
115
|
}
|
|
110
116
|
yad_pin_valid() { printf '%s' "${1:-}" | grep -Eq "^${YAD_MAJOR}\.[0-9]+\.[0-9]+(-[0-9A-Za-z.]+)?\$"; }
|
|
117
|
+
# The Product settings under their new name first (E122); the old one until v5 deletes it.
|
|
118
|
+
YAD_PIN_FILE=.sdlc/product.json; [ -f "$YAD_PIN_FILE" ] || YAD_PIN_FILE=.sdlc/hub.json
|
|
111
119
|
YAD_PKG="${YAD_VERSION:-}"
|
|
112
120
|
if [ -z "$YAD_PKG" ]; then
|
|
113
|
-
for _cand in "$(yad_pin_read
|
|
121
|
+
for _cand in "$(yad_pin_read "$YAD_PIN_FILE" gate_sync_version)" "$(yad_pin_read .sdlc/cli-version.json version)"; do
|
|
114
122
|
[ -n "$_cand" ] || continue
|
|
115
123
|
if yad_pin_valid "$_cand"; then YAD_PKG="$_cand"; break; fi
|
|
116
124
|
echo "yad-gate-sync: ignoring pin '${_cand}' — not an exact ${YAD_MAJOR}.x release." >&2
|
|
@@ -150,14 +158,17 @@ jobs:
|
|
|
150
158
|
concurrency:
|
|
151
159
|
group: yad-gate-mergesync
|
|
152
160
|
cancel-in-progress: false
|
|
161
|
+
permissions:
|
|
162
|
+
contents: write # push the merge-advance ledger commit to the default branch
|
|
163
|
+
pull-requests: read # gh pr view + reviewThreads GraphQL
|
|
153
164
|
env:
|
|
154
165
|
GH_TOKEN: ${{ github.token }}
|
|
155
166
|
steps:
|
|
156
|
-
- uses: actions/checkout@v7
|
|
167
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
157
168
|
with:
|
|
158
169
|
ref: ${{ github.event.repository.default_branch }}
|
|
159
170
|
fetch-depth: 0
|
|
160
|
-
- uses: actions/setup-node@v7
|
|
171
|
+
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
|
161
172
|
with:
|
|
162
173
|
node-version: "20"
|
|
163
174
|
- name: Resolve the yadflow pin
|
|
@@ -165,7 +176,7 @@ jobs:
|
|
|
165
176
|
# >>> yad-pin
|
|
166
177
|
# Which yadflow this job runs, most explicit source first:
|
|
167
178
|
# 1. $YAD_VERSION — the platform variable, a human's deliberate override, verbatim
|
|
168
|
-
# 2. .sdlc/
|
|
179
|
+
# 2. .sdlc/product.json — "gate_sync_version" (.sdlc/hub.json when it is absent), the committed pin for this Product
|
|
169
180
|
# 3. .sdlc/cli-version.json — the version that last wired it (`yad update` re-stamps this)
|
|
170
181
|
# 4. $YAD_MAJOR — floating major, only when nothing above resolves
|
|
171
182
|
# Sources 2 and 3 are machine-written files a stale or hand-edited checkout can get wrong, so
|
|
@@ -186,9 +197,11 @@ jobs:
|
|
|
186
197
|
tr -d '\n' < "$1" 2>/dev/null | sed -nE "s/.*\"$2\"[[:space:]]*:[[:space:]]*\"([^\"]*)\".*/\1/p"
|
|
187
198
|
}
|
|
188
199
|
yad_pin_valid() { printf '%s' "${1:-}" | grep -Eq "^${YAD_MAJOR}\.[0-9]+\.[0-9]+(-[0-9A-Za-z.]+)?\$"; }
|
|
200
|
+
# The Product settings under their new name first (E122); the old one until v5 deletes it.
|
|
201
|
+
YAD_PIN_FILE=.sdlc/product.json; [ -f "$YAD_PIN_FILE" ] || YAD_PIN_FILE=.sdlc/hub.json
|
|
189
202
|
YAD_PKG="${YAD_VERSION:-}"
|
|
190
203
|
if [ -z "$YAD_PKG" ]; then
|
|
191
|
-
for _cand in "$(yad_pin_read
|
|
204
|
+
for _cand in "$(yad_pin_read "$YAD_PIN_FILE" gate_sync_version)" "$(yad_pin_read .sdlc/cli-version.json version)"; do
|
|
192
205
|
[ -n "$_cand" ] || continue
|
|
193
206
|
if yad_pin_valid "$_cand"; then YAD_PKG="$_cand"; break; fi
|
|
194
207
|
echo "yad-gate-sync: ignoring pin '${_cand}' — not an exact ${YAD_MAJOR}.x release." >&2
|
package/skills/{yad-hub-bridge → yad-product-bridge}/templates/gitlab/gitlab-ci.include-root.yml
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# SDLC gate sync — minimal root pipeline for the PRODUCT.
|
|
2
|
-
# Written by the yad-
|
|
2
|
+
# Written by the yad-product-bridge `wire` action ONLY when the Product has no existing root
|
|
3
3
|
# `.gitlab-ci.yml`. The job itself lives in the included fragment so the same single source is
|
|
4
4
|
# reused whether or not a root pipeline already exists. If a root later grows real jobs, they
|
|
5
5
|
# coexist with the included yad-* job (which carries `needs: []` and no `stage:`).
|
package/skills/{yad-hub-bridge → yad-product-bridge}/templates/gitlab/yad-gate-sync.gitlab-ci.yml
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# yad-managed-include: yad-
|
|
1
|
+
# yad-managed-include: yad-product-bridge
|
|
2
2
|
# Merge-time gate sync for the PRODUCT, as an INCLUDABLE fragment (Path B). Pulled into the
|
|
3
3
|
# Product's root .gitlab-ci.yml via:
|
|
4
4
|
# include:
|
|
@@ -90,7 +90,7 @@ yad-gate-sync:
|
|
|
90
90
|
# >>> yad-pin
|
|
91
91
|
# Which yadflow this job runs, most explicit source first:
|
|
92
92
|
# 1. $YAD_VERSION — the CI/CD variable, a human's deliberate override, verbatim
|
|
93
|
-
# 2. .sdlc/
|
|
93
|
+
# 2. .sdlc/product.json — "gate_sync_version" (.sdlc/hub.json when it is absent), the committed pin for this Product
|
|
94
94
|
# 3. .sdlc/cli-version.json — the version that last wired it (`yad update` re-stamps this)
|
|
95
95
|
# 4. $YAD_MAJOR — floating major, only when nothing above resolves
|
|
96
96
|
# Sources 2 and 3 are machine-written files a stale or hand-edited checkout can get wrong, so
|
|
@@ -111,9 +111,11 @@ yad-gate-sync:
|
|
|
111
111
|
tr -d '\n' < "$1" 2>/dev/null | sed -nE "s/.*\"$2\"[[:space:]]*:[[:space:]]*\"([^\"]*)\".*/\1/p"
|
|
112
112
|
}
|
|
113
113
|
yad_pin_valid() { printf '%s' "${1:-}" | grep -Eq "^${YAD_MAJOR}\.[0-9]+\.[0-9]+(-[0-9A-Za-z.]+)?\$"; }
|
|
114
|
+
# The Product settings under their new name first (E122); the old one until v5 deletes it.
|
|
115
|
+
YAD_PIN_FILE=.sdlc/product.json; [ -f "$YAD_PIN_FILE" ] || YAD_PIN_FILE=.sdlc/hub.json
|
|
114
116
|
YAD_PKG="${YAD_VERSION:-}"
|
|
115
117
|
if [ -z "$YAD_PKG" ]; then
|
|
116
|
-
for _cand in "$(yad_pin_read
|
|
118
|
+
for _cand in "$(yad_pin_read "$YAD_PIN_FILE" gate_sync_version)" "$(yad_pin_read .sdlc/cli-version.json version)"; do
|
|
117
119
|
[ -n "$_cand" ] || continue
|
|
118
120
|
if yad_pin_valid "$_cand"; then YAD_PKG="$_cand"; break; fi
|
|
119
121
|
echo "yad-gate-sync: ignoring pin '${_cand}' — not an exact ${YAD_MAJOR}.x release." >&2
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: yad-report
|
|
3
|
-
description: 'Self issue reporter for the yad CLI. When a yadflow flow breaks, file a well-formed bug in the upstream yadflow repo (abdelrahmannasr/yadflow) with auto-scrubbed diagnostics attached
|
|
3
|
+
description: 'Self issue reporter for the yad CLI. When a yadflow flow breaks, file a well-formed bug in the upstream yadflow repo (abdelrahmannasr/yadflow) with auto-scrubbed diagnostics attached. Drives the `yad report` command: it captures ONLY a privacy-safe allowlist (yadflow/node/os version, tool present+authenticated booleans, Product platform enum, the YadError code/hint, a path-scrubbed error message, and the failing command + flag NAMES) — never absolute paths, hostnames, git URLs, repo names, logins/emails, epic/story IDs, branch names, or flag values. It searches open issues first to avoid duplicates, shows the exact payload, and asks before posting anything to the public repo. Files directly via an authenticated gh, or falls back to a prefilled issues/new URL. Also offered automatically after an unexpected failure (interactive only; YAD_NO_REPORT=1 or SDLC_NONINTERACTIVE disables it). Use when the user says "yad report", "report this bug", "file an issue", or "something broke in the flow".'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# yad — Self Issue Reporter
|
|
@@ -32,7 +32,7 @@ The CLI never calls an LLM — **you** (this skill) generate the text and post i
|
|
|
32
32
|
questions and flagged concerns become the review record. If you **cannot** answer from the material,
|
|
33
33
|
say so — that gap is itself a finding (post it as a genuine, blocking comment).
|
|
34
34
|
4. **🏆 Social (E)** — a verified-engagement mark on a real companion review, a friendly public
|
|
35
|
-
@-mention nudge on a bare approve, and light read-only gamification surfaced by `yad
|
|
35
|
+
@-mention nudge on a bare approve, and light read-only gamification surfaced by the `yad-status` skill.
|
|
36
36
|
|
|
37
37
|
## Markers (the contract with the gate — all live in PLATFORM data, never a ledger file)
|
|
38
38
|
|
|
@@ -54,7 +54,7 @@ Inputs: `epic` + `artifact` (Shape) **or** `repo` + `pr` (Build); and the `actio
|
|
|
54
54
|
`requireEngagement`, and `step.gateRule` — the step's full approver count (`base`, `riskStep`,
|
|
55
55
|
`needed`, `risk`) — with `step.cap`, an object `{ active, limit, to, capped }` (E72): `to` is the count
|
|
56
56
|
capped by the active people, `capped` is true when the cap lowered it, and the field is `null` when
|
|
57
|
-
the people could not be counted, which is worth naming in the trailer on a high-risk step and never a blocker. Build: `yad review
|
|
57
|
+
the people could not be counted, which is worth naming in the trailer on a high-risk step and never a blocker. Build: `yad review context --repo <r> --pr <n>` (see `yad-engineer-review`) provides the
|
|
58
58
|
diff + code-map grounding. Read the named files yourself — never invent content.
|
|
59
59
|
2. **Trailer.** Generate ≤6 lines (what / risk / read-time), grounded only in the bundle. Post it:
|
|
60
60
|
`yad gate trailer <epic> <artifact> --body "<text>" [--pr <n>]` (idempotent; re-run after edits).
|
|
@@ -81,7 +81,7 @@ Inputs: `epic` + `artifact` (Shape) **or** `repo` + `pr` (Build); and the `actio
|
|
|
81
81
|
- **Companion comments never block; genuine concerns always do.** Flag the former with `yad:noblock`;
|
|
82
82
|
leave the latter unflagged.
|
|
83
83
|
- **The engagement signal is gameable and you say so.** It makes review quality visible; it is not
|
|
84
|
-
proof. The strict-mode switch is `
|
|
84
|
+
proof. The strict-mode switch is `review.requireEngagement` (Product settings) (off by default).
|
|
85
85
|
- **The companion never approves on a human's behalf and never merges.** It assists; the human acts.
|
|
86
86
|
|
|
87
87
|
## local mode (no platform)
|