@deftai/directive-core 0.108.0 → 0.109.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/dist/cache/operations.js +1 -1
- package/dist/check/gate-lists.d.ts +14 -0
- package/dist/check/gate-lists.js +26 -3
- package/dist/check/session-completed-ac.d.ts +1 -1
- package/dist/check/session-completed-ac.js +1 -1
- package/dist/consumer-check-contract/evaluate.d.ts +47 -0
- package/dist/consumer-check-contract/evaluate.js +181 -15
- package/dist/delivery-attempt/evaluate.d.ts +9 -1
- package/dist/delivery-attempt/evaluate.js +69 -0
- package/dist/delivery-attempt/index.d.ts +1 -1
- package/dist/delivery-attempt/index.js +1 -1
- package/dist/deposit/live-procedure-exclusions.d.ts +18 -0
- package/dist/deposit/live-procedure-exclusions.js +110 -0
- package/dist/deposit/live-procedure-targets.d.ts +45 -0
- package/dist/deposit/live-procedure-targets.js +274 -0
- package/dist/deposit/python-free.d.ts +6 -0
- package/dist/deposit/python-free.js +15 -0
- package/dist/deposit/rewrite-deposit-links.d.ts +42 -0
- package/dist/deposit/rewrite-deposit-links.js +148 -0
- package/dist/deposit/run-stage-content-pack.d.ts +2 -0
- package/dist/deposit/run-stage-content-pack.js +3 -0
- package/dist/deposit/stage-content-pack.d.ts +17 -0
- package/dist/deposit/stage-content-pack.js +91 -0
- package/dist/design-critique/citation-grammar.d.ts +7 -0
- package/dist/design-critique/citation-grammar.js +1 -1
- package/dist/design-critique/completed-arc-record.d.ts +1 -1
- package/dist/design-critique/completed-arc-record.js +59 -14
- package/dist/hooks/classify/host-session-identity.d.ts +24 -11
- package/dist/hooks/classify/host-session-identity.js +46 -49
- package/dist/hooks/classify/index.d.ts +2 -2
- package/dist/hooks/classify/index.js +2 -2
- package/dist/hooks/classify/paths.d.ts +2 -0
- package/dist/hooks/classify/paths.js +8 -4
- package/dist/hooks/classify/stdin.js +69 -3
- package/dist/hooks/dest-form.d.ts +20 -1
- package/dist/hooks/dest-form.js +158 -21
- package/dist/hooks/dispatcher.d.ts +6 -2
- package/dist/hooks/dispatcher.js +258 -62
- package/dist/hooks/fixtures/cases.d.ts +6 -0
- package/dist/hooks/fixtures/cases.js +53 -0
- package/dist/hooks/git-destructive-log.d.ts +32 -0
- package/dist/hooks/git-destructive-log.js +46 -0
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/index.js +2 -0
- package/dist/hooks/shell-write-targets.d.ts +10 -0
- package/dist/hooks/shell-write-targets.js +274 -0
- package/dist/hooks/tools.d.ts +20 -2
- package/dist/hooks/tools.js +30 -1
- package/dist/init-deposit/gitignore.d.ts +7 -0
- package/dist/init-deposit/gitignore.js +24 -0
- package/dist/init-deposit/init-deposit.js +3 -0
- package/dist/init-deposit/refresh.js +7 -0
- package/dist/init-deposit/runtime-writers.d.ts +14 -0
- package/dist/init-deposit/runtime-writers.js +33 -0
- package/dist/intake/issue-ingest.d.ts +6 -1
- package/dist/intake/issue-ingest.js +21 -2
- package/dist/intake/reconcile-issues.js +13 -13
- package/dist/lifecycle/brief-envelope.d.ts +25 -0
- package/dist/lifecycle/brief-envelope.js +42 -0
- package/dist/lifecycle/index.d.ts +1 -0
- package/dist/lifecycle/index.js +1 -0
- package/dist/literal-acceptance/evaluate.js +14 -5
- package/dist/literal-acceptance/index.d.ts +1 -1
- package/dist/literal-acceptance/index.js +1 -1
- package/dist/literal-acceptance/run.d.ts +2 -0
- package/dist/literal-acceptance/run.js +19 -1
- package/dist/orphan-active/candidate-scope.d.ts +53 -0
- package/dist/orphan-active/candidate-scope.js +157 -0
- package/dist/orphan-active/evaluate.d.ts +27 -0
- package/dist/orphan-active/evaluate.js +63 -6
- package/dist/orphan-active/index.d.ts +1 -0
- package/dist/orphan-active/index.js +1 -0
- package/dist/policy/merge-approval-head.js +7 -6
- package/dist/pr-closing-keywords/gh.js +32 -9
- package/dist/pr-closing-keywords/main.d.ts +1 -0
- package/dist/pr-closing-keywords/main.js +68 -3
- package/dist/pr-closing-keywords/types.d.ts +2 -0
- package/dist/pr-merge-readiness/gh.js +32 -9
- package/dist/pr-protected-issues/gh.js +36 -9
- package/dist/pr-wait-mergeable/wrappers.js +3 -3
- package/dist/product-first-done-gate/evaluate.js +6 -2
- package/dist/product-first-done-gate/types.js +2 -0
- package/dist/release/consumer-hard-stops.d.ts +48 -0
- package/dist/release/consumer-hard-stops.js +140 -0
- package/dist/release/consumer-readiness-disclosure.d.ts +18 -0
- package/dist/release/consumer-readiness-disclosure.js +51 -0
- package/dist/release/index.d.ts +2 -0
- package/dist/release/index.js +2 -0
- package/dist/release/pipeline.js +55 -0
- package/dist/release/run-consumer-readiness.d.ts +15 -0
- package/dist/release/run-consumer-readiness.js +24 -0
- package/dist/release/types.d.ts +6 -0
- package/dist/review-monitor/github-lease.js +4 -3
- package/dist/run-summary/types.d.ts +2 -2
- package/dist/scm/build-command.d.ts +2 -2
- package/dist/scm/build-command.js +2 -2
- package/dist/scm/call-shape.d.ts +25 -0
- package/dist/scm/call-shape.js +59 -0
- package/dist/scm/call.d.ts +7 -4
- package/dist/scm/call.js +41 -11
- package/dist/scm/gh-rest.d.ts +25 -12
- package/dist/scm/gh-rest.js +65 -15
- package/dist/scm/index.d.ts +2 -0
- package/dist/scm/index.js +2 -0
- package/dist/scm/spawn-status.d.ts +33 -0
- package/dist/scm/spawn-status.js +53 -0
- package/dist/scope/acceptance-evidence.d.ts +1 -1
- package/dist/scope/transition.js +4 -14
- package/dist/session/ac-pass-banking.d.ts +2 -2
- package/dist/session/ac-pass-banking.js +2 -2
- package/dist/session/host-session-owner.d.ts +53 -0
- package/dist/session/host-session-owner.js +89 -0
- package/dist/session/index.d.ts +1 -0
- package/dist/session/index.js +1 -0
- package/dist/session/occupancy.d.ts +21 -1
- package/dist/session/occupancy.js +70 -12
- package/dist/session/verify-ac-session-cache.d.ts +2 -2
- package/dist/session/verify-ac-session-cache.js +2 -2
- package/dist/triage/evaluate/worktrees.js +153 -6
- package/dist/umbrella-current-shape/index.d.ts +51 -3
- package/dist/umbrella-current-shape/index.js +106 -18
- package/dist/validate-content/deposit-required.d.ts +39 -0
- package/dist/validate-content/deposit-required.js +147 -0
- package/dist/validate-content/index.d.ts +1 -0
- package/dist/validate-content/index.js +1 -0
- package/dist/validate-content/validate-links.d.ts +2 -3
- package/dist/validate-content/validate-links.js +29 -2
- package/dist/vbrief-activate/activate.d.ts +7 -2
- package/dist/vbrief-activate/activate.js +26 -13
- package/dist/verify-ac/evaluate.d.ts +9 -0
- package/dist/verify-ac/evaluate.js +32 -9
- package/dist/verify-source/deposit-closure.d.ts +23 -0
- package/dist/verify-source/deposit-closure.js +162 -0
- package/dist/verify-source/index.d.ts +2 -0
- package/dist/verify-source/index.js +2 -0
- package/dist/verify-source/semantic-single-source.d.ts +36 -0
- package/dist/verify-source/semantic-single-source.js +349 -0
- package/package.json +3 -3
|
@@ -3,6 +3,7 @@ import { join, relative, resolve } from "node:path";
|
|
|
3
3
|
import { hasArtifactSuffix, resolveLifecycleRoot } from "../layout/resolve.js";
|
|
4
4
|
import { ScmStubError } from "../scm/errors.js";
|
|
5
5
|
import { resolveRepo } from "../triage/queue/repo.js";
|
|
6
|
+
import { normalizeScopePath, resolveCandidateScope, } from "./candidate-scope.js";
|
|
6
7
|
import { AGGREGATE_LATENCY_BUDGET_MS, formatAge, makeGateRunner, OpenIssueInventory, resolveIssueStateAggregate, resolveIssueStateScoped, SCOPED_LATENCY_BUDGET_MS, } from "./issue-state.js";
|
|
7
8
|
import { collectGithubRefs } from "./refs.js";
|
|
8
9
|
function readJson(path) {
|
|
@@ -220,7 +221,34 @@ function basisLines(tally, basis) {
|
|
|
220
221
|
}
|
|
221
222
|
return lines;
|
|
222
223
|
}
|
|
223
|
-
|
|
224
|
+
/** Keep an operator-supplied ref (or a reason quoting one) on a single line. */
|
|
225
|
+
function oneLine(text) {
|
|
226
|
+
return text.replace(/\r?\n/g, " ");
|
|
227
|
+
}
|
|
228
|
+
/** One operator-visible line naming what this run was allowed to look at (#3893). */
|
|
229
|
+
function scopeLines(scope, skipped) {
|
|
230
|
+
if (scope === null) {
|
|
231
|
+
return [];
|
|
232
|
+
}
|
|
233
|
+
if (scope.kind === "sweep") {
|
|
234
|
+
return [` Scope: repo-wide sweep -- ${oneLine(scope.reason)} (#3893).`];
|
|
235
|
+
}
|
|
236
|
+
const noun = skipped === 1 ? "brief" : "briefs";
|
|
237
|
+
const verb = skipped === 1 ? "was" : "were";
|
|
238
|
+
return [
|
|
239
|
+
` Scope: candidate diff against ${oneLine(scope.baseRef)}; ${skipped} running ${noun} outside ` +
|
|
240
|
+
`this branch's diff ${verb} not evaluated (#3893).`,
|
|
241
|
+
];
|
|
242
|
+
}
|
|
243
|
+
function summarizeScope(scope, skipped) {
|
|
244
|
+
if (scope === null) {
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
return scope.kind === "sweep"
|
|
248
|
+
? { kind: "sweep", baseRef: null, reason: scope.reason, skipped }
|
|
249
|
+
: { kind: "diff", baseRef: scope.baseRef, reason: null, skipped };
|
|
250
|
+
}
|
|
251
|
+
function formatRefusal(orphans, projectRoot, issueFilter, tally, basis, scope, skipped) {
|
|
224
252
|
const shippedOrphans = orphans.filter((orphan) => orphan.kind === "shipped");
|
|
225
253
|
const unresolvedOrphans = orphans.filter((orphan) => orphan.kind === "unresolved");
|
|
226
254
|
const noun = orphans.length === 1 ? "" : "s";
|
|
@@ -243,6 +271,8 @@ function formatRefusal(orphans, projectRoot, issueFilter, tally, basis) {
|
|
|
243
271
|
lines.push(" task scope:cancel -- xbrief/active/<file>.xbrief.json # when abandoning", " For stop-at:pr-open workers the orchestrator owns post-merge complete/cancel;", " for drive-to:merge-ready workers scope:complete is part of the worker unit (#2321 / #3429).", " Or run task swarm:finalize-cohort / task swarm:complete-cohort after cohort merge.");
|
|
244
272
|
}
|
|
245
273
|
if (unresolvedOrphans.length > 0) {
|
|
274
|
+
// Only the scoped `--issue N` arm can report unresolved, so the retry never
|
|
275
|
+
// needs a --changed-only form (the sweep arm fails open on unknown).
|
|
246
276
|
const retry = issueFilter === null
|
|
247
277
|
? "task verify:orphan-active"
|
|
248
278
|
: `task verify:orphan-active -- --issue ${issueFilter}`;
|
|
@@ -252,7 +282,7 @@ function formatRefusal(orphans, projectRoot, issueFilter, tally, basis) {
|
|
|
252
282
|
for (const orphan of orphans) {
|
|
253
283
|
lines.push(` - ${orphan.path} (${orphan.reason})`);
|
|
254
284
|
}
|
|
255
|
-
lines.push(...basisLines(tally, basis));
|
|
285
|
+
lines.push(...basisLines(tally, basis), ...scopeLines(scope, skipped));
|
|
256
286
|
return lines.join("\n");
|
|
257
287
|
}
|
|
258
288
|
function emptyBasis(budgetMs) {
|
|
@@ -279,6 +309,11 @@ function emptyBasis(budgetMs) {
|
|
|
279
309
|
* one complete, fail-closed open-issue inventory and stays fail-open on
|
|
280
310
|
* unknown so offline work is not network-authorized. A cache hit is honoured
|
|
281
311
|
* only inside `ISSUE_CACHE_MAX_AGE_MS`, and every verdict reports its basis.
|
|
312
|
+
*
|
|
313
|
+
* `changedOnly` is the merge-chokepoint form (#3893): only briefs the
|
|
314
|
+
* candidate's own diff touches are evaluated, so a brief stranded by another
|
|
315
|
+
* merge cannot fail this candidate. It falls back to the repo-wide sweep at
|
|
316
|
+
* the delivery tip and whenever the base ref is unresolvable.
|
|
282
317
|
*/
|
|
283
318
|
export function evaluate(projectRoot, options = {}) {
|
|
284
319
|
const root = resolve(projectRoot);
|
|
@@ -295,6 +330,7 @@ export function evaluate(projectRoot, options = {}) {
|
|
|
295
330
|
stream: "stderr",
|
|
296
331
|
orphans: [],
|
|
297
332
|
basis: emptyBasis(budgetMs),
|
|
333
|
+
scope: null,
|
|
298
334
|
};
|
|
299
335
|
}
|
|
300
336
|
let lifecycleRoot;
|
|
@@ -312,6 +348,7 @@ export function evaluate(projectRoot, options = {}) {
|
|
|
312
348
|
stream: quiet ? "none" : "stdout",
|
|
313
349
|
orphans: [],
|
|
314
350
|
basis: emptyBasis(budgetMs),
|
|
351
|
+
scope: null,
|
|
315
352
|
};
|
|
316
353
|
}
|
|
317
354
|
return {
|
|
@@ -320,6 +357,7 @@ export function evaluate(projectRoot, options = {}) {
|
|
|
320
357
|
stream: "stderr",
|
|
321
358
|
orphans: [],
|
|
322
359
|
basis: emptyBasis(budgetMs),
|
|
360
|
+
scope: null,
|
|
323
361
|
};
|
|
324
362
|
}
|
|
325
363
|
if (!existsSync(lifecycleRoot)) {
|
|
@@ -329,6 +367,7 @@ export function evaluate(projectRoot, options = {}) {
|
|
|
329
367
|
stream: quiet ? "none" : "stdout",
|
|
330
368
|
orphans: [],
|
|
331
369
|
basis: emptyBasis(budgetMs),
|
|
370
|
+
scope: null,
|
|
332
371
|
};
|
|
333
372
|
}
|
|
334
373
|
const defaultRepo = resolveRepo(options.repo, root);
|
|
@@ -349,9 +388,23 @@ export function evaluate(projectRoot, options = {}) {
|
|
|
349
388
|
};
|
|
350
389
|
try {
|
|
351
390
|
// #3774: missing gh/ghx is config (code 2), not an uncaught throw.
|
|
391
|
+
const briefs = listActiveRunningBriefs(root);
|
|
392
|
+
// Merge-chokepoint scoping (#3893). `--issue N` is already one origin, and
|
|
393
|
+
// an empty active/ has nothing to scope, so neither pays for the git reads.
|
|
394
|
+
const scope = options.changedOnly === true && issueFilter === null && briefs.length > 0
|
|
395
|
+
? resolveCandidateScope(root, join(lifecycleRoot, "active"), {
|
|
396
|
+
baseRef: options.baseRef,
|
|
397
|
+
runGit: options.runGit,
|
|
398
|
+
})
|
|
399
|
+
: null;
|
|
352
400
|
const orphans = [];
|
|
353
401
|
let scanned = 0;
|
|
354
|
-
|
|
402
|
+
let skipped = 0;
|
|
403
|
+
for (const brief of briefs) {
|
|
404
|
+
if (scope?.kind === "diff" && !scope.paths.has(normalizeScopePath(brief.path))) {
|
|
405
|
+
skipped += 1;
|
|
406
|
+
continue;
|
|
407
|
+
}
|
|
355
408
|
scanned += 1;
|
|
356
409
|
const { issues, prs } = collectGithubRefs(brief.plan, defaultRepo);
|
|
357
410
|
// --issue N is one origin: briefs that name that issue. PR-only briefs stay on the unscoped scan (#3429).
|
|
@@ -380,27 +433,30 @@ export function evaluate(projectRoot, options = {}) {
|
|
|
380
433
|
elapsedMs: Math.max(0, clock() - startedMs),
|
|
381
434
|
budgetMs,
|
|
382
435
|
};
|
|
436
|
+
const scopeSummary = summarizeScope(scope, skipped);
|
|
383
437
|
if (orphans.length > 0) {
|
|
384
438
|
return {
|
|
385
439
|
code: 1,
|
|
386
|
-
message: formatRefusal(orphans, root, issueFilter, tally, basis),
|
|
440
|
+
message: formatRefusal(orphans, root, issueFilter, tally, basis, scope, skipped),
|
|
387
441
|
stream: "stderr",
|
|
388
442
|
orphans,
|
|
389
443
|
basis,
|
|
444
|
+
scope: scopeSummary,
|
|
390
445
|
};
|
|
391
446
|
}
|
|
392
447
|
if (quiet) {
|
|
393
|
-
return { code: 0, message: "", stream: "none", orphans: [], basis };
|
|
448
|
+
return { code: 0, message: "", stream: "none", orphans: [], basis, scope: scopeSummary };
|
|
394
449
|
}
|
|
395
450
|
const issueNote = issueFilter === null ? "" : ` for issue #${issueFilter}`;
|
|
396
451
|
const headline = `verify:orphan-active: no orphaned active/running xBRIEFs${issueNote} ` +
|
|
397
452
|
`(scanned ${scanned} running brief${scanned === 1 ? "" : "s"} in active/).`;
|
|
398
453
|
return {
|
|
399
454
|
code: 0,
|
|
400
|
-
message: [headline, ...basisLines(tally, basis)].join("\n"),
|
|
455
|
+
message: [headline, ...basisLines(tally, basis), ...scopeLines(scope, skipped)].join("\n"),
|
|
401
456
|
stream: "stdout",
|
|
402
457
|
orphans: [],
|
|
403
458
|
basis,
|
|
459
|
+
scope: scopeSummary,
|
|
404
460
|
};
|
|
405
461
|
}
|
|
406
462
|
catch (err) {
|
|
@@ -411,6 +467,7 @@ export function evaluate(projectRoot, options = {}) {
|
|
|
411
467
|
stream: "stderr",
|
|
412
468
|
orphans: [],
|
|
413
469
|
basis: emptyBasis(budgetMs),
|
|
470
|
+
scope: null,
|
|
414
471
|
};
|
|
415
472
|
}
|
|
416
473
|
throw err;
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
import { join } from "node:path";
|
|
14
14
|
import { DEFAULT_EVENT_LOG, readEvents } from "../lifecycle/events.js";
|
|
15
15
|
import { captureExec } from "../pr-wait-mergeable/wrappers.js";
|
|
16
|
-
import {
|
|
16
|
+
import { resolveBinaryForArgv } from "../scm/call-shape.js";
|
|
17
17
|
import { resolveHumanMergePolicy } from "./require-human-merge.js";
|
|
18
18
|
/** Event name recorded at Phase 5 → 6 approval. */
|
|
19
19
|
export const PLAN_APPROVED_EVENT = "plan:approved";
|
|
@@ -358,14 +358,14 @@ export function evaluateMergeApprovalHead(input) {
|
|
|
358
358
|
}
|
|
359
359
|
/** Live `gh pr merge <N> --disable-auto` (best-effort). */
|
|
360
360
|
export function disablePullRequestAutoMerge(prNumber, repo, options = {}) {
|
|
361
|
+
const args = ["pr", "merge", String(prNumber), "--disable-auto"];
|
|
361
362
|
let binary;
|
|
362
363
|
try {
|
|
363
|
-
binary =
|
|
364
|
+
binary = resolveBinaryForArgv("pr", args.slice(1));
|
|
364
365
|
}
|
|
365
366
|
catch {
|
|
366
367
|
return { ok: false, stderr: "gh CLI not found" };
|
|
367
368
|
}
|
|
368
|
-
const args = ["pr", "merge", String(prNumber), "--disable-auto"];
|
|
369
369
|
if (repo !== null && repo.length > 0) {
|
|
370
370
|
args.push("--repo", repo);
|
|
371
371
|
}
|
|
@@ -388,15 +388,16 @@ export function disablePullRequestAutoMerge(prNumber, repo, options = {}) {
|
|
|
388
388
|
export function fetchPrHeadShaRest(prNumber, repo, options = {}) {
|
|
389
389
|
if (repo === null || repo.length === 0)
|
|
390
390
|
return null;
|
|
391
|
+
const endpoint = `repos/${repo}/pulls/${prNumber}`;
|
|
392
|
+
const apiArgs = ["api", endpoint, "--jq", ".head.sha"];
|
|
391
393
|
let binary;
|
|
392
394
|
try {
|
|
393
|
-
binary =
|
|
395
|
+
binary = resolveBinaryForArgv(apiArgs[0] ?? "api", apiArgs.slice(1));
|
|
394
396
|
}
|
|
395
397
|
catch {
|
|
396
398
|
return null;
|
|
397
399
|
}
|
|
398
|
-
const
|
|
399
|
-
const result = captureExec(binary, ["api", endpoint, "--jq", ".head.sha"], (options.timeoutSec ?? 60) * 1000);
|
|
400
|
+
const result = captureExec(binary, apiArgs, (options.timeoutSec ?? 60) * 1000);
|
|
400
401
|
if (result.returncode !== 0)
|
|
401
402
|
return null;
|
|
402
403
|
return normalizeSha(result.stdout.trim());
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { execFileSync } from "node:child_process";
|
|
2
|
-
import {
|
|
2
|
+
import { defaultWhich } from "../scm/binary.js";
|
|
3
|
+
import { classifyScmArgv, resolveBinaryForArgv } from "../scm/call-shape.js";
|
|
4
|
+
import { ghxSpawnFallbackBinary } from "../scm/spawn-status.js";
|
|
3
5
|
import { SUBPROCESS_MAX_BUFFER } from "../subprocess/max-buffer.js";
|
|
4
6
|
import { GH_TIMEOUT_S } from "./constants.js";
|
|
5
7
|
/** UTF-8-safe gh capture via execFile (no shell) — mirrors _safe_subprocess.run_text (#1366). */
|
|
@@ -7,18 +9,39 @@ export function defaultRunGh(cmd) {
|
|
|
7
9
|
if (cmd.length === 0 || cmd[0] !== "gh") {
|
|
8
10
|
return { returncode: -1, stdout: "", stderr: "expected gh as first argv element" };
|
|
9
11
|
}
|
|
10
|
-
const binary = resolveBinary();
|
|
11
12
|
const args = cmd.slice(1);
|
|
13
|
+
const binary = resolveBinaryForArgv(args[0] ?? "", args.slice(1));
|
|
14
|
+
const execOpts = {
|
|
15
|
+
encoding: "utf8",
|
|
16
|
+
timeout: GH_TIMEOUT_S * 1000,
|
|
17
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
18
|
+
maxBuffer: SUBPROCESS_MAX_BUFFER,
|
|
19
|
+
};
|
|
20
|
+
const runOnce = (bin) => execFileSync(bin, args, execOpts);
|
|
12
21
|
try {
|
|
13
|
-
const stdout =
|
|
14
|
-
encoding: "utf8",
|
|
15
|
-
timeout: GH_TIMEOUT_S * 1000,
|
|
16
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
17
|
-
maxBuffer: SUBPROCESS_MAX_BUFFER,
|
|
18
|
-
});
|
|
22
|
+
const stdout = runOnce(binary);
|
|
19
23
|
return { returncode: 0, stdout: typeof stdout === "string" ? stdout : "", stderr: "" };
|
|
20
24
|
}
|
|
21
|
-
catch (
|
|
25
|
+
catch (caught) {
|
|
26
|
+
let err = caught;
|
|
27
|
+
const first = err;
|
|
28
|
+
const retry = ghxSpawnFallbackBinary(classifyScmArgv(args[0] ?? "", args.slice(1)), binary, defaultWhich, {
|
|
29
|
+
status: typeof first.status === "number" ? first.status : null,
|
|
30
|
+
error: first.code === "ENOENT" || first.code === "ETIMEDOUT" || typeof first.status !== "number"
|
|
31
|
+
? { code: first.code, message: first.message }
|
|
32
|
+
: undefined,
|
|
33
|
+
stdout: typeof first.stdout === "string" ? first.stdout : "",
|
|
34
|
+
stderr: typeof first.stderr === "string" ? first.stderr : "",
|
|
35
|
+
});
|
|
36
|
+
if (retry !== null) {
|
|
37
|
+
try {
|
|
38
|
+
const stdout = runOnce(retry);
|
|
39
|
+
return { returncode: 0, stdout: typeof stdout === "string" ? stdout : "", stderr: "" };
|
|
40
|
+
}
|
|
41
|
+
catch (retryErr) {
|
|
42
|
+
err = retryErr;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
22
45
|
const e = err;
|
|
23
46
|
if (e.code === "ENOENT") {
|
|
24
47
|
return { returncode: -1, stdout: "", stderr: "gh CLI not found. Install GitHub CLI." };
|
|
@@ -3,6 +3,7 @@ export declare function parseAllowList(values: readonly string[]): Set<number>;
|
|
|
3
3
|
export declare function parseArgs(argv: readonly string[]): ParsedArgs;
|
|
4
4
|
export interface RunOptions {
|
|
5
5
|
readonly runGh?: RunGhFn;
|
|
6
|
+
readonly runGit?: RunGhFn;
|
|
6
7
|
}
|
|
7
8
|
export declare function run(argv: readonly string[], options?: RunOptions): number;
|
|
8
9
|
export declare function cmdPrCheckClosingKeywords(argv: readonly string[], options?: RunOptions): number;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { SUBPROCESS_MAX_BUFFER } from "../subprocess/max-buffer.js";
|
|
1
3
|
import { EXIT_CONFIG_ERROR, EXIT_HITS_FOUND, EXIT_OK } from "./constants.js";
|
|
2
4
|
import { findAllClosingKeywordHits, findHits, renderHit } from "./detect.js";
|
|
3
5
|
import { defaultRunGh, fetchPrBody, fetchPrCommitMessages } from "./gh.js";
|
|
@@ -23,6 +25,7 @@ function emptyParsed(error) {
|
|
|
23
25
|
pr: null,
|
|
24
26
|
bodyFile: null,
|
|
25
27
|
commitsFile: null,
|
|
28
|
+
fromGitRange: null,
|
|
26
29
|
repo: null,
|
|
27
30
|
allowKnownFalsePositives: [],
|
|
28
31
|
allowClose: [],
|
|
@@ -34,6 +37,7 @@ export function parseArgs(argv) {
|
|
|
34
37
|
let pr = null;
|
|
35
38
|
let bodyFile = null;
|
|
36
39
|
let commitsFile = null;
|
|
40
|
+
let fromGitRange = null;
|
|
37
41
|
let repo = null;
|
|
38
42
|
let mode = "both";
|
|
39
43
|
const allowKnownFalsePositives = [];
|
|
@@ -82,6 +86,17 @@ export function parseArgs(argv) {
|
|
|
82
86
|
else if (arg?.startsWith("--commits-file=")) {
|
|
83
87
|
commitsFile = arg.slice("--commits-file=".length);
|
|
84
88
|
}
|
|
89
|
+
else if (arg === "--from-git-range") {
|
|
90
|
+
const value = argv[i + 1];
|
|
91
|
+
if (value === undefined) {
|
|
92
|
+
return emptyParsed("argument --from-git-range: expected one argument");
|
|
93
|
+
}
|
|
94
|
+
fromGitRange = value;
|
|
95
|
+
i += 1;
|
|
96
|
+
}
|
|
97
|
+
else if (arg?.startsWith("--from-git-range=")) {
|
|
98
|
+
fromGitRange = arg.slice("--from-git-range=".length);
|
|
99
|
+
}
|
|
85
100
|
else if (arg === "--repo") {
|
|
86
101
|
const value = argv[i + 1];
|
|
87
102
|
if (value === undefined) {
|
|
@@ -140,7 +155,50 @@ export function parseArgs(argv) {
|
|
|
140
155
|
return emptyParsed(`unrecognized arguments: ${arg}`);
|
|
141
156
|
}
|
|
142
157
|
}
|
|
143
|
-
return {
|
|
158
|
+
return {
|
|
159
|
+
pr,
|
|
160
|
+
bodyFile,
|
|
161
|
+
commitsFile,
|
|
162
|
+
fromGitRange,
|
|
163
|
+
repo,
|
|
164
|
+
allowKnownFalsePositives,
|
|
165
|
+
allowClose,
|
|
166
|
+
mode,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
function defaultRunGit(cmd) {
|
|
170
|
+
try {
|
|
171
|
+
const stdout = execFileSync("git", [...cmd], {
|
|
172
|
+
encoding: "utf8",
|
|
173
|
+
timeout: 30_000,
|
|
174
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
175
|
+
maxBuffer: SUBPROCESS_MAX_BUFFER,
|
|
176
|
+
});
|
|
177
|
+
return { returncode: 0, stdout: typeof stdout === "string" ? stdout : "", stderr: "" };
|
|
178
|
+
}
|
|
179
|
+
catch (err) {
|
|
180
|
+
const e = err;
|
|
181
|
+
return {
|
|
182
|
+
returncode: typeof e.status === "number" ? e.status : 1,
|
|
183
|
+
stdout: typeof e.stdout === "string" ? e.stdout : "",
|
|
184
|
+
stderr: typeof e.stderr === "string" ? e.stderr : String(e.message ?? ""),
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
function readGitRange(range, runGit) {
|
|
189
|
+
const result = runGit(["log", range, "--format=%B%n--END--"]);
|
|
190
|
+
if (result.returncode !== 0) {
|
|
191
|
+
process.stderr.write("Error: git log " +
|
|
192
|
+
range +
|
|
193
|
+
" failed: " +
|
|
194
|
+
result.stderr.trim() +
|
|
195
|
+
". Recovery: git fetch origin master.\n");
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
return result.stdout
|
|
199
|
+
.split("\n--END--\n")
|
|
200
|
+
.map((part) => part.trim())
|
|
201
|
+
.filter((part) => part.length > 0);
|
|
144
202
|
}
|
|
145
203
|
function filterHits(hits, allowList) {
|
|
146
204
|
return hits.filter((h) => !allowList.has(h.issueNumber));
|
|
@@ -220,8 +278,8 @@ export function run(argv, options = {}) {
|
|
|
220
278
|
commitMessages = msgs;
|
|
221
279
|
}
|
|
222
280
|
else {
|
|
223
|
-
if (args.bodyFile === null && args.commitsFile === null) {
|
|
224
|
-
process.stderr.write("Error: must specify --pr OR --body-file / --commits-file.\n");
|
|
281
|
+
if (args.bodyFile === null && args.commitsFile === null && args.fromGitRange === null) {
|
|
282
|
+
process.stderr.write("Error: must specify --pr OR --body-file / --commits-file / --from-git-range.\n");
|
|
225
283
|
return EXIT_CONFIG_ERROR;
|
|
226
284
|
}
|
|
227
285
|
if (args.bodyFile !== null) {
|
|
@@ -238,6 +296,13 @@ export function run(argv, options = {}) {
|
|
|
238
296
|
}
|
|
239
297
|
commitMessages = msgs;
|
|
240
298
|
}
|
|
299
|
+
if (args.fromGitRange !== null) {
|
|
300
|
+
const msgs = readGitRange(args.fromGitRange, options.runGit ?? defaultRunGit);
|
|
301
|
+
if (msgs === null) {
|
|
302
|
+
return EXIT_CONFIG_ERROR;
|
|
303
|
+
}
|
|
304
|
+
commitMessages = [...commitMessages, ...msgs];
|
|
305
|
+
}
|
|
241
306
|
}
|
|
242
307
|
const runFp = args.mode === "fp" || args.mode === "both";
|
|
243
308
|
const runIntent = args.mode === "intent" || args.mode === "both";
|
|
@@ -18,6 +18,8 @@ export interface ParsedArgs {
|
|
|
18
18
|
readonly pr: number | null;
|
|
19
19
|
readonly bodyFile: string | null;
|
|
20
20
|
readonly commitsFile: string | null;
|
|
21
|
+
/** Offline git range (e.g. origin/master..HEAD) for check-graph wiring (#3969). */
|
|
22
|
+
readonly fromGitRange: string | null;
|
|
21
23
|
readonly repo: string | null;
|
|
22
24
|
readonly allowKnownFalsePositives: readonly string[];
|
|
23
25
|
/** Intent-mode allowlist of issue numbers permitted to use real closing keywords (#3015). */
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { execFileSync } from "node:child_process";
|
|
2
|
-
import {
|
|
2
|
+
import { defaultWhich } from "../scm/binary.js";
|
|
3
|
+
import { classifyScmArgv, resolveBinaryForArgv } from "../scm/call-shape.js";
|
|
4
|
+
import { ghxSpawnFallbackBinary } from "../scm/spawn-status.js";
|
|
3
5
|
import { SUBPROCESS_MAX_BUFFER } from "../subprocess/max-buffer.js";
|
|
4
6
|
import { GH_TIMEOUT_S, GREPTILE_LOGIN } from "./constants.js";
|
|
5
7
|
/** Display / inventory key for a required context (includes app binding when set). */
|
|
@@ -47,18 +49,39 @@ export function defaultRunGh(cmd) {
|
|
|
47
49
|
if (cmd.length === 0 || cmd[0] !== "gh") {
|
|
48
50
|
return { returncode: -1, stdout: "", stderr: "expected gh as first argv element" };
|
|
49
51
|
}
|
|
50
|
-
const binary = resolveBinary();
|
|
51
52
|
const args = cmd.slice(1);
|
|
53
|
+
const binary = resolveBinaryForArgv(args[0] ?? "", args.slice(1));
|
|
54
|
+
const execOpts = {
|
|
55
|
+
encoding: "utf8",
|
|
56
|
+
timeout: GH_TIMEOUT_S * 1000,
|
|
57
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
58
|
+
maxBuffer: SUBPROCESS_MAX_BUFFER,
|
|
59
|
+
};
|
|
60
|
+
const runOnce = (bin) => execFileSync(bin, args, execOpts);
|
|
52
61
|
try {
|
|
53
|
-
const stdout =
|
|
54
|
-
encoding: "utf8",
|
|
55
|
-
timeout: GH_TIMEOUT_S * 1000,
|
|
56
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
57
|
-
maxBuffer: SUBPROCESS_MAX_BUFFER,
|
|
58
|
-
});
|
|
62
|
+
const stdout = runOnce(binary);
|
|
59
63
|
return { returncode: 0, stdout: typeof stdout === "string" ? stdout : "", stderr: "" };
|
|
60
64
|
}
|
|
61
|
-
catch (
|
|
65
|
+
catch (caught) {
|
|
66
|
+
let err = caught;
|
|
67
|
+
const first = err;
|
|
68
|
+
const retry = ghxSpawnFallbackBinary(classifyScmArgv(args[0] ?? "", args.slice(1)), binary, defaultWhich, {
|
|
69
|
+
status: typeof first.status === "number" ? first.status : null,
|
|
70
|
+
error: first.code === "ENOENT" || first.code === "ETIMEDOUT" || typeof first.status !== "number"
|
|
71
|
+
? { code: first.code, message: first.message }
|
|
72
|
+
: undefined,
|
|
73
|
+
stdout: typeof first.stdout === "string" ? first.stdout : "",
|
|
74
|
+
stderr: typeof first.stderr === "string" ? first.stderr : "",
|
|
75
|
+
});
|
|
76
|
+
if (retry !== null) {
|
|
77
|
+
try {
|
|
78
|
+
const stdout = runOnce(retry);
|
|
79
|
+
return { returncode: 0, stdout: typeof stdout === "string" ? stdout : "", stderr: "" };
|
|
80
|
+
}
|
|
81
|
+
catch (retryErr) {
|
|
82
|
+
err = retryErr;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
62
85
|
const e = err;
|
|
63
86
|
if (e.code === "ENOENT") {
|
|
64
87
|
return { returncode: -1, stdout: "", stderr: "gh CLI not found. Install GitHub CLI." };
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { execFileSync } from "node:child_process";
|
|
2
|
-
import {
|
|
2
|
+
import { defaultWhich } from "../scm/binary.js";
|
|
3
|
+
import { classifyScmArgv, resolveBinaryForArgv } from "../scm/call-shape.js";
|
|
4
|
+
import { ghxSpawnFallbackBinary } from "../scm/spawn-status.js";
|
|
3
5
|
import { SUBPROCESS_MAX_BUFFER } from "../subprocess/max-buffer.js";
|
|
4
6
|
import { GH_TIMEOUT_S } from "./constants.js";
|
|
5
7
|
/**
|
|
@@ -11,18 +13,43 @@ export function defaultRunGh(cmd, binaryOverride) {
|
|
|
11
13
|
if (cmd.length === 0 || cmd[0] !== "gh") {
|
|
12
14
|
return { returncode: -1, stdout: "", stderr: "expected gh as first argv element" };
|
|
13
15
|
}
|
|
14
|
-
const binary = binaryOverride ?? resolveBinary();
|
|
15
16
|
const args = cmd.slice(1);
|
|
17
|
+
const binary = binaryOverride ?? resolveBinaryForArgv(args[0] ?? "", args.slice(1));
|
|
18
|
+
const execOpts = {
|
|
19
|
+
encoding: "utf8",
|
|
20
|
+
timeout: GH_TIMEOUT_S * 1000,
|
|
21
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
22
|
+
maxBuffer: SUBPROCESS_MAX_BUFFER,
|
|
23
|
+
};
|
|
24
|
+
const runOnce = (bin) => execFileSync(bin, args, execOpts);
|
|
16
25
|
try {
|
|
17
|
-
const stdout =
|
|
18
|
-
encoding: "utf8",
|
|
19
|
-
timeout: GH_TIMEOUT_S * 1000,
|
|
20
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
21
|
-
maxBuffer: SUBPROCESS_MAX_BUFFER,
|
|
22
|
-
});
|
|
26
|
+
const stdout = runOnce(binary);
|
|
23
27
|
return { returncode: 0, stdout: typeof stdout === "string" ? stdout : "", stderr: "" };
|
|
24
28
|
}
|
|
25
|
-
catch (
|
|
29
|
+
catch (caught) {
|
|
30
|
+
let err = caught;
|
|
31
|
+
const first = err;
|
|
32
|
+
const retry = binaryOverride === undefined
|
|
33
|
+
? ghxSpawnFallbackBinary(classifyScmArgv(args[0] ?? "", args.slice(1)), binary, defaultWhich, {
|
|
34
|
+
status: typeof first.status === "number" ? first.status : null,
|
|
35
|
+
error: first.code === "ENOENT" ||
|
|
36
|
+
first.code === "ETIMEDOUT" ||
|
|
37
|
+
typeof first.status !== "number"
|
|
38
|
+
? { code: first.code, message: first.message }
|
|
39
|
+
: undefined,
|
|
40
|
+
stdout: typeof first.stdout === "string" ? first.stdout : "",
|
|
41
|
+
stderr: typeof first.stderr === "string" ? first.stderr : "",
|
|
42
|
+
})
|
|
43
|
+
: null;
|
|
44
|
+
if (retry !== null) {
|
|
45
|
+
try {
|
|
46
|
+
const stdout = runOnce(retry);
|
|
47
|
+
return { returncode: 0, stdout: typeof stdout === "string" ? stdout : "", stderr: "" };
|
|
48
|
+
}
|
|
49
|
+
catch (retryErr) {
|
|
50
|
+
err = retryErr;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
26
53
|
const e = err;
|
|
27
54
|
if (e.code === "ENOENT") {
|
|
28
55
|
return { returncode: -1, stdout: "", stderr: "gh CLI not found. Install GitHub CLI." };
|
|
@@ -4,7 +4,7 @@ import { createRequire } from "node:module";
|
|
|
4
4
|
import { dirname, resolve } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { evaluate as evaluateCloseoutAttestable } from "../pr-closeout-attestable/evaluate.js";
|
|
7
|
-
import {
|
|
7
|
+
import { resolveBinaryForArgv } from "../scm/call-shape.js";
|
|
8
8
|
import { SUBPROCESS_MAX_BUFFER } from "../subprocess/max-buffer.js";
|
|
9
9
|
/** UTF-8-safe subprocess capture via spawnSync (no shell) — mirrors #1366. */
|
|
10
10
|
export function captureExec(executable, args, timeoutMs, options = {}) {
|
|
@@ -140,14 +140,14 @@ export function runMonitor(prNumber, repo, capMinutes, options = {}) {
|
|
|
140
140
|
/** Invoke ``gh pr merge --squash --delete-branch --admin`` [``--match-head-commit``]. */
|
|
141
141
|
export function runGhMerge(prNumber, repo, options = {}) {
|
|
142
142
|
const timeoutSec = options.timeout ?? 120;
|
|
143
|
+
const args = ["pr", "merge", String(prNumber), "--squash", "--delete-branch", "--admin"];
|
|
143
144
|
let binary;
|
|
144
145
|
try {
|
|
145
|
-
binary =
|
|
146
|
+
binary = resolveBinaryForArgv("pr", args.slice(1));
|
|
146
147
|
}
|
|
147
148
|
catch {
|
|
148
149
|
return [-1, "", "gh CLI not found. Install GitHub CLI."];
|
|
149
150
|
}
|
|
150
|
-
const args = ["pr", "merge", String(prNumber), "--squash", "--delete-branch", "--admin"];
|
|
151
151
|
if (repo) {
|
|
152
152
|
args.push("--repo", repo);
|
|
153
153
|
}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
import { existsSync, readFileSync } from "node:fs";
|
|
11
11
|
import { basename, isAbsolute, relative, resolve } from "node:path";
|
|
12
12
|
import { emitAcceptanceStampFromPlan, MISSING_AMBIGUITY_ATTESTATION_CAUSE, stampedAmbiguityAttestationError, } from "../intake/clause-derivation.js";
|
|
13
|
-
import { appendLiteralAcceptanceAdvisory, evaluateLiteralAcceptanceFromPlan, isExecutableLiteralSource, isInlineProseMention, isNoopRefusalReason, readNotAcceptanceCommands, readStoredLiteralAcceptanceCommands, runLiteralAcceptanceCommands, stripLiteralAcceptanceAdvisory, } from "../literal-acceptance/index.js";
|
|
13
|
+
import { appendLiteralAcceptanceAdvisory, evaluateLiteralAcceptanceFromPlan, isExecutableLiteralSource, isInlineProseMention, isNoopRefusalReason, isSafetyRefusalRun, readNotAcceptanceCommands, readStoredLiteralAcceptanceCommands, runLiteralAcceptanceCommands, stripLiteralAcceptanceAdvisory, } from "../literal-acceptance/index.js";
|
|
14
14
|
import { ENV_RUN_SUMMARY_PATH, RunSummaryEmitter, } from "../run-summary/index.js";
|
|
15
15
|
import { maybeBankOnAcPass } from "../session/ac-pass-banking.js";
|
|
16
16
|
import { resolveAcReuse, resolveScopeIdForAcReuse, snapshotFromReuseFields, } from "../session/ac-pass-reuse.js";
|
|
@@ -634,6 +634,10 @@ function annotate(result, acceptance, quiet) {
|
|
|
634
634
|
message = "";
|
|
635
635
|
}
|
|
636
636
|
const commandCount = Math.max(result.commands.length, acceptance.commands.length);
|
|
637
|
+
const executedRuns = result.runs.filter((run) => !isSafetyRefusalRun(run));
|
|
638
|
+
const refusedRuns = result.runs.filter(isSafetyRefusalRun);
|
|
639
|
+
const hasRefusal = refusedRuns.length > 0 || (result.rejected?.length ?? 0) > 0;
|
|
640
|
+
const servedFrom = executedRuns.length > 0 ? "executed" : hasRefusal ? "refused" : "executed";
|
|
637
641
|
return {
|
|
638
642
|
...result,
|
|
639
643
|
message,
|
|
@@ -648,7 +652,7 @@ function annotate(result, acceptance, quiet) {
|
|
|
648
652
|
rejectedCount: result.rejected?.length ?? 0,
|
|
649
653
|
}),
|
|
650
654
|
resolvedCommandCount: result.runs.length > 0 ? result.runs.length : commandCount,
|
|
651
|
-
servedFrom
|
|
655
|
+
servedFrom,
|
|
652
656
|
};
|
|
653
657
|
}
|
|
654
658
|
/**
|
|
@@ -26,7 +26,9 @@ export const HYGIENE_GATE_ID_PREFIXES = [
|
|
|
26
26
|
"verify:rule-ownership",
|
|
27
27
|
"verify:biome-config",
|
|
28
28
|
"verify:content-manifest",
|
|
29
|
+
"verify:deposit-closure",
|
|
29
30
|
"verify:skill-external-fetch-gate",
|
|
31
|
+
"verify:semantic-single-source",
|
|
30
32
|
"verify:cursor-tier1",
|
|
31
33
|
"verify:openclaw-tier1",
|
|
32
34
|
"verify:go-freeze",
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check 4 -- consumer hard-stop census (#3900 / #3713 / #3969).
|
|
3
|
+
*
|
|
4
|
+
* Enumerates open issues by privileged label only.
|
|
5
|
+
* BLOCKER is the sole permitted consumer title classification (inbound flare).
|
|
6
|
+
* The adoption-blocker label is never derived from a title.
|
|
7
|
+
* Issue bodies are not read into the verdict.
|
|
8
|
+
*
|
|
9
|
+
* parseClosesSet composes citation-grammar classifyPosition; do not add a
|
|
10
|
+
* third position classifier (#3969).
|
|
11
|
+
*/
|
|
12
|
+
export declare const CONSUMER_HARD_STOP_TITLE_RE: RegExp;
|
|
13
|
+
export declare const ADOPTION_BLOCKER_LABEL = "adoption-blocker";
|
|
14
|
+
export declare const BLOCKS_RELEASE_TAG_LABEL = "blocks-release-tag";
|
|
15
|
+
/** Maintainer-applied labels that can block a cut. Titles cannot. */
|
|
16
|
+
export declare const PRIVILEGED_HARD_STOP_LABELS: readonly string[];
|
|
17
|
+
export interface HardStopIssue {
|
|
18
|
+
readonly number: number;
|
|
19
|
+
readonly title: string;
|
|
20
|
+
readonly labels: readonly string[];
|
|
21
|
+
}
|
|
22
|
+
export interface HardStopCensusEntry {
|
|
23
|
+
readonly number: number;
|
|
24
|
+
readonly title: string;
|
|
25
|
+
readonly viaTitle: boolean;
|
|
26
|
+
readonly viaLabel: boolean;
|
|
27
|
+
}
|
|
28
|
+
export interface HardStopCensusResult {
|
|
29
|
+
readonly code: 0 | 1 | 2;
|
|
30
|
+
readonly message: string;
|
|
31
|
+
readonly stream: "stdout" | "stderr";
|
|
32
|
+
readonly matches: readonly HardStopCensusEntry[];
|
|
33
|
+
readonly shipsPast: readonly HardStopCensusEntry[];
|
|
34
|
+
}
|
|
35
|
+
/** Privileged labels only. Title is recorded as a flare, never as a match. Never inspect a body field. */
|
|
36
|
+
export declare function classifyHardStop(issue: HardStopIssue): HardStopCensusEntry | null;
|
|
37
|
+
export declare function enumerateConsumerHardStops(issues: readonly HardStopIssue[]): HardStopCensusEntry[];
|
|
38
|
+
/**
|
|
39
|
+
* Extract Closes/Fixes/Resolves #N from changelog Unreleased text.
|
|
40
|
+
* Never from issue bodies. Hits in refused markdown positions are not claims.
|
|
41
|
+
*/
|
|
42
|
+
export declare function parseClosesSet(changelogText: string): Set<number>;
|
|
43
|
+
export declare function evaluateConsumerHardStopCensus(options: {
|
|
44
|
+
readonly issues: readonly HardStopIssue[];
|
|
45
|
+
readonly closesSet: ReadonlySet<number>;
|
|
46
|
+
}): HardStopCensusResult;
|
|
47
|
+
export declare function issueFromInventoryRow(row: Record<string, unknown>): HardStopIssue | null;
|
|
48
|
+
//# sourceMappingURL=consumer-hard-stops.d.ts.map
|