yadflow 2.14.0 → 2.16.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.
@@ -5,22 +5,39 @@
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
7
  # --profile hub — a front-half artifact-review title "review: <artifact> (EP-<slug>)", the shape
8
- # `yad gate open` creates (cli/gate.mjs).
8
+ # `yad gate open` creates (cli/gate.mjs) — BUT only for review/EP-* head branches. Every other
9
+ # hub PR is a tooling/code change to the hub itself and follows the code convention; pass the
10
+ # head ref via --head so the gate can tell the two apart (a tooling PR has no EP artifact to
11
+ # review). With no --head, the hub profile stays strict (requires the review shape).
12
+ # Branch name is not enough on its own: a non-review head that actually changes front-half
13
+ # artifacts (epics/**) would otherwise slip past the review workflow with a plain code title.
14
+ # Pass the PR's changed paths via --changed <file> (one path per line); when they touch epics/**
15
+ # on a non-review head the gate FAILS — artifact changes must go through a review/EP-* PR.
9
16
  # The title is passed as the (single) positional arg; CI injects it from the event payload
10
17
  # (GitHub: github.event.pull_request.title; GitLab: $CI_MERGE_REQUEST_TITLE).
11
18
  set -euo pipefail
12
19
 
13
20
  PROFILE=code
21
+ HEADREF=""
22
+ CHANGED=""
14
23
  ARGS=()
15
24
  while [ $# -gt 0 ]; do
16
25
  case "$1" in
17
26
  --profile) PROFILE="${2:-code}"; shift 2 ;;
18
27
  --profile=*) PROFILE="${1#*=}"; shift ;;
28
+ --head) HEADREF="${2:-}"; shift 2 ;;
29
+ --head=*) HEADREF="${1#*=}"; shift ;;
30
+ --changed) CHANGED="${2:-}"; shift 2 ;;
31
+ --changed=*) CHANGED="${1#*=}"; shift ;;
19
32
  *) ARGS+=("$1"); shift ;;
20
33
  esac
21
34
  done
22
35
  case "$PROFILE" in code|hub) ;; *) echo "FAIL [pr-title]: unknown --profile '$PROFILE' (code|hub)."; exit 1 ;; esac
23
36
 
37
+ # True when the PR changes a front-half artifact (anything under epics/**). Reads the --changed list
38
+ # of paths CI computed from the PR diff; with no list (direct caller / test) it reports false.
39
+ artifact_changed() { [ -n "$CHANGED" ] && [ -f "$CHANGED" ] && grep -qE '^epics/' "$CHANGED"; }
40
+
24
41
  TITLE="${ARGS[0]:-}"
25
42
  if [ -z "$TITLE" ]; then
26
43
  echo "FAIL [pr-title]: empty title — pass the PR/MR title as the argument."
@@ -29,23 +46,43 @@ fi
29
46
 
30
47
  TYPES='feat|fix|docs|refactor|test|perf|build|ci|chore|revert'
31
48
 
32
- if [ "$PROFILE" = hub ]; then
33
- # review: <artifact> (EP-<slug>)
34
- if printf '%s' "$TITLE" | grep -qE '^review: .+ \(EP-[a-z0-9-]+\)$'; then
35
- echo "PASS [pr-title]: '${TITLE}' (profile: hub)"
36
- exit 0
49
+ # Conventional-Commits subject (optional scope + breaking `!`), no trailing period. Used by the code
50
+ # profile and by hub tooling PRs (any head branch that is not review/EP-*).
51
+ check_code_title() {
52
+ if ! printf '%s' "$TITLE" | grep -qE "^(${TYPES})(\([a-z0-9._-]+\))?!?: .+"; then
53
+ echo "FAIL [pr-title]: '${TITLE}' is not '<type>(<scope>)?!?: <description>' (type one of: ${TYPES//|/, })."
54
+ exit 1
37
55
  fi
38
- echo "FAIL [pr-title]: '${TITLE}' is not a hub review title 'review: <artifact> (EP-<slug>)'."
39
- exit 1
40
- fi
56
+ if printf '%s' "$TITLE" | grep -qE '\.$'; then
57
+ echo "FAIL [pr-title]: '${TITLE}' must not end with a period."
58
+ exit 1
59
+ fi
60
+ echo "PASS [pr-title]: '${TITLE}' (profile: ${PROFILE}, tooling/code)"
61
+ exit 0
62
+ }
41
63
 
42
- # code profile Conventional-Commits subject (optional scope + breaking `!`), no trailing period.
43
- if ! printf '%s' "$TITLE" | grep -qE "^(${TYPES})(\([a-z0-9._-]+\))?!?: .+"; then
44
- echo "FAIL [pr-title]: '${TITLE}' is not '<type>(<scope>)?!?: <description>' (type one of: ${TYPES//|/, })."
45
- exit 1
46
- fi
47
- if printf '%s' "$TITLE" | grep -qE '\.$'; then
48
- echo "FAIL [pr-title]: '${TITLE}' must not end with a period."
49
- exit 1
64
+ if [ "$PROFILE" = hub ]; then
65
+ # review/EP-* head branch (or unknown head ref) => front-half artifact-review PR: 'review: <artifact> (EP-<slug>)'.
66
+ case "$HEADREF" in
67
+ review/EP-*|"")
68
+ if printf '%s' "$TITLE" | grep -qE '^review: .+ \(EP-[a-z0-9-]+\)$'; then
69
+ echo "PASS [pr-title]: '${TITLE}' (profile: hub, artifact-review)"
70
+ exit 0
71
+ fi
72
+ echo "FAIL [pr-title]: '${TITLE}' is not a hub review title 'review: <artifact> (EP-<slug>)'."
73
+ exit 1
74
+ ;;
75
+ *)
76
+ # Any other hub PR is a tooling/code change to the hub itself — UNLESS it changes front-half
77
+ # artifacts (epics/**), which must go through a review/EP-* PR. Without this guard a non-review
78
+ # head could carry an artifact change past the front-half review with only a code title.
79
+ if artifact_changed; then
80
+ echo "FAIL [pr-title]: head '${HEADREF}' changes front-half artifacts (epics/**) but is not a review/EP-* branch — artifact changes must go through a review PR."
81
+ exit 1
82
+ fi
83
+ # tooling only — fall through to the code convention.
84
+ ;;
85
+ esac
50
86
  fi
51
- echo "PASS [pr-title]: '${TITLE}' (profile: code)"
87
+
88
+ check_code_title
@@ -165,21 +165,23 @@ If the predicate **passes**:
165
165
  now `ready-for-build`, with `test-cases` running in parallel).
166
166
 
167
167
  ### PR-driven automation (the `yad gate` CLI)
168
- When the hub has a platform, the mechanical `open`/`sync`/`advance` is performed deterministically by the
169
- **`yad gate` CLI** (`yad gate open|sync|comments|status`), which writes the same `.sdlc/` + `reviews/`
170
- records this skill describes. The skill's job is then the human half: presenting the artifact, helping the
171
- owner address comments, and narrating the gate. The CLI is the single implementation of the gh/glab
172
- mechanics do not hand-run gh/glab recipes when it is installed.
168
+ When the hub has a platform, **CI is the sole writer of the ledger**. `yad gate open` opens the review
169
+ PR only; CI (`yad gate ci`) writes the `.sdlc/` + `reviews/` records this skill describes. The skill's
170
+ job is the human half: presenting the artifact, helping the owner address comments, and narrating the
171
+ gate. Local `yad gate sync` is advisory in bridge mode (reads the platform, prints status, writes
172
+ nothing); a human must never commit gate-state files (the `ledger-guard` check rejects it).
173
173
 
174
174
  Under that CLI the gate **advances on merge**: a review PR/MR whose reviewer rule is satisfied, whose
175
175
  comment threads are **all resolved**, and which has been **merged** auto-marks the step `done` and
176
176
  unblocks the next step. (Until those three hold, the step stays `in_review`.)
177
177
 
178
- `sync` can also be **event-driven**: when the hub is wired with the gate-sync CI (`yad-hub-bridge`
179
- `wire` action), every approval / change request / dismissal / merge on the review PR/MR triggers
180
- `yad gate ci` on the hub, which runs the same sync and commits the ledger updates directly to the
181
- hub's default branch (pull to see them locally). The predicate, the human merge, and manual
182
- `yad gate sync` are all unchanged CI never approves and never merges.
178
+ The flow is **merge-driven** (wired by `yad-hub-bridge` `wire`): during review CI writes nothing — the
179
+ platform PR/MR is the source of truth (native approvals + threads), and CI never touches the review
180
+ branch (so an in-flight approval is never dismissed and required checks never strand). On the human
181
+ **merge** CI re-reads approvals, advances the step, and flips the artifact `status:` on the **default
182
+ branch**. After a merge, `git checkout <default> && git pull` to see it. The predicate and the human
183
+ merge are unchanged — CI never approves and never merges. File-only mode (no platform) keeps the local
184
+ write path.
183
185
 
184
186
  ### Hard rules (build plan §1, §5)
185
187
  - **The merge click is the human approval act.** A front step advances only when a human merges the