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.
Files changed (156) hide show
  1. package/CHANGELOG.md +355 -0
  2. package/README.md +79 -26
  3. package/bin/commands.mjs +41 -0
  4. package/bin/yad.mjs +437 -124
  5. package/cli/artifact-status.mjs +34 -15
  6. package/cli/checkpoint.mjs +69 -49
  7. package/cli/codeowners-command.mjs +170 -0
  8. package/cli/codeowners.mjs +397 -0
  9. package/cli/commit.mjs +13 -9
  10. package/cli/companion.mjs +2 -2
  11. package/cli/dial.mjs +183 -0
  12. package/cli/docs.mjs +88 -32
  13. package/cli/doctor.mjs +1472 -97
  14. package/cli/epic-state.mjs +3478 -232
  15. package/cli/epic.mjs +506 -0
  16. package/cli/errors.mjs +4 -1
  17. package/cli/gate.mjs +1002 -209
  18. package/cli/history.mjs +556 -0
  19. package/cli/hook.mjs +266 -55
  20. package/cli/hubcommit.mjs +6 -17
  21. package/cli/index-command.mjs +87 -0
  22. package/cli/ledger.mjs +57 -7
  23. package/cli/lib.mjs +184 -18
  24. package/cli/manifest.mjs +367 -56
  25. package/cli/migrate.mjs +726 -53
  26. package/cli/mode.mjs +170 -0
  27. package/cli/next.mjs +349 -90
  28. package/cli/openpr.mjs +191 -39
  29. package/cli/people.mjs +654 -0
  30. package/cli/plan.mjs +417 -132
  31. package/cli/platform.mjs +110 -129
  32. package/cli/product-index.mjs +287 -0
  33. package/cli/protection.mjs +706 -0
  34. package/cli/reconcile.mjs +38 -12
  35. package/cli/repo-publish.mjs +24 -26
  36. package/cli/repo.mjs +23 -14
  37. package/cli/report.mjs +21 -15
  38. package/cli/review.mjs +24 -27
  39. package/cli/riskmap-command.mjs +289 -0
  40. package/cli/riskmap.mjs +373 -0
  41. package/cli/setup.mjs +139 -287
  42. package/cli/ship.mjs +7 -6
  43. package/cli/skill.mjs +180 -0
  44. package/cli/skip.mjs +211 -30
  45. package/cli/thread.mjs +42 -17
  46. package/cli/tidy.mjs +20 -20
  47. package/cli/update-commit.mjs +22 -22
  48. package/cli/usage.mjs +115 -109
  49. package/package.json +3 -3
  50. package/skills/sdlc/config.yaml +166 -87
  51. package/skills/sdlc/module-help.csv +35 -35
  52. package/skills/yad-analysis/SKILL.md +125 -65
  53. package/skills/yad-architecture/SKILL.md +34 -23
  54. package/skills/yad-architecture/references/contract-format.md +10 -8
  55. package/skills/yad-backfill/SKILL.md +14 -8
  56. package/skills/yad-backfill/references/backfill.md +1 -1
  57. package/skills/yad-change/SKILL.md +127 -52
  58. package/skills/yad-change/references/triage.md +42 -28
  59. package/skills/yad-checks/SKILL.md +89 -45
  60. package/skills/yad-checks/references/check-gates.md +315 -92
  61. package/skills/yad-checks/templates/checks/build-test-lint.sh +25 -7
  62. package/skills/yad-checks/templates/checks/commit-message.sh +17 -3
  63. package/skills/yad-checks/templates/checks/contract-check.sh +58 -2
  64. package/skills/yad-checks/templates/checks/epic-open.sh +3 -3
  65. package/skills/yad-checks/templates/checks/install-deps.sh +46 -0
  66. package/skills/yad-checks/templates/checks/ledger-guard.sh +94 -18
  67. package/skills/yad-checks/templates/checks/lineage-check.sh +23 -9
  68. package/skills/yad-checks/templates/checks/package-manager.sh +140 -0
  69. package/skills/yad-checks/templates/checks/reconcile-debt-check.sh +4 -4
  70. package/skills/yad-checks/templates/checks/risk-map-check.sh +438 -0
  71. package/skills/yad-checks/templates/checks/verified-commits.sh +20 -46
  72. package/skills/yad-checks/templates/github/yad-checks.yml +37 -5
  73. package/skills/yad-checks/templates/github/yad-hub-checks.yml +5 -5
  74. package/skills/yad-checks/templates/github/yad-update-guard.yml +3 -4
  75. package/skills/yad-checks/templates/github/yad-verified-commits.yml +4 -4
  76. package/skills/yad-checks/templates/gitlab/.gitlab-ci.yml +7 -1
  77. package/skills/yad-checks/templates/gitlab/yad-checks.gitlab-ci.yml +22 -4
  78. package/skills/yad-checks/templates/gitlab/yad-hub-checks.gitlab-ci.yml +5 -5
  79. package/skills/yad-checks/templates/gitlab/yad-verified-commits.gitlab-ci.yml +4 -4
  80. package/skills/yad-checks/templates/hooks/ledger-guard-cursor.sh +91 -0
  81. package/skills/yad-checks/templates/hooks/ledger-guard.sh +38 -7
  82. package/skills/yad-commit/SKILL.md +6 -6
  83. package/skills/yad-connect-design/SKILL.md +6 -6
  84. package/skills/yad-connect-design/references/design-context.md +1 -1
  85. package/skills/yad-connect-design/references/design-registry.md +2 -2
  86. package/skills/yad-connect-docs/SKILL.md +12 -12
  87. package/skills/yad-connect-docs/references/docs-registry.md +1 -1
  88. package/skills/yad-connect-learning/SKILL.md +5 -5
  89. package/skills/yad-connect-learning/references/learning-registry.md +2 -2
  90. package/skills/yad-connect-repos/SKILL.md +92 -54
  91. package/skills/yad-connect-repos/references/code-context.md +6 -6
  92. package/skills/yad-connect-repos/references/hub-config.md +68 -58
  93. package/skills/yad-connect-repos/references/repos-registry.md +10 -9
  94. package/skills/yad-connect-repos/references/risk-map.md +81 -0
  95. package/skills/yad-connect-testing/SKILL.md +6 -6
  96. package/skills/yad-connect-testing/references/testing-context.md +3 -4
  97. package/skills/yad-connect-testing/references/testing-registry.md +2 -2
  98. package/skills/yad-defects/SKILL.md +8 -8
  99. package/skills/yad-discovery/SKILL.md +130 -94
  100. package/skills/yad-discovery/references/discovery-schema.md +23 -7
  101. package/skills/yad-discovery/references/foundation-schema.md +374 -0
  102. package/skills/yad-docs/SKILL.md +16 -11
  103. package/skills/yad-docs/references/data-mapping.md +9 -7
  104. package/skills/yad-docs/templates/app/package-lock.json +3 -3
  105. package/skills/yad-docs-overview/SKILL.md +32 -17
  106. package/skills/yad-docs-overview/references/pipeline-model.md +47 -28
  107. package/skills/yad-docs-sync/SKILL.md +10 -5
  108. package/skills/yad-docs-sync/references/staleness.md +8 -7
  109. package/skills/yad-engineer-review/SKILL.md +88 -24
  110. package/skills/yad-engineer-review/references/ship-and-record.md +25 -16
  111. package/skills/yad-epic/SKILL.md +178 -100
  112. package/skills/yad-epic/references/state-schema.md +626 -117
  113. package/skills/yad-hub-bridge/SKILL.md +66 -48
  114. package/skills/yad-hub-bridge/references/bridge.md +110 -83
  115. package/skills/yad-hub-bridge/references/login-roster.md +163 -70
  116. package/skills/yad-hub-bridge/templates/checks/hub-route.sh +22 -19
  117. package/skills/yad-hub-bridge/templates/github/yad-gate-sync.yml +34 -14
  118. package/skills/yad-hub-bridge/templates/gitlab/gitlab-ci.include-root.yml +2 -2
  119. package/skills/yad-hub-bridge/templates/gitlab/yad-gate-sync.gitlab-ci.yml +22 -12
  120. package/skills/yad-implement/SKILL.md +29 -15
  121. package/skills/yad-implement/references/implement-conventions.md +2 -2
  122. package/skills/yad-learn/SKILL.md +9 -9
  123. package/skills/yad-learn/references/learning-state.md +2 -2
  124. package/skills/yad-open-pr/SKILL.md +64 -29
  125. package/skills/yad-pair-review/SKILL.md +18 -16
  126. package/skills/yad-pair-review/references/session-state.md +4 -4
  127. package/skills/yad-pr-template/SKILL.md +48 -27
  128. package/skills/yad-pr-template/references/risk-routing.md +97 -24
  129. package/skills/yad-pr-template/templates/checks/pr-template.sh +37 -15
  130. package/skills/yad-pr-template/templates/checks/pr-title.sh +27 -13
  131. package/skills/yad-pr-template/templates/checks/risk-route.sh +107 -14
  132. package/skills/yad-pr-template/templates/github/pull_request_template.md +7 -5
  133. package/skills/yad-pr-template/templates/gitlab/merge_request_templates/Default.md +7 -5
  134. package/skills/yad-pr-template/templates/hub/github/pull_request_template.md +15 -14
  135. package/skills/yad-pr-template/templates/hub/gitlab/merge_request_templates/Default.md +15 -13
  136. package/skills/yad-reconcile/SKILL.md +3 -3
  137. package/skills/yad-report/SKILL.md +5 -5
  138. package/skills/yad-review-companion/SKILL.md +12 -9
  139. package/skills/yad-review-gate/SKILL.md +198 -79
  140. package/skills/yad-review-gate/references/gating.md +230 -54
  141. package/skills/yad-run/SKILL.md +86 -56
  142. package/skills/yad-run/references/run-loop.md +67 -45
  143. package/skills/yad-ship/SKILL.md +18 -14
  144. package/skills/yad-spec/SKILL.md +31 -17
  145. package/skills/yad-spec/references/spec-handoff.md +17 -5
  146. package/skills/yad-status/SKILL.md +114 -56
  147. package/skills/yad-stories/SKILL.md +42 -27
  148. package/skills/yad-stories/references/story-schema.md +10 -9
  149. package/skills/yad-stub/SKILL.md +59 -48
  150. package/skills/yad-sync-repos/SKILL.md +3 -3
  151. package/skills/yad-test-cases/SKILL.md +37 -30
  152. package/skills/yad-test-cases/references/test-cases-schema.md +8 -5
  153. package/skills/yad-timeline/SKILL.md +8 -7
  154. package/skills/yad-ui/SKILL.md +46 -25
  155. package/cli/roster.mjs +0 -164
  156. package/skills/sdlc/install.sh +0 -68
package/cli/tidy.mjs CHANGED
@@ -1,4 +1,4 @@
1
- // `yad tidy up` — fold FINISHED back-half shards back into their single folded ledger, on demand.
1
+ // `yad tidy up` — fold FINISHED Build shards back into their single folded ledger, on demand.
2
2
  //
3
3
  // The two logs are shard-then-fold (cli/ledger.mjs): writers drop one loose file per entry so
4
4
  // concurrent writers never conflict. Left alone they accumulate; `yad tidy up` is the "pack it up"
@@ -9,11 +9,11 @@
9
9
  import fs from 'node:fs';
10
10
  import path from 'node:path';
11
11
  import { c, log, ok, info, fail, hand, exists, pushWithRebase } from './lib.mjs';
12
- import { PROJECT_FILES } from './manifest.mjs';
13
- import { loadHub } from './gate.mjs';
14
- import { resolveCommitterLogin } from './platform.mjs';
12
+ import { productConfigPath } from './manifest.mjs';
13
+ import { loadProduct } from './gate.mjs';
14
+ import { platformLogin } from './platform.mjs';
15
15
  import { checkpointAuthor } from './checkpoint.mjs';
16
- import { hubGit, resolveDefaultBranch, guardDefaultBranch } from './hubcommit.mjs';
16
+ import { productGit, resolveDefaultBranch, guardDefaultBranch } from './hubcommit.mjs';
17
17
  import { foldTrust, foldBuild } from './ledger.mjs';
18
18
  import { epicRoot, isValidEpicId, readFrontmatter } from './epic-state.mjs';
19
19
 
@@ -39,14 +39,14 @@ function epicsWithLedgers(root) {
39
39
  export async function runTidy(root, opts = {}) {
40
40
  log(c.bold('\nyad tidy up'));
41
41
  if (!exists(path.join(root, '.git'))) { fail('not a git repo'); process.exitCode = 1; return; }
42
- if (!exists(path.join(root, PROJECT_FILES.hubConfig))) {
43
- fail('no .sdlc/hub.json — run `yad tidy up` from the product hub');
42
+ if (!exists(productConfigPath(root))) {
43
+ fail('no .sdlc/hub.json — run `yad tidy up` from the Product');
44
44
  process.exitCode = 1;
45
45
  return;
46
46
  }
47
47
 
48
- const { hub } = loadHub(root);
49
- const git = hubGit(root);
48
+ const { hub } = loadProduct(root);
49
+ const git = productGit(root);
50
50
  const branch = git('rev-parse', '--abbrev-ref', 'HEAD').stdout;
51
51
  const defaultBranch = resolveDefaultBranch(git, hub);
52
52
  if (!guardDefaultBranch(branch, defaultBranch, { allowBranch: opts.allowBranch, cmd: 'yad tidy up' })) return;
@@ -68,16 +68,16 @@ export async function runTidy(root, opts = {}) {
68
68
  info(`${epic}: ${opts.dryRun ? 'would fold' : 'folded'} ${t.folded} trust + ${b.folded} build shard(s)`);
69
69
  }
70
70
  }
71
- if (!folded) { info('nothing to tidy — no finished shards to fold'); return { folded: 0 }; }
71
+ if (!folded) { info('nothing to tidy — no finished shards to fold'); return { folded: 0, epics: [], message: null, committed: false, pushed: false, dryRun: !!opts.dryRun }; }
72
72
 
73
- const author = checkpointAuthor(resolveCommitterLogin(root, hub?.roster || []), git('config', 'user.name').stdout);
73
+ const author = checkpointAuthor(platformLogin(root, hub?.platform), git('config', 'user.name').stdout);
74
74
  const label = touched.length === 1 ? touched[0] : `${touched.length} epics`;
75
- const message = `chore(hub): tidy back-half ledgers — ${label} by ${author} [skip ci]`;
75
+ const message = `chore(hub): tidy Build ledgers — ${label} by ${author} [skip ci]`;
76
76
 
77
77
  if (opts.dryRun) {
78
78
  log('\n' + c.dim(message) + '\n');
79
79
  info('dry run — nothing folded or committed');
80
- return { message, folded };
80
+ return { message, folded, epics: touched, committed: false, pushed: false, dryRun: true };
81
81
  }
82
82
 
83
83
  // Stage the fold (modified folded files + deleted shards) under each touched epic's ledger paths.
@@ -90,11 +90,11 @@ export async function runTidy(root, opts = {}) {
90
90
  });
91
91
  const pathspecs = candidates.filter((spec) =>
92
92
  exists(path.join(root, spec)) || git('ls-files', '--error-unmatch', '--', spec).ok);
93
- if (!pathspecs.length) { info('fold produced no stageable change'); return { folded }; }
93
+ if (!pathspecs.length) { info('fold produced no stageable change'); return { message: null, folded, epics: touched, committed: false, pushed: false, dryRun: false }; }
94
94
  const add = git('add', '-A', '--', ...pathspecs);
95
- if (!add.ok) { fail(`git add failed — ${add.stderr.split('\n')[0] || add.code}`); process.exitCode = 1; return { folded }; }
95
+ if (!add.ok) { fail(`git add failed — ${add.stderr.split('\n')[0] || add.code}`); process.exitCode = 1; return { message: null, folded, epics: touched, committed: false, pushed: false, dryRun: false }; }
96
96
  const staged = git('diff', '--cached', '--name-only', '--', ...pathspecs).stdout.split('\n').filter(Boolean);
97
- if (!staged.length) { info('fold produced no net change — nothing to commit'); return { folded }; }
97
+ if (!staged.length) { info('fold produced no net change — nothing to commit'); return { message: null, folded, epics: touched, committed: false, pushed: false, dryRun: false }; }
98
98
 
99
99
  const cm = git('commit', '-m', message, '--', ...staged);
100
100
  if (!cm.ok) {
@@ -103,14 +103,14 @@ export async function runTidy(root, opts = {}) {
103
103
  fail(`git commit failed — ${cm.stderr.split('\n')[0] || cm.code}`);
104
104
  hand('the fold is staged — re-run `yad tidy up`, or `yad checkpoint` to land it');
105
105
  process.exitCode = 1;
106
- return { message, folded };
106
+ return { message, folded, epics: touched, committed: false, pushed: false, dryRun: false };
107
107
  }
108
108
  ok(`tidied ${folded} shard(s) into ${touched.length} epic ledger(s): ${c.dim(label)}`);
109
109
 
110
- if (!opts.push) return { message, folded };
111
- if (pushWithRebase(root, branch).ok) { ok(`pushed to origin/${branch}`); return { message, folded }; }
110
+ if (!opts.push) return { message, folded, epics: touched, committed: true, pushed: false, dryRun: false };
111
+ if (pushWithRebase(root, branch).ok) { ok(`pushed to origin/${branch}`); return { message, folded, epics: touched, committed: true, pushed: true, dryRun: false }; }
112
112
  fail(`could not push to origin/${branch} — a protected branch, or an unresolvable rebase conflict`);
113
113
  hand(`run \`git pull --rebase\` and re-run \`yad tidy up --push\``);
114
114
  process.exitCode = 1;
115
- return { message, folded };
115
+ return { message, folded, epics: touched, committed: true, pushed: false, dryRun: false };
116
116
  }
@@ -1,8 +1,8 @@
1
1
  // `yad update --push` / `yad check --fix --push` — after reconcile applies drift into the working
2
- // trees, commit the applied changes PER REPO (the hub and every connected repo) and push them
2
+ // trees, commit the applied changes PER REPO (the Product and every connected repo) and push them
3
3
  // straight to the default branch, so a package update "just lands" everywhere instead of leaving
4
4
  // dirty trees for someone to hand-commit across N repos. This is the update-flow analogue of the
5
- // back-half `yad checkpoint` (cli/checkpoint.mjs) and reuses its machine-commit machinery.
5
+ // Build `yad checkpoint` (cli/checkpoint.mjs) and reuses its machine-commit machinery.
6
6
  //
7
7
  // Invariants (shared with checkpoint):
8
8
  // 1. Stage an EXPLICIT per-repo allowlist — exactly the paths the reconcile actions declared they
@@ -23,24 +23,24 @@ import fs from 'node:fs';
23
23
  import path from 'node:path';
24
24
  import { c, log, ok, info, warn, fail, hand, run, pushWithRebase } from './lib.mjs';
25
25
  import { VERSION } from './manifest.mjs';
26
- import { hubGit, resolveDefaultBranch } from './hubcommit.mjs';
26
+ import { productGit, resolveDefaultBranch } from './hubcommit.mjs';
27
27
 
28
28
  // Collapse whitespace/newline runs to a single space — keeps a stray path or hostile value from
29
29
  // breaking the one-line subject or injecting a fake trailer line.
30
30
  const oneLine = (s = '') => String(s).replace(/\s+/g, ' ').trim();
31
31
 
32
- // A short, human label for a repo root relative to the hub: 'hub' for the hub itself, else the
32
+ // A short, human label for a repo root relative to the Product: 'hub' for the Product itself, else the
33
33
  // registered path (e.g. demo-repos/backend), else the basename.
34
- export function repoLabel(hubRoot, root) {
35
- if (root === hubRoot) return 'hub';
36
- const r = path.relative(hubRoot, root);
34
+ export function repoLabel(productRoot, root) {
35
+ if (root === productRoot) return 'hub';
36
+ const r = path.relative(productRoot, root);
37
37
  return r && !r.startsWith('..') ? r.split(path.sep).join('/') : path.basename(root);
38
38
  }
39
39
 
40
40
  // PURE — group applied reconcile actions by the repo root they wrote to. Each group carries the
41
41
  // deduped pathspecs to stage plus the human-readable item labels for the commit body. Actions with
42
42
  // no root/paths (e.g. gaps, or a status:'ok' that was force-reapplied without a root) are ignored.
43
- // Order follows first-encounter, so the hub (its module/_bmad/hub actions come first in reconcile)
43
+ // Order follows first-encounter, so the Product (its module and Product actions come first in reconcile)
44
44
  // leads and connected repos follow in registry order.
45
45
  export function groupByRoot(actions = []) {
46
46
  const groups = new Map();
@@ -94,14 +94,14 @@ function stageAllowlist(git, root, paths) {
94
94
  // Commit (and, with push, push) one repo group. `defaultBranch` is the repo's configured default
95
95
  // (hub.default_branch / repo.default_branch); falls back to origin/HEAD then 'main'. Returns a small
96
96
  // result object; never throws. On any hard error it sets process.exitCode so the CLI reports failure.
97
- export function commitAndPush(group, { push = false, allowBranch = false, hubRoot, defaultBranch } = {}) {
97
+ export function commitAndPush(group, { push = false, allowBranch = false, productRoot, defaultBranch } = {}) {
98
98
  const { root, paths, items } = group;
99
- const label = repoLabel(hubRoot ?? root, root);
100
- const git = hubGit(root);
99
+ const label = repoLabel(productRoot ?? root, root);
100
+ const git = productGit(root);
101
101
 
102
102
  // `root` must be the TOP of its OWN git repo — not merely "inside a work tree". A registered repo
103
103
  // whose clone is missing (reconcile's apply() happily recreates the wiring files) but whose path
104
- // sits under the hub would otherwise report inside-work-tree=true against the HUB: git resolves the
104
+ // sits under the Product would otherwise report inside-work-tree=true against the HUB: git resolves the
105
105
  // pathspecs relative to cwd, so we would stage the connected repo's files into the HUB's index and
106
106
  // push them to the HUB's remote, mislabeled. Require the worktree top to BE this root.
107
107
  const top = git('rev-parse', '--show-toplevel');
@@ -110,7 +110,7 @@ export function commitAndPush(group, { push = false, allowBranch = false, hubRoo
110
110
  })();
111
111
  if (!sameRepo) {
112
112
  warn(`${label}: not its own git repo (missing/renamed clone?) — skipped (changes left in the working tree)`);
113
- return { label, committed: false, skipped: true };
113
+ return { label, committed: false, pushed: false, skipped: true, error: false };
114
114
  }
115
115
  const branch = git('rev-parse', '--abbrev-ref', 'HEAD').stdout;
116
116
  const target = defaultBranch || resolveDefaultBranch(git);
@@ -120,13 +120,13 @@ export function commitAndPush(group, { push = false, allowBranch = false, hubRoo
120
120
  warn(`${label}: on '${branch}', not default '${target}' — --allow-branch: commit/push go to origin/${branch}`);
121
121
  } else {
122
122
  warn(`${label}: on '${branch}', not the default branch '${target}' — skipped (switch to '${target}' or pass --allow-branch)`);
123
- return { label, committed: false, skipped: true };
123
+ return { label, committed: false, pushed: false, skipped: true, error: false };
124
124
  }
125
125
  }
126
126
 
127
127
  const { staged, addError } = stageAllowlist(git, root, paths);
128
128
  if (addError) warn(`${label}: git add reported "${addError}" — staging may be incomplete`);
129
- if (!staged.length) { info(`${label}: nothing to commit (unchanged or ignored)`); return { label, committed: false }; }
129
+ if (!staged.length) { info(`${label}: nothing to commit (unchanged or ignored)`); return { label, committed: false, pushed: false, skipped: false, error: false }; }
130
130
 
131
131
  // Pushing HEAD lands any local commits ahead of the remote too. Warn before we add ours so the
132
132
  // operator sees unpublished WIP about to ride the update push (never silently publish it).
@@ -142,27 +142,27 @@ export function commitAndPush(group, { push = false, allowBranch = false, hubRoo
142
142
  git('reset', '-q', '--', ...staged); // don't leave our allowlist staged for an unrelated commit to sweep up
143
143
  fail(`${label}: git commit failed — ${cm.stderr.split('\n')[0] || cm.code}`);
144
144
  process.exitCode = 1;
145
- return { label, committed: false, error: true };
145
+ return { label, committed: false, pushed: false, skipped: false, error: true };
146
146
  }
147
147
  ok(`${label}: committed ${staged.length} file(s)`);
148
148
 
149
- if (!push) return { label, committed: true };
150
- if (pushWithRebase(root, branch).ok) { ok(`${label}: pushed to origin/${branch}`); return { label, committed: true, pushed: true }; }
149
+ if (!push) return { label, committed: true, pushed: false, skipped: false, error: false };
150
+ if (pushWithRebase(root, branch).ok) { ok(`${label}: pushed to origin/${branch}`); return { label, committed: true, pushed: true, skipped: false, error: false }; }
151
151
  // The commit already landed locally — a re-run of `yad update --push` would see no drift and skip
152
152
  // this repo, so point the operator at the direct push of the commit that already exists.
153
153
  fail(`${label}: could not push to origin/${branch} — a protected branch, or an unresolvable rebase conflict`);
154
154
  hand(`resolve it in ${label === 'hub' ? '.' : label}, then push the existing commit with \`git push origin ${branch}\``);
155
155
  process.exitCode = 1;
156
- return { label, committed: true, pushed: false, error: true };
156
+ return { label, committed: true, pushed: false, skipped: false, error: true };
157
157
  }
158
158
 
159
159
  // Orchestrate the per-repo commit/push over the grouped applied actions, bookended by the announce
160
160
  // banners. `defaultBranchFor(root)` yields each repo's configured default branch (undefined ->
161
161
  // resolve from the remote). Prints, commits/pushes each group, then the done banner.
162
- export function commitUpdates(hubRoot, groups, { push = false, allowBranch = false, defaultBranchFor, guardInactiveGitlab } = {}) {
162
+ export function commitUpdates(productRoot, groups, { push = false, allowBranch = false, defaultBranchFor, guardInactiveGitlab } = {}) {
163
163
  if (!groups.length) { info('no committable changes were applied'); return []; }
164
164
 
165
- const labels = groups.map((g) => repoLabel(hubRoot, g.root));
165
+ const labels = groups.map((g) => repoLabel(productRoot, g.root));
166
166
  log('');
167
167
  log(c.bold(`yad update — ${push ? 'publishing to default branches' : 'committing locally (no --push)'}`));
168
168
  warn(`about to ${push ? 'commit + push directly to the default branch' : 'commit'} on: ${labels.join(', ')}`);
@@ -170,7 +170,7 @@ export function commitUpdates(hubRoot, groups, { push = false, allowBranch = fal
170
170
 
171
171
  const results = [];
172
172
  for (const g of groups) {
173
- const r = commitAndPush(g, { push, allowBranch, hubRoot, defaultBranch: defaultBranchFor?.(g.root) });
173
+ const r = commitAndPush(g, { push, allowBranch, productRoot, defaultBranch: defaultBranchFor?.(g.root) });
174
174
  if (push && r.pushed && guardInactiveGitlab?.(g.root)) {
175
175
  hand(`${r.label}: GitLab yad-update-guard is not active — add \`- local: '.gitlab/ci/yad-update-guard.yml'\` to the root .gitlab-ci.yml include (re-run the yad-checks wire step) so this direct-to-default push is gated`);
176
176
  }