session-orchestrator 3.20.0 → 3.21.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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.cursor/rules/030-wave-execution.mdc +10 -8
- package/CHANGELOG.md +414 -0
- package/README.md +16 -11
- package/agents/analyst.md +1 -1
- package/agents/architect-reviewer.md +1 -1
- package/agents/code-implementer.md +4 -2
- package/agents/db-specialist.md +1 -1
- package/agents/dialectic-deriver.md +1 -1
- package/agents/docs-writer.md +1 -1
- package/agents/memory-proposal-collector.md +1 -1
- package/agents/qa-strategist.md +1 -1
- package/agents/security-reviewer.md +1 -1
- package/agents/session-reviewer.md +42 -1
- package/agents/skill-applied-judge.md +1 -1
- package/agents/test-writer.md +1 -1
- package/agents/ui-developer.md +1 -1
- package/agents/ux-evaluator.md +1 -1
- package/commands/release.md +60 -0
- package/docs/migration-v3.md +9 -6
- package/docs/persona-panel.md +3 -1
- package/docs/scope-collision-guard.md +167 -0
- package/hooks/enforce-scope.mjs +103 -3
- package/hooks/hooks-codex.json +1 -1
- package/hooks/hooks.json +11 -1
- package/hooks/on-session-end.mjs +52 -5
- package/hooks/on-stop.mjs +127 -12
- package/hooks/post-bash-write-verify.mjs +8 -32
- package/hooks/pre-task-scope-disjoint.mjs +1042 -0
- package/package.json +1 -1
- package/pi/prompts/release.md +12 -0
- package/scripts/autopilot.mjs +3 -1
- package/scripts/emit-session.mjs +42 -0
- package/scripts/export-hw-learnings.mjs +61 -2
- package/scripts/lib/autopilot/worktree-pipeline.mjs +5 -5
- package/scripts/lib/backlog-scan.mjs +106 -15
- package/scripts/lib/build-live-signals.mjs +7 -3
- package/scripts/lib/ci-status-banner.mjs +207 -23
- package/scripts/lib/git-config-drift.mjs +471 -0
- package/scripts/lib/harness-audit/categories/category6.mjs +65 -12
- package/scripts/lib/io.mjs +193 -7
- package/scripts/lib/learnings/select.mjs +157 -3
- package/scripts/lib/memory-cleanup-stamp.mjs +132 -8
- package/scripts/lib/mirror-issues-banner.mjs +266 -0
- package/scripts/lib/named-vault-resolver.mjs +105 -16
- package/scripts/lib/peer-cards/schema.mjs +6 -2
- package/scripts/lib/reconcile/writer.mjs +69 -30
- package/scripts/lib/redact-spans.mjs +89 -0
- package/scripts/lib/scope-baseline.mjs +77 -17
- package/scripts/lib/scope-gate.mjs +658 -0
- package/scripts/lib/secret-masker.mjs +262 -0
- package/scripts/lib/session-lock.mjs +34 -10
- package/scripts/lib/session-registry.mjs +9 -1
- package/scripts/lib/state-md/mission-status.mjs +21 -12
- package/scripts/lib/tmux-layout/vcs-detector.mjs +108 -4
- package/scripts/lib/validate/check-agents.mjs +77 -5
- package/scripts/lib/validate/check-commands.mjs +2 -20
- package/scripts/lib/validate/check-doc-cli-commands.mjs +514 -0
- package/scripts/lib/validate/check-hooks-symmetry.mjs +18 -0
- package/scripts/lib/validate/check-owner-leakage.mjs +185 -17
- package/scripts/lib/validate/check-skills.mjs +191 -0
- package/scripts/lib/validate/check-test-git-config-target.mjs +665 -0
- package/scripts/lib/validate/check-unicode-safety.mjs +22 -2
- package/scripts/lib/validate/check-untracked-test-deps.mjs +925 -0
- package/scripts/lib/validate/check-unwired-features.mjs +219 -11
- package/scripts/lib/validate/check-vcs-repo-flag.mjs +965 -0
- package/scripts/lib/validate/frontmatter-block.mjs +61 -0
- package/scripts/lib/validate/tier-inference.mjs +46 -8
- package/scripts/lib/vault-mirror/namespace.mjs +146 -1
- package/scripts/lib/vault-mirror/process.mjs +264 -31
- package/scripts/lib/vault-mirror/render-sessions.mjs +115 -4
- package/scripts/lib/vault-status/board-writer.mjs +300 -56
- package/scripts/lib/vault-status/narrative-mirror.mjs +119 -5
- package/scripts/lib/vcs-repo-spec.mjs +500 -19
- package/scripts/print-learnings-index.mjs +30 -3
- package/scripts/release.mjs +616 -61
- package/scripts/site-numbers.mjs +1049 -0
- package/scripts/validate-plugin.mjs +61 -0
- package/scripts/validate-wave-scope.mjs +281 -12
- package/scripts/vault-mirror.mjs +26 -1
- package/skills/_shared/monitor-patterns.md +24 -4
- package/skills/_shared/state-ownership.md +17 -0
- package/skills/claude-md-drift-check/SKILL.md +9 -1
- package/skills/debug/SKILL.md +4 -1
- package/skills/discovery/issue-templates.md +4 -4
- package/skills/discovery/probes-code.md +2 -2
- package/skills/discovery/probes-feature.md +6 -6
- package/skills/discovery/probes-infra.md +2 -2
- package/skills/discovery/probes-session.md +5 -5
- package/skills/dispatcher/SKILL.md +10 -1
- package/skills/frontmatter-guard/SKILL.md +9 -1
- package/skills/gitlab-ops/SKILL.md +49 -39
- package/skills/gitlab-portfolio/SKILL.md +10 -1
- package/skills/memory-cleanup/SKILL.md +18 -5
- package/skills/npm-publish/SKILL.md +22 -50
- package/skills/persona-panel/SKILL.md +3 -1
- package/skills/plan/mode-new.md +23 -5
- package/skills/repo-audit/SKILL.md +10 -1
- package/skills/session-end/SKILL.md +44 -2
- package/skills/session-end/metrics-collection.md +1 -1
- package/skills/session-end/session-metrics-write.md +4 -10
- package/skills/session-plan/SKILL.md +2 -2
- package/skills/session-plan/wave-template.md +1 -1
- package/skills/session-start/SKILL.md +13 -1
- package/skills/spinout/SKILL.md +5 -1
- package/skills/sunset-review/SKILL.md +11 -1
- package/skills/tmux-layout/SKILL.md +7 -2
- package/skills/vault-mirror/SKILL.md +10 -1
- package/skills/vault-sync/SKILL.md +10 -1
- package/skills/vault-sync/validator.mjs +55 -6
- package/skills/wave-executor/wave-loop.md +42 -12
- package/scripts/lib/mission-status-schema.mjs +0 -114
|
@@ -7,12 +7,21 @@
|
|
|
7
7
|
* no-op condition (no VCS, CLI missing, timeout, parse failure).
|
|
8
8
|
*
|
|
9
9
|
* Supports GitLab (via glab) and GitHub (via gh).
|
|
10
|
-
* VCS is auto-detected from
|
|
10
|
+
* VCS is auto-detected from the repo's git remotes via
|
|
11
|
+
* `vcs-repo-spec.mjs`'s `detectVcsFamily` — NOT from a hard-coded `origin`
|
|
12
|
+
* lookup, which left the banner structurally dark in every repo whose remotes
|
|
13
|
+
* are named `gitlab`/`github` (#1039).
|
|
11
14
|
*/
|
|
12
15
|
|
|
13
16
|
import { execFile as _execFile } from 'node:child_process';
|
|
14
17
|
import { promisify } from 'node:util';
|
|
15
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
resolveRepoSpec,
|
|
20
|
+
resolveRepoHost,
|
|
21
|
+
redactUrlCredentials,
|
|
22
|
+
detectVcsFamily,
|
|
23
|
+
isQueryFailure,
|
|
24
|
+
} from './vcs-repo-spec.mjs';
|
|
16
25
|
|
|
17
26
|
const execFileAsync = promisify(_execFile);
|
|
18
27
|
|
|
@@ -38,24 +47,130 @@ async function execWithTimeout(cmd, args, opts = {}) {
|
|
|
38
47
|
}
|
|
39
48
|
|
|
40
49
|
/**
|
|
41
|
-
*
|
|
42
|
-
*
|
|
50
|
+
* The one failure this probe can produce that the frozen
|
|
51
|
+
* `REMOTE_RESOLUTION_REASONS` set has no member for: the async timeout race
|
|
52
|
+
* every subprocess here runs under has no counterpart in the SYNCHRONOUS
|
|
53
|
+
* `vcs-repo-spec.mjs` core (it uses `execFileSync`, which cannot time out).
|
|
54
|
+
*
|
|
55
|
+
* Deliberately NOT a query-failure for warning purposes — a timeout stays
|
|
56
|
+
* SILENT, matching how `checkCiStatus`'s outer catch has always treated one
|
|
57
|
+
* (`msg === 'timeout'` → silent `null`). Warning on a hung `git` but not on a
|
|
58
|
+
* hung `glab` would be an inconsistency inside a single banner, and a hang is
|
|
59
|
+
* not a fact an operator can act on the way "git is not installed" is.
|
|
60
|
+
*/
|
|
61
|
+
const PROBE_TIMEOUT = 'probe-timeout';
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* The ONE `isQueryFailure` member this banner deliberately keeps silent.
|
|
65
|
+
*
|
|
66
|
+
* `vcs-repo-spec.mjs` classifies `not-a-git-repo` as a query failure, and that
|
|
67
|
+
* is correct IN ITS DOMAIN: a remote-resolution helper genuinely could not
|
|
68
|
+
* enumerate remotes. For THIS consumer the same fact is a benign absence —
|
|
69
|
+
* session-start Phase 4 asking a non-repo directory about its CI has a complete
|
|
70
|
+
* and unsurprising answer ("there is no CI here"), and an operator cannot act on
|
|
71
|
+
* being told their directory is not a git repo. Warning would print on every
|
|
72
|
+
* such session-start and train exactly the reflex that makes the
|
|
73
|
+
* `git-unavailable` / `git-error` lines worthless.
|
|
74
|
+
*
|
|
75
|
+
* Expressed as a SUBTRACTION from `isQueryFailure` rather than as a hard-coded
|
|
76
|
+
* warn-list, so a reason added to the frozen set upstream is warned by default
|
|
77
|
+
* (fail-toward-visible) instead of silently inheriting the benign path.
|
|
78
|
+
*
|
|
79
|
+
* @type {ReadonlySet<string>}
|
|
80
|
+
*/
|
|
81
|
+
const SILENT_QUERY_FAILURES = new Set(['not-a-git-repo']);
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Classify a rejected ASYNC `execFile` into a `REMOTE_RESOLUTION_REASONS`
|
|
85
|
+
* member.
|
|
86
|
+
*
|
|
87
|
+
* This cannot reuse `vcs-repo-spec.mjs`'s own (unexported) `classifyGitFailure`
|
|
88
|
+
* because that one reads an `execFileSync` result, where the spawn errno lands
|
|
89
|
+
* in `err.code` and the child's exit status in `err.status`. Async `execFile`
|
|
90
|
+
* folds BOTH into `err.code`, discriminated only by TYPE: the string `'ENOENT'`
|
|
91
|
+
* when the binary is not on PATH, the number `128` when git ran and rejected
|
|
92
|
+
* the path as a work tree. Same taxonomy, different carrier.
|
|
93
|
+
*
|
|
94
|
+
* @param {unknown} err
|
|
95
|
+
* @returns {'git-unavailable'|'not-a-git-repo'|'git-error'}
|
|
96
|
+
*/
|
|
97
|
+
function classifyGitProbeFailure(err) {
|
|
98
|
+
const code = err && typeof err === 'object' ? /** @type {any} */ (err).code : undefined;
|
|
99
|
+
if (code === 'ENOENT') return 'git-unavailable';
|
|
100
|
+
// `git remote -v` exits 128 for "not a git repository" — and, unlike the
|
|
101
|
+
// `remote get-url origin` call this replaced, NOT for "no such remote":
|
|
102
|
+
// a repo with zero remotes exits 0 with empty stdout. That is exactly the
|
|
103
|
+
// split that makes `no-remotes` (benign absence) reportable at all.
|
|
104
|
+
if (code === 128) return 'not-a-git-repo';
|
|
105
|
+
return 'git-error';
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Detect the repo's VCS family from its git remotes.
|
|
110
|
+
*
|
|
111
|
+
* **Never throws.** Returns a discriminated result carrying a reason from
|
|
112
|
+
* `vcs-repo-spec.mjs`'s frozen `REMOTE_RESOLUTION_REASONS` (plus
|
|
113
|
+
* {@link PROBE_TIMEOUT}), so the caller can separate "I could not ask"
|
|
114
|
+
* (`isQueryFailure` → WARN) from "I asked; this repo has no VCS remote"
|
|
115
|
+
* (benign, silent). This is the `mirror-issues-banner.mjs` shape — a third
|
|
116
|
+
* state beyond `value | null` — applied to the last unmigrated path in this
|
|
117
|
+
* module (#1039).
|
|
118
|
+
*
|
|
119
|
+
* What it replaces, and why both halves were defects:
|
|
120
|
+
* 1. `git remote get-url origin` — a hard-coded remote NAME. In a repo whose
|
|
121
|
+
* remotes are `gitlab` + `github` (no `origin`) the call failed, the
|
|
122
|
+
* caller swallowed it to `null`, and the banner was structurally dark:
|
|
123
|
+
* it could never report, not even on a red pipeline.
|
|
124
|
+
* 2. `remoteUrl.includes('github.com') ? 'github' : 'gitlab'` — a substring
|
|
125
|
+
* test that classifies GitHub Enterprise (`git@github.example.com:o/r`)
|
|
126
|
+
* as gitlab and then drives `glab` at a GitHub instance.
|
|
127
|
+
* `detectVcsFamily` fixes both: preference-ordered remote selection, and
|
|
128
|
+
* host-prefix classification (`github.*` / `gitlab.*`) ahead of remote-name.
|
|
129
|
+
*
|
|
130
|
+
* The `git remote -v` spawn stays HERE rather than inside `detectVcsFamily`
|
|
131
|
+
* because this module owes every subprocess a timeout race — the shared core
|
|
132
|
+
* is synchronous by design. So the output is captured once and handed to the
|
|
133
|
+
* shared classifier through its `gitRun` seam: one spawn, one taxonomy, no
|
|
134
|
+
* second copy of the parsing or the credential strip.
|
|
43
135
|
*
|
|
44
136
|
* @param {string} repoRoot
|
|
45
137
|
* @param {{ execFile?: Function, timeoutMs?: number }} deps
|
|
46
|
-
* @returns {Promise<'github'|'gitlab'
|
|
138
|
+
* @returns {Promise<{ ok: true, vcs: 'github'|'gitlab' }
|
|
139
|
+
* | { ok: false, reason: string, stderr?: string }>}
|
|
47
140
|
*/
|
|
48
141
|
async function detectVcs(repoRoot, deps = {}) {
|
|
49
142
|
// Use the smaller of 2000ms or the caller-supplied timeout so that a short
|
|
50
143
|
// test-level timeout is still respected here.
|
|
51
144
|
const gitTimeout = Math.min(2000, deps.timeoutMs ?? 2000);
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
145
|
+
|
|
146
|
+
let stdout;
|
|
147
|
+
try {
|
|
148
|
+
const result = await execWithTimeout(
|
|
149
|
+
'git',
|
|
150
|
+
['remote', '-v'],
|
|
151
|
+
{ cwd: repoRoot, timeoutMs: gitTimeout, execFile: deps.execFile },
|
|
152
|
+
);
|
|
153
|
+
stdout = String(result?.stdout ?? '');
|
|
154
|
+
} catch (err) {
|
|
155
|
+
const msg = err instanceof Error ? err.message : String(err ?? '');
|
|
156
|
+
if (msg === 'timeout') return { ok: false, reason: PROBE_TIMEOUT };
|
|
157
|
+
const stderr = err && typeof err === 'object' ? String(/** @type {any} */ (err).stderr ?? '') : '';
|
|
158
|
+
return { ok: false, reason: classifyGitProbeFailure(err), stderr };
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Replay the captured output through the shared core's `gitRun` seam. The
|
|
162
|
+
// arg guard is insurance, not decoration: `detectVcsFamily` asks only for
|
|
163
|
+
// `remote -v` today, and if it ever grows a second query the guard turns a
|
|
164
|
+
// silently-wrong answer (built from the WRONG command's output) into a
|
|
165
|
+
// reported `git-error`.
|
|
166
|
+
const replayGitRun = (args) =>
|
|
167
|
+
Array.isArray(args) && args.at(-2) === 'remote' && args.at(-1) === '-v'
|
|
168
|
+
? { ok: true, stdout, stderr: '', status: 0 }
|
|
169
|
+
: { ok: false, stdout: '', stderr: `unsupported git query in ci-status probe: ${String(args)}`, status: 1 };
|
|
170
|
+
|
|
171
|
+
const family = detectVcsFamily({ repoRoot, gitRun: replayGitRun });
|
|
172
|
+
if (!family.ok) return { ok: false, reason: family.reason, stderr: family.stderr };
|
|
173
|
+
return { ok: true, vcs: family.vcs };
|
|
59
174
|
}
|
|
60
175
|
|
|
61
176
|
/**
|
|
@@ -308,9 +423,25 @@ async function checkGitlab(repoRoot, now, deps = {}) {
|
|
|
308
423
|
/**
|
|
309
424
|
* GitHub CI status check (v1 — red/green only; lastGreen not implemented).
|
|
310
425
|
*
|
|
311
|
-
* #872: pins the `gh repo view` lookup to `deps.repoSpec`
|
|
312
|
-
*
|
|
313
|
-
*
|
|
426
|
+
* #872/#1022: pins the `gh repo view` lookup to `deps.repoSpec` when resolved
|
|
427
|
+
* (host-pinning — `gh repo view` otherwise falls back to the ambient
|
|
428
|
+
* `GH_HOST`). The spec is passed POSITIONALLY, not via `-R`: `gh repo view`
|
|
429
|
+
* takes `[<repository>]` as a positional argument and has no `-R`/`--repo`
|
|
430
|
+
* flag at all, so `-R` made gh exit 1 with `unknown shorthand flag: 'R'` —
|
|
431
|
+
* an error `checkCiStatus`'s outer catch swallowed to `null`, leaving the
|
|
432
|
+
* Phase 4 banner silently dead on EVERY GitHub repo (#1022). The
|
|
433
|
+
* `[HOST/]OWNER/REPO` shape `resolveRepoSpec({ vcs: 'github' })` returns is
|
|
434
|
+
* exactly the positional's documented input format.
|
|
435
|
+
*
|
|
436
|
+
* The asymmetry with `getGlabProjectId` is real and deliberate: `glab repo
|
|
437
|
+
* view` DOES accept `-R`, and `gh api`/`glab api` accept neither `-R` nor a
|
|
438
|
+
* positional — only `--hostname`. Do not unify these three call sites.
|
|
439
|
+
*
|
|
440
|
+
* `nameWithOwner` is NOT derivable from `deps.repoSpec`, so this lookup
|
|
441
|
+
* cannot be dropped: the spec carries a HOST prefix the `repos/<owner>/<repo>`
|
|
442
|
+
* API path must not contain, it is `undefined` whenever no remote resolves
|
|
443
|
+
* (cross-family guard, unsafe-argv guard), and `normalizeGithubSpec` falls
|
|
444
|
+
* back to the raw URL on an unrecognised remote shape.
|
|
314
445
|
*
|
|
315
446
|
* @param {string} repoRoot
|
|
316
447
|
* @param {{ execFile?: Function, timeoutMs?: number, repoSpec?: string, repoHost?: string }} deps
|
|
@@ -318,8 +449,9 @@ async function checkGitlab(repoRoot, now, deps = {}) {
|
|
|
318
449
|
*/
|
|
319
450
|
async function checkGithub(repoRoot, deps = {}) {
|
|
320
451
|
// Resolve owner/repo from gh to keep the API path generic.
|
|
321
|
-
const repoViewArgs = ['repo', 'view'
|
|
322
|
-
if (deps.repoSpec) repoViewArgs.push(
|
|
452
|
+
const repoViewArgs = ['repo', 'view'];
|
|
453
|
+
if (deps.repoSpec) repoViewArgs.push(deps.repoSpec);
|
|
454
|
+
repoViewArgs.push('--json', 'nameWithOwner');
|
|
323
455
|
const repoViewResult = await execWithTimeout(
|
|
324
456
|
'gh',
|
|
325
457
|
repoViewArgs,
|
|
@@ -387,11 +519,17 @@ async function checkGithub(repoRoot, deps = {}) {
|
|
|
387
519
|
* Checks CI status for the current HEAD commit.
|
|
388
520
|
*
|
|
389
521
|
* Returns `null` (silent no-op) when:
|
|
390
|
-
* -
|
|
522
|
+
* - The repo has no usable VCS remote (not a git repo, no remotes at all,
|
|
523
|
+
* or >= 2 remotes with no preference match) — a benign, measured absence
|
|
391
524
|
* - Required CLI (glab / gh) not in PATH
|
|
392
525
|
* - Any CLI invocation times out
|
|
393
526
|
* - JSON parse failure on CLI output
|
|
394
527
|
*
|
|
528
|
+
* Also returns `null`, but with a `console.warn` trace, when the VCS-detection
|
|
529
|
+
* QUERY ITSELF failed (`git` not on PATH, `git remote -v` erroring) or when a
|
|
530
|
+
* present CLI rejected its invocation. `null` alone cannot express "could not
|
|
531
|
+
* read" — see the outer catch and Step 1 for why the warn channel carries it.
|
|
532
|
+
*
|
|
395
533
|
* @param {{
|
|
396
534
|
* repoRoot?: string,
|
|
397
535
|
* vcs?: 'gitlab'|'github',
|
|
@@ -441,12 +579,30 @@ export async function checkCiStatus(opts = {}, deps = {}) {
|
|
|
441
579
|
// Step 1: detect VCS (or use forced value).
|
|
442
580
|
let vcs = forcedVcs;
|
|
443
581
|
if (!vcs) {
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
//
|
|
582
|
+
const detected = await detectVcs(repoRoot, depsWithExec);
|
|
583
|
+
if (!detected.ok) {
|
|
584
|
+
// The two-state `null` return is fixed by 13 sibling banners, so the
|
|
585
|
+
// third state lives in the WARN channel: an ABSENCE (`no-remotes`,
|
|
586
|
+
// `no-matching-remote`, `unsafe-value`) is a real, benign answer and
|
|
587
|
+
// stays silent — warning there would train operators to ignore this
|
|
588
|
+
// line, which is the more expensive error. A QUERY FAILURE
|
|
589
|
+
// (`git-unavailable`, `git-error`) means the question could not be
|
|
590
|
+
// asked at all, and that is precisely the state that was
|
|
591
|
+
// indistinguishable from "nothing to report" before #1039.
|
|
592
|
+
// `not-a-git-repo` is the one query failure this banner reads as an
|
|
593
|
+
// absence — see {@link SILENT_QUERY_FAILURES}.
|
|
594
|
+
if (isQueryFailure(detected.reason) && !SILENT_QUERY_FAILURES.has(detected.reason)) {
|
|
595
|
+
const detail = detected.stderr
|
|
596
|
+
? ` — ${redactUrlCredentials(detected.stderr).trim()}`
|
|
597
|
+
: '';
|
|
598
|
+
console.warn(
|
|
599
|
+
`WARN ci-status-banner: VCS detection failed (${detected.reason}), banner suppressed — ` +
|
|
600
|
+
`CI state is UNKNOWN, not "green".${detail}`,
|
|
601
|
+
);
|
|
602
|
+
}
|
|
448
603
|
return null;
|
|
449
604
|
}
|
|
605
|
+
vcs = detected.vcs;
|
|
450
606
|
}
|
|
451
607
|
|
|
452
608
|
// Step 1b (#872): resolve the -R/--hostname host-pinning spec ONCE per
|
|
@@ -482,7 +638,35 @@ export async function checkCiStatus(opts = {}, deps = {}) {
|
|
|
482
638
|
return null;
|
|
483
639
|
}
|
|
484
640
|
|
|
485
|
-
//
|
|
641
|
+
// Everything else means the CLI was PRESENT but the invocation failed —
|
|
642
|
+
// non-zero exit, rejected flag, unparseable output. Returning a bare null
|
|
643
|
+
// here makes that state indistinguishable from "CLI not installed", which
|
|
644
|
+
// is exactly how #1022 (`gh repo view -R` → `unknown shorthand flag: 'R'`)
|
|
645
|
+
// stayed invisible on every GitHub repo. THIS module keeps the two-state
|
|
646
|
+
// contract (null ⇒ silent no-op) for backwards compatibility with the
|
|
647
|
+
// Phase-4 callers already written against it, so leave a stderr trace
|
|
648
|
+
// instead: non-blocking, but the next defect of this class is no longer
|
|
649
|
+
// silent. Credentials redacted defense-in-depth (#907) — the message can
|
|
650
|
+
// quote the failed argv, which carries the repo spec.
|
|
651
|
+
//
|
|
652
|
+
// DIRECTION FOR NEW BANNERS — do not copy this shape. The
|
|
653
|
+
// absence-preserving form is `scripts/lib/mirror-issues-banner.mjs`: a
|
|
654
|
+
// THIRD return state `{ severity, message, degraded }` whose `degraded`
|
|
655
|
+
// is a member of the closed `DEGRADED_REASONS` enum exported there, so
|
|
656
|
+
// "could not read" stays distinguishable from "read, and clean".
|
|
657
|
+
//
|
|
658
|
+
// The REMOTE probe (`detectVcs`) has since been pulled onto that shape
|
|
659
|
+
// (#1039): it returns a reason from the frozen `REMOTE_RESOLUTION_REASONS`
|
|
660
|
+
// set and Step 1 branches on `isQueryFailure` — absence silent, query
|
|
661
|
+
// failure warned. This outer catch is what remains unmigrated: it still
|
|
662
|
+
// collapses every CLI-side failure onto `null`, which a caller reads as
|
|
663
|
+
// all-clear, and it cannot be widened without changing a return contract
|
|
664
|
+
// 13 sibling banners share. Same verdict, stated caller-side, in
|
|
665
|
+
// `skills/session-start/SKILL.md` § Phase 4 (the mirror-issues
|
|
666
|
+
// paragraph): "Do not reproduce it."
|
|
667
|
+
console.warn(
|
|
668
|
+
`WARN ci-status-banner: CI status check failed, banner suppressed — ${redactUrlCredentials(msg)}`,
|
|
669
|
+
);
|
|
486
670
|
return null;
|
|
487
671
|
}
|
|
488
672
|
}
|