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
package/cli/openpr.mjs
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import path from 'node:path';
|
|
6
6
|
import fs from 'node:fs';
|
|
7
7
|
import { c, log, ok, info, warn, hand, fail, run, exists, readJSON } from './lib.mjs';
|
|
8
|
-
import { PROJECT_FILES , productConfigPath } from './manifest.mjs';
|
|
8
|
+
import { PROJECT_FILES , productConfigPath, settingsEditHint } from './manifest.mjs';
|
|
9
9
|
import {
|
|
10
10
|
detectPlatform, createPr, platformLogin, resolveBaseBranch,
|
|
11
11
|
} from './platform.mjs';
|
|
@@ -17,35 +17,43 @@ import { baseChangeLevel, changedSince, recentAuthorsFor, suggestedAuthorsFor }
|
|
|
17
17
|
import { personLabel } from './riskmap.mjs';
|
|
18
18
|
import { gateOpen } from './gate.mjs';
|
|
19
19
|
import { unfoldedPaths, authorStepOf } from './fold.mjs';
|
|
20
|
+
import { registryEntryFor } from './find-product.mjs';
|
|
20
21
|
|
|
21
|
-
// Resolve the target code repo: --repo <name> from the registry, else --dir, else cwd.
|
|
22
|
-
|
|
22
|
+
// Resolve the target code repo: --repo <name> from the registry, else --dir, else cwd. `product` (E80) is
|
|
23
|
+
// the Product found from a code repo beside it: the registry is read there, and a repo run without
|
|
24
|
+
// --repo is looked up in it by its path — so its platform and default branch are the recorded ones.
|
|
25
|
+
function resolveRepo(root, { repo, dir, product }) {
|
|
26
|
+
const productRoot = product || root;
|
|
23
27
|
if (repo) {
|
|
24
|
-
const reg = readJSON(path.join(
|
|
25
|
-
const found = reg.repos.find((r) => r.name === repo);
|
|
26
|
-
if (found) return { repoRoot: path.resolve(
|
|
28
|
+
const reg = readJSON(path.join(productRoot, PROJECT_FILES.reposRegistry), { repos: [] });
|
|
29
|
+
const found = (reg.repos || []).find((r) => r.name === repo);
|
|
30
|
+
if (found) return { repoRoot: path.resolve(productRoot, found.path), meta: found };
|
|
27
31
|
}
|
|
28
|
-
|
|
32
|
+
const here = path.resolve(root, dir || '.');
|
|
33
|
+
// The checkout it runs in, when that checkout is a registered repo: worked at its top, from any
|
|
34
|
+
// folder inside it. Anything else (a worktree, a nested repo) stays exactly where it is.
|
|
35
|
+
const hit = registryEntryFor(product, here);
|
|
36
|
+
return { repoRoot: hit ? hit.top : here, meta: hit ? hit.meta : null };
|
|
29
37
|
}
|
|
30
38
|
|
|
31
39
|
// Which SDLC stage is this PR? The Product serves two vehicles; a code repo only one. Mirrors the
|
|
32
40
|
// `--head` split the Product pattern gates (pr-title.sh/pr-template.sh) already apply:
|
|
33
41
|
// code-repo — NOT the Product (a registry repo via --repo, or root is not a Product).
|
|
34
|
-
//
|
|
35
|
-
//
|
|
42
|
+
// product-shape — the Product itself AND head is a review/EP-* branch (artifact-review PR).
|
|
43
|
+
// product-tooling — the Product itself AND head is anything else (a tooling/CI change to the Product).
|
|
36
44
|
// `meta` (truthy when resolved from the repos registry via --repo) is a connected code repo, so it is
|
|
37
45
|
// never the Product regardless of its path. Otherwise "is the Product" = repoRoot resolves to root AND root
|
|
38
46
|
// carries .sdlc/hub.json. path.resolve normalises `--dir .` / trailing slashes.
|
|
39
47
|
export function detectStage(root, repoRoot, head, meta) {
|
|
40
48
|
if (meta) return 'code-repo';
|
|
41
|
-
const
|
|
49
|
+
const isProduct = path.resolve(repoRoot) === path.resolve(root)
|
|
42
50
|
&& exists(productConfigPath(root));
|
|
43
|
-
if (!
|
|
44
|
-
return /^review\/EP-[a-z0-9-]+\//.test(head || '') ? '
|
|
51
|
+
if (!isProduct) return 'code-repo';
|
|
52
|
+
return /^review\/EP-[a-z0-9-]+\//.test(head || '') ? 'product-shape' : 'product-tooling';
|
|
45
53
|
}
|
|
46
54
|
|
|
47
55
|
// The bundled code-task template — the same file `REPO_WIRING` installs into code repos, resolved
|
|
48
|
-
// from the package (mirrors how manifest.mjs reads ../package.json). Used for a
|
|
56
|
+
// from the package (mirrors how manifest.mjs reads ../package.json). Used for a product-tooling PR, whose
|
|
49
57
|
// `.github/pull_request_template.md` is the ARTIFACT-REVIEW template (wrong shape for the code-task
|
|
50
58
|
// Product gate). Falls back to a minimal body that still carries every section the gate requires.
|
|
51
59
|
function codeTaskTemplate(platform) {
|
|
@@ -70,10 +78,10 @@ function codeTaskTemplate(platform) {
|
|
|
70
78
|
}
|
|
71
79
|
|
|
72
80
|
export function templateBody(repoRoot, platform, { task, summary, risk, contract, domains, stage }) {
|
|
73
|
-
//
|
|
81
|
+
// product-tooling: the Product's own template is artifact-review — use the bundled code-task template so the
|
|
74
82
|
// body matches the shape the Product `pr-template` gate demands for a non-review head.
|
|
75
83
|
let base;
|
|
76
|
-
if (stage === '
|
|
84
|
+
if (stage === 'product-tooling') {
|
|
77
85
|
base = codeTaskTemplate(platform);
|
|
78
86
|
} else {
|
|
79
87
|
const tplPath = platform === 'gitlab'
|
|
@@ -241,11 +249,11 @@ export async function runOpenPr(root, opts = {}) {
|
|
|
241
249
|
const branch = run('git', ['rev-parse', '--abbrev-ref', 'HEAD'], { cwd: repoRoot }).stdout;
|
|
242
250
|
const stage = detectStage(root, repoRoot, branch, meta);
|
|
243
251
|
|
|
244
|
-
//
|
|
252
|
+
// product-shape: this is a Shape artifact-review PR (review/EP-*/<artifact> head on the Product). The
|
|
245
253
|
// artifact-review title, body, and ledger bookkeeping all live in `yad gate open` — delegate to it
|
|
246
254
|
// rather than emit the code-task shape (which the Product gate would reject). Push first (gateOpen does
|
|
247
255
|
// not push), then hand off; any --title/--message is dropped (gateOpen sets `review: …`).
|
|
248
|
-
if (stage === '
|
|
256
|
+
if (stage === 'product-shape') {
|
|
249
257
|
const parsed = parseReviewBranch(branch);
|
|
250
258
|
if (!parsed) { fail(`could not parse review branch '${branch}' (expected review/EP-<slug>/<artifact>)`); process.exitCode = 1; return; }
|
|
251
259
|
// E44: the review carries what was FOLDED. A step file changed since then is not on this branch.
|
|
@@ -268,17 +276,17 @@ export async function runOpenPr(root, opts = {}) {
|
|
|
268
276
|
}
|
|
269
277
|
|
|
270
278
|
// The Product's default_branch, which only applies when the PR targets the Product ITSELF
|
|
271
|
-
// (a
|
|
272
|
-
// must never leak in. Resolved AFTER the
|
|
279
|
+
// (a product-tooling branch) — for a connected code repo the Product's trunk belongs to a different repo and
|
|
280
|
+
// must never leak in. Resolved AFTER the product-shape hand-off above, which delegates its own base to
|
|
273
281
|
// `yad gate open`: resolving before it would spend a platform round-trip and print a base that the
|
|
274
282
|
// delegated path then ignores.
|
|
275
|
-
const
|
|
283
|
+
const productConfig = readJSON(productConfigPath(root), {});
|
|
276
284
|
|
|
277
285
|
// Resolve the base rather than assume it (#168). Hardcoding 'main' mis-based every PR on a repo
|
|
278
286
|
// whose trunk is something else — and CodeRabbit decides auto-review eligibility from the base at
|
|
279
287
|
// PR-OPEN time, so those PRs silently got no AI first pass at all.
|
|
280
288
|
const { base: baseBranch, source: baseSource, platformDefault } = resolveBaseBranch(platform, {
|
|
281
|
-
cwd: repoRoot, explicit: opts.base, meta,
|
|
289
|
+
cwd: repoRoot, explicit: opts.base, meta, productConfig: stage === 'code-repo' ? null : productConfig, runner: opts.runner,
|
|
282
290
|
});
|
|
283
291
|
if (branch === baseBranch) { fail(`on ${baseBranch} — switch to your task branch first`); process.exitCode = 1; return; }
|
|
284
292
|
info(`base ${baseBranch} ${c.dim(`(from ${baseSource})`)}`);
|
|
@@ -288,8 +296,8 @@ export async function runOpenPr(root, opts = {}) {
|
|
|
288
296
|
// would mean contradicting their own committed config on every PR, forever.
|
|
289
297
|
if (platformDefault && platformDefault !== baseBranch) {
|
|
290
298
|
warn(`base '${baseBranch}' is not the repo default '${platformDefault}' — CodeRabbit skips auto-review on a non-default base unless .coderabbit.yaml lists it under reviews.base_branches, and retargeting later does NOT undo the skip`);
|
|
291
|
-
if (baseSource === 'registry' || baseSource === '
|
|
292
|
-
hand(`the configured default_branch (${baseSource === '
|
|
299
|
+
if (baseSource === 'registry' || baseSource === 'product') {
|
|
300
|
+
hand(`the configured default_branch (${baseSource === 'product' ? settingsEditHint(root) : '.sdlc/repos.json'}) disagrees with the platform — reconcile them, or allow '${baseBranch}' in .coderabbit.yaml`);
|
|
293
301
|
} else {
|
|
294
302
|
hand(`open against '${platformDefault}' (or pass --base ${platformDefault}) unless you meant to stack this PR`);
|
|
295
303
|
}
|
|
@@ -342,7 +350,7 @@ export async function runOpenPr(root, opts = {}) {
|
|
|
342
350
|
// feeding the number E72 caps with. `null` is the honest answer there: we are not standing in a
|
|
343
351
|
// Product, so we did not count.
|
|
344
352
|
const active = (() => {
|
|
345
|
-
// Only the `code-repo` stage prints it (`routeCount`, below). On a `
|
|
353
|
+
// Only the `code-repo` stage prints it (`routeCount`, below). On a `product-tooling` PR this would
|
|
346
354
|
// otherwise walk the Product and every connected repo's history and throw the answer away.
|
|
347
355
|
if (stage !== 'code-repo') return null;
|
|
348
356
|
if (!exists(productConfigPath(root))) return null;
|
package/cli/people.mjs
CHANGED
|
@@ -34,7 +34,8 @@ import fs from 'node:fs';
|
|
|
34
34
|
import path from 'node:path';
|
|
35
35
|
import { spawnSync } from 'node:child_process';
|
|
36
36
|
|
|
37
|
-
import { c, info, readJSONStrict, warn } from './lib.mjs';
|
|
37
|
+
import { c, info, readJSONStrict, warn, forTerminal, asArg } from './lib.mjs';
|
|
38
|
+
import { judgeRepo, runnable } from './workspace.mjs';
|
|
38
39
|
import { PROJECT_FILES, epicFiles } from './manifest.mjs';
|
|
39
40
|
import { epicIds, epicRoot, unlistedLedgerDirs, FOUNDATION_EPIC, FOUNDATION_DIR, ledgerPersonLogin, legacyLogins, capLimit, capSeat, smallestTeam } from './epic-state.mjs';
|
|
40
41
|
import { corruptShards, readShips } from './ledger.mjs';
|
|
@@ -312,10 +313,12 @@ export function gitHas(repoRoot, sha) {
|
|
|
312
313
|
return r.status === 0;
|
|
313
314
|
}
|
|
314
315
|
|
|
315
|
-
function gitAuthors(
|
|
316
|
-
const git = (args) => spawnSync('git', args, { cwd:
|
|
316
|
+
function gitAuthors(dir, since) {
|
|
317
|
+
const git = (args) => spawnSync('git', args, { cwd: dir, encoding: 'utf8', maxBuffer: 1 << 30 });
|
|
318
|
+
// Every reason below is printed: the folder is built from the shared registry (E81 review 15).
|
|
319
|
+
const repoRoot = forTerminal(dir);
|
|
317
320
|
if (since === null) return { unknown: 'the window has no readable start date' };
|
|
318
|
-
if (!fs.existsSync(
|
|
321
|
+
if (!fs.existsSync(dir)) return { unknown: `${repoRoot} is not on disk` };
|
|
319
322
|
if (git(['rev-parse', '--is-inside-work-tree']).status !== 0) return { unknown: `${repoRoot} is not a git repo` };
|
|
320
323
|
// A shallow clone holds only the newest commits. E67's rule, and the reason is even stronger here:
|
|
321
324
|
// reading a truncated history as "these are all the people" is exactly the quiet under-count.
|
|
@@ -374,8 +377,17 @@ function connectedRepos(root) {
|
|
|
374
377
|
if (!r || typeof r.name !== 'string' || !r.name) return { unknown: `${PROJECT_FILES.reposRegistry} holds a repo with no name` };
|
|
375
378
|
// A repo with no path is registered but not on this machine. It is NOT "zero people": we cannot
|
|
376
379
|
// read it, so the count is unknown rather than smaller.
|
|
377
|
-
if (typeof r.path !== 'string' || !r.path) return { unknown: `repo '${r.name}' has no local path — its history cannot be counted here` };
|
|
378
|
-
|
|
380
|
+
if (typeof r.path !== 'string' || !r.path) return { unknown: `repo '${forTerminal(r.name)}' has no local path — its history cannot be counted here` };
|
|
381
|
+
// Judged before git runs (E81 review 15), as every command that runs git in a registered path is: git
|
|
382
|
+
// run in a folder the Product commits, shaped as a repo, reads its `config`, which can run a
|
|
383
|
+
// command (`gpg.program` with `log.showSignature`). A refused entry is not "zero people" either —
|
|
384
|
+
// it is an unknown, said with the reason, never a silent skip.
|
|
385
|
+
const name = forTerminal(r.name);
|
|
386
|
+
const j = judgeRepo(root, r);
|
|
387
|
+
if (!runnable(j)) return { unknown: `repo '${name}': ${j.state === 'missing' ? 'not cloned on this machine' : j.linked ? 'reached through a link inside a repo\'s tree' : j.reason} — its history cannot be counted here` };
|
|
388
|
+
// A commit id only: `git cat-file -e <syncedHead>^{commit}` must never see anything else.
|
|
389
|
+
const syncedHead = typeof r.syncedHead === 'string' && /^[0-9a-f]{40,64}$/i.test(r.syncedHead) ? r.syncedHead : null;
|
|
390
|
+
repos.push({ name, arg: asArg(r.name), root: path.resolve(root, r.path), syncedHead });
|
|
379
391
|
}
|
|
380
392
|
return { repos };
|
|
381
393
|
}
|
|
@@ -406,7 +418,7 @@ export function peopleEvidence(root, { today = todayString(), aliases = new Map(
|
|
|
406
418
|
// error rather than throwing, so `gitHas` would answer false for a repo that is simply not on
|
|
407
419
|
// this machine and send the reader off to fix the wrong thing.
|
|
408
420
|
if (fs.existsSync(repo.root) && repo.syncedHead && !gitHas(repo.root, repo.syncedHead)) {
|
|
409
|
-
unknown.push(`repo '${repo.name}': this clone does not hold the commit the registry last packed — it is behind, and a behind clone shows fewer people; fetch it, or re-pack it with \`yad repo refresh ${repo.
|
|
421
|
+
unknown.push(`repo '${repo.name}': this clone does not hold the commit the registry last packed — it is behind, and a behind clone shows fewer people; fetch it, or re-pack it with \`yad repo refresh ${repo.arg}\``);
|
|
410
422
|
continue;
|
|
411
423
|
}
|
|
412
424
|
const got = gitAuthors(repo.root, since);
|
|
@@ -611,10 +623,10 @@ export function teamHint(counted) {
|
|
|
611
623
|
// The ONE wiring every surface uses (`yad mode`, `gate status`, `yad next`, `yad doctor`): null outside
|
|
612
624
|
// solo mode, where no count is read; otherwise `teamHint` over the count the caller already read, or a
|
|
613
625
|
// fresh one with the roster aliases (without them an older roster-shaped approval reads as a second
|
|
614
|
-
// person). `solo` is the caller's `isSolo(
|
|
615
|
-
export function soloTeamHint(root,
|
|
626
|
+
// person). `solo` is the caller's `isSolo(productConfig)` — it lives in cli/gate.mjs, which imports this file.
|
|
627
|
+
export function soloTeamHint(root, productConfig, { solo, headCount = null, today = null } = {}) {
|
|
616
628
|
if (!solo) return null;
|
|
617
|
-
return teamHint(headCount || activePeople(root, { today: today || undefined, aliases: legacyLogins(
|
|
629
|
+
return teamHint(headCount || activePeople(root, { today: today || undefined, aliases: legacyLogins(productConfig) }));
|
|
618
630
|
}
|
|
619
631
|
|
|
620
632
|
// The ONE way a suggestion is printed on a text surface: a known one as a warning line (`! …`), an
|