@deftai/directive-core 0.79.3 → 0.80.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/coverage-hotspots/evaluate.d.ts +49 -0
- package/dist/coverage-hotspots/evaluate.js +262 -0
- package/dist/coverage-hotspots/index.d.ts +3 -0
- package/dist/coverage-hotspots/index.js +3 -0
- package/dist/coverage-hotspots/thresholds.d.ts +5 -0
- package/dist/coverage-hotspots/thresholds.js +48 -0
- package/dist/hooks/dispatcher.d.ts +25 -3
- package/dist/hooks/dispatcher.js +216 -65
- package/dist/hooks/readonly.d.ts +7 -0
- package/dist/hooks/readonly.js +83 -0
- package/dist/hooks/tools.d.ts +7 -0
- package/dist/hooks/tools.js +16 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/init-deposit/agent-hooks.d.ts +4 -1
- package/dist/init-deposit/agent-hooks.js +77 -21
- package/dist/init-deposit/xbrief-projections.d.ts +7 -0
- package/dist/init-deposit/xbrief-projections.js +29 -1
- package/dist/integration-e2e/bootstrap-cache-module.d.ts +2 -4
- package/dist/integration-e2e/bootstrap-cache-module.js +2 -20
- package/dist/policy/index.d.ts +2 -0
- package/dist/policy/index.js +31 -1
- package/dist/policy/runtime-authority.d.ts +54 -0
- package/dist/policy/runtime-authority.js +180 -0
- package/dist/policy/staleness-tickler.d.ts +20 -0
- package/dist/policy/staleness-tickler.js +151 -0
- package/dist/pr-merge-readiness/ci-gate.d.ts +7 -1
- package/dist/pr-merge-readiness/ci-gate.js +38 -5
- package/dist/pr-merge-readiness/gh.d.ts +4 -0
- package/dist/pr-merge-readiness/gh.js +10 -3
- package/dist/pr-merge-readiness/index.d.ts +2 -1
- package/dist/pr-merge-readiness/index.js +2 -1
- package/dist/pr-merge-readiness/runner-capacity-stall.d.ts +35 -0
- package/dist/pr-merge-readiness/runner-capacity-stall.js +46 -0
- package/dist/pr-wait-mergeable/cascade.js +8 -1
- package/dist/pr-wait-mergeable/classify.js +5 -1
- package/dist/pr-wait-mergeable/wrappers.js +15 -4
- package/dist/pr-watch/constants.d.ts +15 -0
- package/dist/pr-watch/constants.js +16 -1
- package/dist/pr-watch/main.js +12 -0
- package/dist/pr-watch/probe.js +13 -1
- package/dist/pr-watch/types.d.ts +9 -0
- package/dist/pr-watch/watch.js +24 -1
- package/dist/scope/main.js +12 -0
- package/dist/scope/undo.js +12 -2
- package/dist/session/release-availability.d.ts +21 -0
- package/dist/session/release-availability.js +109 -0
- package/dist/session/ritual-sentinel.d.ts +14 -0
- package/dist/session/ritual-sentinel.js +28 -0
- package/dist/session/session-start.d.ts +10 -0
- package/dist/session/session-start.js +18 -0
- package/dist/staleness-tickler/escalation.d.ts +14 -0
- package/dist/staleness-tickler/escalation.js +106 -0
- package/dist/staleness-tickler/idle.d.ts +18 -0
- package/dist/staleness-tickler/idle.js +47 -0
- package/dist/staleness-tickler/index.d.ts +8 -0
- package/dist/staleness-tickler/index.js +8 -0
- package/dist/staleness-tickler/probe-directive.d.ts +32 -0
- package/dist/staleness-tickler/probe-directive.js +106 -0
- package/dist/staleness-tickler/probe-xbrief.d.ts +12 -0
- package/dist/staleness-tickler/probe-xbrief.js +103 -0
- package/dist/staleness-tickler/run.d.ts +31 -0
- package/dist/staleness-tickler/run.js +227 -0
- package/dist/staleness-tickler/state.d.ts +10 -0
- package/dist/staleness-tickler/state.js +46 -0
- package/dist/staleness-tickler/types.d.ts +85 -0
- package/dist/staleness-tickler/types.js +5 -0
- package/dist/triage/bootstrap/cache-module.d.ts +25 -0
- package/dist/triage/bootstrap/cache-module.js +39 -0
- package/dist/triage/bootstrap/index.d.ts +1 -0
- package/dist/triage/bootstrap/index.js +7 -82
- package/dist/vbrief-validate/plan-hooks.d.ts +4 -0
- package/dist/vbrief-validate/plan-hooks.js +50 -0
- package/dist/verify-env/agent-hooks.js +4 -2
- package/dist/xbrief-migrate/agents-header.d.ts +0 -11
- package/dist/xbrief-migrate/agents-header.js +10 -1
- package/package.json +3 -3
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { readPlanPolicy } from "./plan-extensions.js";
|
|
2
|
+
export const FIELD_STALENESS_TICKLER = "plan.policy.stalenessTickler";
|
|
3
|
+
export const FIELD_STALENESS_TICKLER_CLI_ALIAS = "stalenessTickler";
|
|
4
|
+
const MS_PER_DAY = 24 * 60 * 60 * 1000;
|
|
5
|
+
export const DEFAULT_STALENESS_TICKLER_WEIGHTS = {
|
|
6
|
+
directiveMajor: 10,
|
|
7
|
+
directiveMinor: 3,
|
|
8
|
+
directivePatch: 1,
|
|
9
|
+
schemaMajor: 15,
|
|
10
|
+
schemaMinor: 5,
|
|
11
|
+
agePerDay: 0.1,
|
|
12
|
+
deferral: 2,
|
|
13
|
+
};
|
|
14
|
+
export const DEFAULT_STALENESS_TICKLER_TIERS = {
|
|
15
|
+
noticeMinorThreshold: 2,
|
|
16
|
+
strongAgeMs: 30 * MS_PER_DAY,
|
|
17
|
+
assertDeferralCap: 5,
|
|
18
|
+
};
|
|
19
|
+
export const DEFAULT_STALENESS_TICKLER_SNOOZE = {
|
|
20
|
+
quietMs: 7 * MS_PER_DAY,
|
|
21
|
+
noticeMs: MS_PER_DAY,
|
|
22
|
+
strongMs: 4 * 60 * 60 * 1000,
|
|
23
|
+
maxWidenMultiplier: 4,
|
|
24
|
+
};
|
|
25
|
+
export const DEFAULT_STALENESS_TICKLER_POLICY = {
|
|
26
|
+
enabled: true,
|
|
27
|
+
optOut: false,
|
|
28
|
+
weights: DEFAULT_STALENESS_TICKLER_WEIGHTS,
|
|
29
|
+
tiers: DEFAULT_STALENESS_TICKLER_TIERS,
|
|
30
|
+
snooze: DEFAULT_STALENESS_TICKLER_SNOOZE,
|
|
31
|
+
};
|
|
32
|
+
function readBoolean(rec, key, fallback) {
|
|
33
|
+
if (key in rec && typeof rec[key] === "boolean") {
|
|
34
|
+
return rec[key];
|
|
35
|
+
}
|
|
36
|
+
return fallback;
|
|
37
|
+
}
|
|
38
|
+
function readNumber(rec, key, fallback) {
|
|
39
|
+
if (key in rec && typeof rec[key] === "number" && Number.isFinite(rec[key])) {
|
|
40
|
+
return rec[key];
|
|
41
|
+
}
|
|
42
|
+
return fallback;
|
|
43
|
+
}
|
|
44
|
+
function readWeights(raw) {
|
|
45
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
46
|
+
return DEFAULT_STALENESS_TICKLER_WEIGHTS;
|
|
47
|
+
}
|
|
48
|
+
const rec = raw;
|
|
49
|
+
return {
|
|
50
|
+
directiveMajor: readNumber(rec, "directiveMajor", DEFAULT_STALENESS_TICKLER_WEIGHTS.directiveMajor),
|
|
51
|
+
directiveMinor: readNumber(rec, "directiveMinor", DEFAULT_STALENESS_TICKLER_WEIGHTS.directiveMinor),
|
|
52
|
+
directivePatch: readNumber(rec, "directivePatch", DEFAULT_STALENESS_TICKLER_WEIGHTS.directivePatch),
|
|
53
|
+
schemaMajor: readNumber(rec, "schemaMajor", DEFAULT_STALENESS_TICKLER_WEIGHTS.schemaMajor),
|
|
54
|
+
schemaMinor: readNumber(rec, "schemaMinor", DEFAULT_STALENESS_TICKLER_WEIGHTS.schemaMinor),
|
|
55
|
+
agePerDay: readNumber(rec, "agePerDay", DEFAULT_STALENESS_TICKLER_WEIGHTS.agePerDay),
|
|
56
|
+
deferral: readNumber(rec, "deferral", DEFAULT_STALENESS_TICKLER_WEIGHTS.deferral),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function readTierThresholds(raw) {
|
|
60
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
61
|
+
return DEFAULT_STALENESS_TICKLER_TIERS;
|
|
62
|
+
}
|
|
63
|
+
const rec = raw;
|
|
64
|
+
return {
|
|
65
|
+
noticeMinorThreshold: readNumber(rec, "noticeMinorThreshold", DEFAULT_STALENESS_TICKLER_TIERS.noticeMinorThreshold),
|
|
66
|
+
strongAgeMs: readNumber(rec, "strongAgeMs", DEFAULT_STALENESS_TICKLER_TIERS.strongAgeMs),
|
|
67
|
+
assertDeferralCap: readNumber(rec, "assertDeferralCap", DEFAULT_STALENESS_TICKLER_TIERS.assertDeferralCap),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function readSnooze(raw) {
|
|
71
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
72
|
+
return DEFAULT_STALENESS_TICKLER_SNOOZE;
|
|
73
|
+
}
|
|
74
|
+
const rec = raw;
|
|
75
|
+
return {
|
|
76
|
+
quietMs: readNumber(rec, "quietMs", DEFAULT_STALENESS_TICKLER_SNOOZE.quietMs),
|
|
77
|
+
noticeMs: readNumber(rec, "noticeMs", DEFAULT_STALENESS_TICKLER_SNOOZE.noticeMs),
|
|
78
|
+
strongMs: readNumber(rec, "strongMs", DEFAULT_STALENESS_TICKLER_SNOOZE.strongMs),
|
|
79
|
+
maxWidenMultiplier: readNumber(rec, "maxWidenMultiplier", DEFAULT_STALENESS_TICKLER_SNOOZE.maxWidenMultiplier),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
export function resolveStalenessTicklerPolicy(raw) {
|
|
83
|
+
if (raw === null || raw === undefined) {
|
|
84
|
+
return DEFAULT_STALENESS_TICKLER_POLICY;
|
|
85
|
+
}
|
|
86
|
+
if (typeof raw !== "object" || Array.isArray(raw)) {
|
|
87
|
+
return DEFAULT_STALENESS_TICKLER_POLICY;
|
|
88
|
+
}
|
|
89
|
+
const rec = raw;
|
|
90
|
+
return {
|
|
91
|
+
enabled: readBoolean(rec, "enabled", DEFAULT_STALENESS_TICKLER_POLICY.enabled),
|
|
92
|
+
optOut: readBoolean(rec, "optOut", DEFAULT_STALENESS_TICKLER_POLICY.optOut),
|
|
93
|
+
weights: readWeights(rec.weights),
|
|
94
|
+
tiers: readTierThresholds(rec.tiers),
|
|
95
|
+
snooze: readSnooze(rec.snooze),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
export function validateStalenessTickler(value) {
|
|
99
|
+
if (value === null || value === undefined) {
|
|
100
|
+
return [];
|
|
101
|
+
}
|
|
102
|
+
if (typeof value !== "object" || Array.isArray(value)) {
|
|
103
|
+
return [`${FIELD_STALENESS_TICKLER} must be an object; got ${typeof value}`];
|
|
104
|
+
}
|
|
105
|
+
const rec = value;
|
|
106
|
+
const errors = [];
|
|
107
|
+
for (const key of ["enabled", "optOut"]) {
|
|
108
|
+
if (key in rec && typeof rec[key] !== "boolean") {
|
|
109
|
+
errors.push(`${FIELD_STALENESS_TICKLER}.${key} must be a boolean`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return errors;
|
|
113
|
+
}
|
|
114
|
+
function fieldFromResolved(resolved, source) {
|
|
115
|
+
return {
|
|
116
|
+
name: FIELD_STALENESS_TICKLER,
|
|
117
|
+
current: resolved,
|
|
118
|
+
default: DEFAULT_STALENESS_TICKLER_POLICY,
|
|
119
|
+
source,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
/** Inspector row for `policy:show --field=stalenessTickler`. */
|
|
123
|
+
export function inspectStalenessTickler(data) {
|
|
124
|
+
if (data === null) {
|
|
125
|
+
return fieldFromResolved(DEFAULT_STALENESS_TICKLER_POLICY, "default");
|
|
126
|
+
}
|
|
127
|
+
const policyBlock = readPlanPolicy(data.plan);
|
|
128
|
+
if (typeof policyBlock !== "object" ||
|
|
129
|
+
policyBlock === null ||
|
|
130
|
+
Array.isArray(policyBlock) ||
|
|
131
|
+
!("stalenessTickler" in policyBlock)) {
|
|
132
|
+
return fieldFromResolved(DEFAULT_STALENESS_TICKLER_POLICY, "default");
|
|
133
|
+
}
|
|
134
|
+
const resolved = resolveStalenessTicklerPolicy(policyBlock.stalenessTickler);
|
|
135
|
+
return fieldFromResolved(resolved, "typed");
|
|
136
|
+
}
|
|
137
|
+
/** Resolve typed staleness tickler policy from PROJECT-DEFINITION. */
|
|
138
|
+
export function loadStalenessTicklerPolicy(data) {
|
|
139
|
+
if (data === null) {
|
|
140
|
+
return DEFAULT_STALENESS_TICKLER_POLICY;
|
|
141
|
+
}
|
|
142
|
+
const policyBlock = readPlanPolicy(data.plan);
|
|
143
|
+
if (typeof policyBlock !== "object" ||
|
|
144
|
+
policyBlock === null ||
|
|
145
|
+
Array.isArray(policyBlock) ||
|
|
146
|
+
!("stalenessTickler" in policyBlock)) {
|
|
147
|
+
return DEFAULT_STALENESS_TICKLER_POLICY;
|
|
148
|
+
}
|
|
149
|
+
return resolveStalenessTicklerPolicy(policyBlock.stalenessTickler);
|
|
150
|
+
}
|
|
151
|
+
//# sourceMappingURL=staleness-tickler.js.map
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import type { CheckRunRecord } from "./gh.js";
|
|
2
|
-
export type CiReadyState = "ready" | "blocked" | "not_ready_yet" | "skipped";
|
|
2
|
+
export type CiReadyState = "ready" | "blocked" | "not_ready_yet" | "runner_capacity_stall" | "skipped";
|
|
3
3
|
export interface CiGateOptions {
|
|
4
4
|
readonly skipCi?: boolean;
|
|
5
5
|
readonly ignoreCheckNames?: readonly string[];
|
|
6
|
+
/** Stall budget for runner_capacity_stall (#2672); default 20 minutes. */
|
|
7
|
+
readonly capacityStallBudgetMs?: number;
|
|
8
|
+
/** Injectable clock for capacity-stall tests. */
|
|
9
|
+
readonly nowMs?: number;
|
|
6
10
|
}
|
|
7
11
|
export interface CiCheckConclusion {
|
|
8
12
|
readonly name: string;
|
|
@@ -17,6 +21,8 @@ export interface CiGateSummary {
|
|
|
17
21
|
readonly ignored_checks: readonly string[];
|
|
18
22
|
readonly failed_required: readonly string[];
|
|
19
23
|
readonly pending_required: readonly string[];
|
|
24
|
+
/** Required checks classified as capacity-stalled (#2672). */
|
|
25
|
+
readonly capacity_stalled_required: readonly string[];
|
|
20
26
|
readonly conclusions: readonly CiCheckConclusion[];
|
|
21
27
|
}
|
|
22
28
|
export interface CiGateResult {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { classifyCapacityStalledRequired, DEFAULT_CAPACITY_STALL_MS, } from "./runner-capacity-stall.js";
|
|
1
2
|
const FAILED_CONCLUSIONS = new Set(["failure", "cancelled", "timed_out"]);
|
|
2
3
|
const PENDING_STATUSES = new Set(["queued", "in_progress"]);
|
|
3
4
|
function isPending(status, conclusion) {
|
|
@@ -8,8 +9,12 @@ function isPending(status, conclusion) {
|
|
|
8
9
|
}
|
|
9
10
|
export function buildCiSummaryLine(summary) {
|
|
10
11
|
const passed = summary.checked_count - summary.failed_required.length - summary.pending_required.length;
|
|
12
|
+
const stallSuffix = summary.capacity_stalled_required.length > 0
|
|
13
|
+
? ` / ${summary.capacity_stalled_required.length} capacity-stalled`
|
|
14
|
+
: "";
|
|
11
15
|
return (`CI check-runs: ${passed} passed / ` +
|
|
12
|
-
`${summary.failed_required.length} failed / ${summary.pending_required.length} pending`
|
|
16
|
+
`${summary.failed_required.length} failed / ${summary.pending_required.length} pending` +
|
|
17
|
+
stallSuffix);
|
|
13
18
|
}
|
|
14
19
|
export function evaluateCiGate(checkRuns, options = {}) {
|
|
15
20
|
if (options.skipCi === true) {
|
|
@@ -21,6 +26,7 @@ export function evaluateCiGate(checkRuns, options = {}) {
|
|
|
21
26
|
ignored_checks: [],
|
|
22
27
|
failed_required: [],
|
|
23
28
|
pending_required: [],
|
|
29
|
+
capacity_stalled_required: [],
|
|
24
30
|
conclusions: [],
|
|
25
31
|
},
|
|
26
32
|
};
|
|
@@ -29,6 +35,7 @@ export function evaluateCiGate(checkRuns, options = {}) {
|
|
|
29
35
|
const ignoredChecks = [];
|
|
30
36
|
const failedRequired = [];
|
|
31
37
|
const pendingRequired = [];
|
|
38
|
+
const pendingProbes = [];
|
|
32
39
|
const conclusions = [];
|
|
33
40
|
for (const run of checkRuns) {
|
|
34
41
|
const ignored = ignoredSet.has(run.name);
|
|
@@ -52,18 +59,43 @@ export function evaluateCiGate(checkRuns, options = {}) {
|
|
|
52
59
|
}
|
|
53
60
|
if (isPending(run.status, run.conclusion)) {
|
|
54
61
|
pendingRequired.push(`${run.name} (${run.status})`);
|
|
62
|
+
pendingProbes.push(run);
|
|
55
63
|
}
|
|
56
64
|
}
|
|
65
|
+
const stallOpts = {
|
|
66
|
+
budgetMs: options.capacityStallBudgetMs ?? DEFAULT_CAPACITY_STALL_MS,
|
|
67
|
+
nowMs: options.nowMs,
|
|
68
|
+
};
|
|
69
|
+
const capacityStalledRequired = classifyCapacityStalledRequired(pendingProbes, stallOpts);
|
|
57
70
|
const failures = [];
|
|
58
71
|
if (failedRequired.length > 0) {
|
|
59
72
|
failures.push(`Required CI check-runs failed: ${failedRequired.join(", ")}. ` +
|
|
60
73
|
"Required checks fail closed by default (#2169).");
|
|
61
74
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
75
|
+
let readyState;
|
|
76
|
+
if (failedRequired.length > 0) {
|
|
77
|
+
readyState = "blocked";
|
|
78
|
+
}
|
|
79
|
+
else if (pendingRequired.length > 0) {
|
|
80
|
+
// Capacity stall when every pending required check is a queue stall past
|
|
81
|
+
// budget — distinct from ordinary not_ready_yet (under budget / in_progress).
|
|
82
|
+
const allPendingStalled = capacityStalledRequired.length > 0 && capacityStalledRequired.length === pendingProbes.length;
|
|
83
|
+
if (allPendingStalled) {
|
|
84
|
+
readyState = "runner_capacity_stall";
|
|
85
|
+
failures.push(`Required CI check-runs capacity-stalled (runner_capacity_stall): ` +
|
|
86
|
+
`${capacityStalledRequired.join(", ")}. ` +
|
|
87
|
+
"Queued past the stall budget with no runner claimed (#2672). " +
|
|
88
|
+
"Wait for auto-failover to the GH-hosted lane; do NOT use --skip-ci.");
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
readyState = "not_ready_yet";
|
|
92
|
+
failures.push(`Required CI check-runs still running (not-ready-yet): ${pendingRequired.join(", ")}. ` +
|
|
93
|
+
"Wait for required checks to finish before merge.");
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
readyState = "ready";
|
|
65
98
|
}
|
|
66
|
-
const readyState = failedRequired.length > 0 ? "blocked" : pendingRequired.length > 0 ? "not_ready_yet" : "ready";
|
|
67
99
|
return {
|
|
68
100
|
failures,
|
|
69
101
|
summary: {
|
|
@@ -72,6 +104,7 @@ export function evaluateCiGate(checkRuns, options = {}) {
|
|
|
72
104
|
ignored_checks: ignoredChecks,
|
|
73
105
|
failed_required: failedRequired,
|
|
74
106
|
pending_required: pendingRequired,
|
|
107
|
+
capacity_stalled_required: capacityStalledRequired,
|
|
75
108
|
conclusions,
|
|
76
109
|
},
|
|
77
110
|
};
|
|
@@ -5,6 +5,10 @@ export interface CheckRunRecord {
|
|
|
5
5
|
readonly conclusion: string;
|
|
6
6
|
/** check-run `output.summary` text, when present (used by the SLizard verdict gate, #2189). */
|
|
7
7
|
readonly summary?: string;
|
|
8
|
+
/** ISO created_at — capacity-stall budget clock (#2672). */
|
|
9
|
+
readonly created_at?: string | null;
|
|
10
|
+
/** ISO started_at — null while still queued with no runner (#2672). */
|
|
11
|
+
readonly started_at?: string | null;
|
|
8
12
|
}
|
|
9
13
|
/** UTF-8-safe gh capture via execFile (no shell) — mirrors _safe_subprocess.run_text (#1366). */
|
|
10
14
|
export declare function defaultRunGh(cmd: readonly string[]): RunGhResult;
|
|
@@ -296,9 +296,16 @@ export function fetchCheckRunsRest(sha, repo, runGh) {
|
|
|
296
296
|
const conclusion = typeof r.conclusion === "string" ? r.conclusion : "none";
|
|
297
297
|
const name = typeof r.name === "string" && r.name.length > 0 ? r.name : "<unnamed>";
|
|
298
298
|
const runSummary = extractCheckRunSummary(r);
|
|
299
|
-
const
|
|
300
|
-
|
|
301
|
-
|
|
299
|
+
const createdAt = typeof r.created_at === "string" ? r.created_at : null;
|
|
300
|
+
const startedAt = typeof r.started_at === "string" ? r.started_at : null;
|
|
301
|
+
const record = {
|
|
302
|
+
name,
|
|
303
|
+
status,
|
|
304
|
+
conclusion,
|
|
305
|
+
created_at: createdAt,
|
|
306
|
+
started_at: startedAt,
|
|
307
|
+
...(runSummary === null ? {} : { summary: runSummary }),
|
|
308
|
+
};
|
|
302
309
|
checkRuns.push(record);
|
|
303
310
|
byStatus[status] = (byStatus[status] ?? 0) + 1;
|
|
304
311
|
byConclusion[conclusion] = (byConclusion[conclusion] ?? 0) + 1;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { buildCiSummaryLine, evaluateCiGate } from "./ci-gate.js";
|
|
1
|
+
export { buildCiSummaryLine, type CiCheckConclusion, type CiGateOptions, type CiGateResult, type CiGateSummary, type CiReadyState, evaluateCiGate, } from "./ci-gate.js";
|
|
2
2
|
export { type ComputeGateOptions, computeGateResult, type FetchMergeabilityFn } from "./compute.js";
|
|
3
3
|
export * from "./constants.js";
|
|
4
4
|
export { evaluateGates, isMergeReady } from "./evaluate.js";
|
|
@@ -8,6 +8,7 @@ export { cmdPrMergeReadiness, parseArgs, run } from "./main.js";
|
|
|
8
8
|
export { fetchMergeability, isGithubMergeableClean, MERGE_STATE_CLEAN, type MergeabilitySignal, mergeabilityToDict, verdictBlockIsSoftOnly, verdictShaIsStale, } from "./mergeability.js";
|
|
9
9
|
export { emitJson, exitCodeFor, gateResultToDict, printHuman } from "./output.js";
|
|
10
10
|
export { emptyVerdict, isInformalCleanMissingCanonicalFields, parseGreptileBody } from "./parse.js";
|
|
11
|
+
export { type CapacityStallOptions, type CapacityStallProbe, classifyCapacityStalledRequired, DEFAULT_CAPACITY_STALL_MS, isRunnerCapacityStalled, } from "./runner-capacity-stall.js";
|
|
11
12
|
export type { SlizardGateOptions, SlizardGateResult, SlizardGateSummary, SlizardVerdict, } from "./slizard-gate.js";
|
|
12
13
|
export { evaluateSlizardGate, isSlizardCheck, parseSlizardVerdict, SLIZARD_CHECK_NAME, } from "./slizard-gate.js";
|
|
13
14
|
export type { GateResult, GreptileVerdict, RunGhFn, RunGhResult } from "./types.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { buildCiSummaryLine, evaluateCiGate } from "./ci-gate.js";
|
|
1
|
+
export { buildCiSummaryLine, evaluateCiGate, } from "./ci-gate.js";
|
|
2
2
|
export { computeGateResult } from "./compute.js";
|
|
3
3
|
export * from "./constants.js";
|
|
4
4
|
export { evaluateGates, isMergeReady } from "./evaluate.js";
|
|
@@ -8,5 +8,6 @@ export { cmdPrMergeReadiness, parseArgs, run } from "./main.js";
|
|
|
8
8
|
export { fetchMergeability, isGithubMergeableClean, MERGE_STATE_CLEAN, mergeabilityToDict, verdictBlockIsSoftOnly, verdictShaIsStale, } from "./mergeability.js";
|
|
9
9
|
export { emitJson, exitCodeFor, gateResultToDict, printHuman } from "./output.js";
|
|
10
10
|
export { emptyVerdict, isInformalCleanMissingCanonicalFields, parseGreptileBody } from "./parse.js";
|
|
11
|
+
export { classifyCapacityStalledRequired, DEFAULT_CAPACITY_STALL_MS, isRunnerCapacityStalled, } from "./runner-capacity-stall.js";
|
|
11
12
|
export { evaluateSlizardGate, isSlizardCheck, parseSlizardVerdict, SLIZARD_CHECK_NAME, } from "./slizard-gate.js";
|
|
12
13
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runner capacity-stall classifier for required CI check-runs (#2672).
|
|
3
|
+
*
|
|
4
|
+
* Distinct from ordinary `not_ready_yet` (check still under budget / in_progress)
|
|
5
|
+
* and from execution hangs (#2652). A capacity stall is: required check still
|
|
6
|
+
* `queued`, no `started_at`, past the stall budget since `created_at`.
|
|
7
|
+
*/
|
|
8
|
+
/** Default stall budget: 20 minutes (matches CI_CAPACITY_STALL_SECONDS in ci.yml). */
|
|
9
|
+
export declare const DEFAULT_CAPACITY_STALL_MS: number;
|
|
10
|
+
export interface CapacityStallProbe {
|
|
11
|
+
readonly name: string;
|
|
12
|
+
readonly status: string;
|
|
13
|
+
readonly conclusion?: string;
|
|
14
|
+
/** ISO timestamp when the check-run was created (budget clock). */
|
|
15
|
+
readonly created_at?: string | null;
|
|
16
|
+
/** ISO timestamp when execution started; null/absent while still queued. */
|
|
17
|
+
readonly started_at?: string | null;
|
|
18
|
+
}
|
|
19
|
+
export interface CapacityStallOptions {
|
|
20
|
+
/** Stall budget in ms (default 20m). */
|
|
21
|
+
readonly budgetMs?: number;
|
|
22
|
+
/** Injectable clock for tests. */
|
|
23
|
+
readonly nowMs?: number;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* True when a single check-run looks capacity-stalled (queued, never started,
|
|
27
|
+
* past budget). Never true for `in_progress` (#2652 must not be conflated).
|
|
28
|
+
*/
|
|
29
|
+
export declare function isRunnerCapacityStalled(run: CapacityStallProbe, options?: CapacityStallOptions): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Among pending required check names, return those that are capacity-stalled.
|
|
32
|
+
* Callers pass only the required/pending subset.
|
|
33
|
+
*/
|
|
34
|
+
export declare function classifyCapacityStalledRequired(pendingRequired: readonly CapacityStallProbe[], options?: CapacityStallOptions): readonly string[];
|
|
35
|
+
//# sourceMappingURL=runner-capacity-stall.d.ts.map
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runner capacity-stall classifier for required CI check-runs (#2672).
|
|
3
|
+
*
|
|
4
|
+
* Distinct from ordinary `not_ready_yet` (check still under budget / in_progress)
|
|
5
|
+
* and from execution hangs (#2652). A capacity stall is: required check still
|
|
6
|
+
* `queued`, no `started_at`, past the stall budget since `created_at`.
|
|
7
|
+
*/
|
|
8
|
+
/** Default stall budget: 20 minutes (matches CI_CAPACITY_STALL_SECONDS in ci.yml). */
|
|
9
|
+
export const DEFAULT_CAPACITY_STALL_MS = 20 * 60 * 1000;
|
|
10
|
+
/**
|
|
11
|
+
* True when a single check-run looks capacity-stalled (queued, never started,
|
|
12
|
+
* past budget). Never true for `in_progress` (#2652 must not be conflated).
|
|
13
|
+
*/
|
|
14
|
+
export function isRunnerCapacityStalled(run, options = {}) {
|
|
15
|
+
if (run.status !== "queued") {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
if (run.started_at != null && String(run.started_at).length > 0) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
const createdRaw = run.created_at;
|
|
22
|
+
if (createdRaw == null || String(createdRaw).length === 0) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
const createdMs = Date.parse(String(createdRaw));
|
|
26
|
+
if (!Number.isFinite(createdMs)) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
const nowMs = options.nowMs ?? Date.now();
|
|
30
|
+
const budgetMs = options.budgetMs ?? DEFAULT_CAPACITY_STALL_MS;
|
|
31
|
+
return nowMs - createdMs >= budgetMs;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Among pending required check names, return those that are capacity-stalled.
|
|
35
|
+
* Callers pass only the required/pending subset.
|
|
36
|
+
*/
|
|
37
|
+
export function classifyCapacityStalledRequired(pendingRequired, options = {}) {
|
|
38
|
+
const stalled = [];
|
|
39
|
+
for (const run of pendingRequired) {
|
|
40
|
+
if (isRunnerCapacityStalled(run, options)) {
|
|
41
|
+
stalled.push(run.name);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return stalled;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=runner-capacity-stall.js.map
|
|
@@ -2,6 +2,13 @@ import { classifyMonitorOutcome, parseMonitorPayload } from "./classify.js";
|
|
|
2
2
|
import { EXIT_CONFIG_ERROR, EXIT_MERGED, EXIT_TIMEOUT_OR_ESCALATION } from "./constants.js";
|
|
3
3
|
import { makeResult } from "./result.js";
|
|
4
4
|
import { runGhMerge, runMonitor, runProtectedCheck } from "./wrappers.js";
|
|
5
|
+
/** Node module-not-found / missing script — not a protected-issue overlap (#2667). */
|
|
6
|
+
function isProtectedCheckConfigFailure(stderr) {
|
|
7
|
+
const tail = stderr.trim();
|
|
8
|
+
return (tail.includes("MODULE_NOT_FOUND") ||
|
|
9
|
+
tail.includes("Cannot find module") ||
|
|
10
|
+
tail.includes("protected-check script not found:"));
|
|
11
|
+
}
|
|
5
12
|
/** Run protected-check -> wait -> merge cascade (#1369). */
|
|
6
13
|
export function waitMergeableAndMerge(prNumber, repo, options) {
|
|
7
14
|
const protectedFn = options.protectedFn ?? runProtectedCheck;
|
|
@@ -17,7 +24,7 @@ export function waitMergeableAndMerge(prNumber, repo, options) {
|
|
|
17
24
|
stderr: prcStderr,
|
|
18
25
|
protected: [...protectedIssues],
|
|
19
26
|
};
|
|
20
|
-
if (prcRc === 1) {
|
|
27
|
+
if (prcRc === 1 && !isProtectedCheckConfigFailure(prcStderr)) {
|
|
21
28
|
return makeResult({
|
|
22
29
|
prNumber,
|
|
23
30
|
repo,
|
|
@@ -5,7 +5,11 @@ export function classifyMonitorOutcome(monitorReturncode, monitorPayload) {
|
|
|
5
5
|
return ["clean", EXIT_MERGED];
|
|
6
6
|
}
|
|
7
7
|
if (monitorReturncode === 1) {
|
|
8
|
-
|
|
8
|
+
// Bare exit 1 (MODULE_NOT_FOUND / missing script) is not budget exhaustion (#2673).
|
|
9
|
+
if (monitorPayload.monitor_result === "CAP-REACHED") {
|
|
10
|
+
return ["cap-reached", EXIT_TIMEOUT_OR_ESCALATION];
|
|
11
|
+
}
|
|
12
|
+
return ["config-error", EXIT_CONFIG_ERROR];
|
|
9
13
|
}
|
|
10
14
|
if (monitorReturncode === 2) {
|
|
11
15
|
return ["config-error", EXIT_CONFIG_ERROR];
|
|
@@ -56,14 +56,17 @@ export function cliScriptPath(name) {
|
|
|
56
56
|
const candidates = [];
|
|
57
57
|
try {
|
|
58
58
|
const require = createRequire(import.meta.url);
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
// package.json is not an exported subpath under @deftai/directive exports (#2667).
|
|
60
|
+
const mainEntry = require.resolve("@deftai/directive");
|
|
61
|
+
candidates.push(resolve(dirname(mainEntry), script));
|
|
61
62
|
}
|
|
62
63
|
catch {
|
|
63
64
|
// Core does not declare a runtime dependency on the CLI package; ignore.
|
|
64
65
|
}
|
|
65
66
|
// Monorepo: packages/core/dist/pr-wait-mergeable -> packages/cli/dist
|
|
66
67
|
candidates.push(resolve(here, "../../../cli/dist", script));
|
|
68
|
+
// npm hoisted: .../@deftai/directive-core/dist/... -> .../@deftai/directive/dist
|
|
69
|
+
candidates.push(resolve(here, "../../../directive/dist", script));
|
|
67
70
|
// npm nested: .../directive/node_modules/@deftai/directive-core/dist/... -> .../directive/dist
|
|
68
71
|
candidates.push(resolve(here, "../../../../dist", script));
|
|
69
72
|
for (const candidate of candidates) {
|
|
@@ -75,9 +78,13 @@ export function cliScriptPath(name) {
|
|
|
75
78
|
}
|
|
76
79
|
/** Invoke pr-protected-issues CLI and return (returncode, stdout, stderr). */
|
|
77
80
|
export function runProtectedCheck(prNumber, repo, protectedIssues, options = {}) {
|
|
81
|
+
const scriptPath = cliScriptPath("pr-protected-issues");
|
|
82
|
+
if (!existsSync(scriptPath)) {
|
|
83
|
+
return [2, "", `protected-check script not found: ${scriptPath}`];
|
|
84
|
+
}
|
|
78
85
|
const node = options.nodeExecutable ?? process.execPath;
|
|
79
86
|
const cmd = [
|
|
80
|
-
|
|
87
|
+
scriptPath,
|
|
81
88
|
String(prNumber),
|
|
82
89
|
"--protected",
|
|
83
90
|
protectedIssues.map(String).join(","),
|
|
@@ -92,10 +99,14 @@ export function runProtectedCheck(prNumber, repo, protectedIssues, options = {})
|
|
|
92
99
|
}
|
|
93
100
|
/** Invoke pr-monitor CLI with --json and return (returncode, stdout, stderr). */
|
|
94
101
|
export function runMonitor(prNumber, repo, capMinutes, options = {}) {
|
|
102
|
+
const scriptPath = cliScriptPath("pr-monitor");
|
|
103
|
+
if (!existsSync(scriptPath)) {
|
|
104
|
+
return [2, "", `monitor script not found: ${scriptPath}`];
|
|
105
|
+
}
|
|
95
106
|
const node = options.nodeExecutable ?? process.execPath;
|
|
96
107
|
const timeoutSec = options.timeout ?? capMinutes * 60 + 60;
|
|
97
108
|
const cmd = [
|
|
98
|
-
|
|
109
|
+
scriptPath,
|
|
99
110
|
String(prNumber),
|
|
100
111
|
"--repo",
|
|
101
112
|
repo,
|
|
@@ -17,6 +17,16 @@ export declare const VERDICT_NEW_P0_P1 = "NEW_P0_P1";
|
|
|
17
17
|
export declare const VERDICT_ERRORED = "ERRORED";
|
|
18
18
|
export declare const VERDICT_STALL = "STALL";
|
|
19
19
|
export declare const VERDICT_TIMEOUT = "TIMEOUT";
|
|
20
|
+
/**
|
|
21
|
+
* Greptile side of the clean gate is satisfied on HEAD but required CI is red
|
|
22
|
+
* (#2688). Exit 2 — fail-loud toward a CI fix loop instead of idle-polling.
|
|
23
|
+
*/
|
|
24
|
+
export declare const VERDICT_CI_BLOCKED = "CI_BLOCKED";
|
|
25
|
+
/**
|
|
26
|
+
* Required CI is still queued past the capacity-stall budget with no runner
|
|
27
|
+
* claimed (#2672). Exit 2 — wait for auto-failover; never --skip-ci.
|
|
28
|
+
*/
|
|
29
|
+
export declare const VERDICT_RUNNER_CAPACITY_STALL = "RUNNER_CAPACITY_STALL";
|
|
20
30
|
/** --one-shot only: a single probe with no terminal verdict yet. */
|
|
21
31
|
export declare const VERDICT_PENDING = "PENDING";
|
|
22
32
|
/** External/config fault mid-probe (unresolvable repo/HEAD, gh unavailable). */
|
|
@@ -35,4 +45,9 @@ export declare const WATCH_HELP: string;
|
|
|
35
45
|
* whole 30-minute budget (#1039 STALL terminal).
|
|
36
46
|
*/
|
|
37
47
|
export declare const DEFAULT_STALL_THRESHOLD = 3;
|
|
48
|
+
/**
|
|
49
|
+
* Consecutive polls with clean_gate_holdout=ci_failures (Greptile otherwise
|
|
50
|
+
* satisfied) before CI_BLOCKED (#2688). Same default as SHA STALL.
|
|
51
|
+
*/
|
|
52
|
+
export declare const DEFAULT_CI_BLOCKED_THRESHOLD = 3;
|
|
38
53
|
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -17,6 +17,16 @@ export const VERDICT_NEW_P0_P1 = "NEW_P0_P1";
|
|
|
17
17
|
export const VERDICT_ERRORED = "ERRORED";
|
|
18
18
|
export const VERDICT_STALL = "STALL";
|
|
19
19
|
export const VERDICT_TIMEOUT = "TIMEOUT";
|
|
20
|
+
/**
|
|
21
|
+
* Greptile side of the clean gate is satisfied on HEAD but required CI is red
|
|
22
|
+
* (#2688). Exit 2 — fail-loud toward a CI fix loop instead of idle-polling.
|
|
23
|
+
*/
|
|
24
|
+
export const VERDICT_CI_BLOCKED = "CI_BLOCKED";
|
|
25
|
+
/**
|
|
26
|
+
* Required CI is still queued past the capacity-stall budget with no runner
|
|
27
|
+
* claimed (#2672). Exit 2 — wait for auto-failover; never --skip-ci.
|
|
28
|
+
*/
|
|
29
|
+
export const VERDICT_RUNNER_CAPACITY_STALL = "RUNNER_CAPACITY_STALL";
|
|
20
30
|
/** --one-shot only: a single probe with no terminal verdict yet. */
|
|
21
31
|
export const VERDICT_PENDING = "PENDING";
|
|
22
32
|
/** External/config fault mid-probe (unresolvable repo/HEAD, gh unavailable). */
|
|
@@ -49,7 +59,7 @@ export const WATCH_HELP = "usage: task pr:watch -- <pr_number> [options]\n" +
|
|
|
49
59
|
"exit codes:\n" +
|
|
50
60
|
" 0 CLEAN SHA-matched review, confidence > 3, no P0/P1, CI green\n" +
|
|
51
61
|
" 1 NEW_P0_P1 Blocking findings on the current (SHA-matched) review\n" +
|
|
52
|
-
" 2 ERRORED | STALL | TIMEOUT | config / usage error\n";
|
|
62
|
+
" 2 ERRORED | STALL | TIMEOUT | CI_BLOCKED | RUNNER_CAPACITY_STALL | config / usage error\n";
|
|
53
63
|
/**
|
|
54
64
|
* Consecutive polls with a review PRESENT but stuck on a stale (non-HEAD)
|
|
55
65
|
* commit before the loop surfaces STALL instead of waiting the full cap. Keeps
|
|
@@ -57,4 +67,9 @@ export const WATCH_HELP = "usage: task pr:watch -- <pr_number> [options]\n" +
|
|
|
57
67
|
* whole 30-minute budget (#1039 STALL terminal).
|
|
58
68
|
*/
|
|
59
69
|
export const DEFAULT_STALL_THRESHOLD = 3;
|
|
70
|
+
/**
|
|
71
|
+
* Consecutive polls with clean_gate_holdout=ci_failures (Greptile otherwise
|
|
72
|
+
* satisfied) before CI_BLOCKED (#2688). Same default as SHA STALL.
|
|
73
|
+
*/
|
|
74
|
+
export const DEFAULT_CI_BLOCKED_THRESHOLD = 3;
|
|
60
75
|
//# sourceMappingURL=constants.js.map
|
package/dist/pr-watch/main.js
CHANGED
|
@@ -151,6 +151,9 @@ export function watchResultToJson(result) {
|
|
|
151
151
|
p1_count: p.p1Count,
|
|
152
152
|
errored: p.errored,
|
|
153
153
|
ci_failures: p.ciFailures,
|
|
154
|
+
ci_failed_checks: [...p.ciFailedChecks],
|
|
155
|
+
ci_ready_state: p.ciReadyState,
|
|
156
|
+
ci_capacity_stalled_checks: [...p.ciCapacityStalledChecks],
|
|
154
157
|
is_clean: p.isClean,
|
|
155
158
|
clean_gate_holdout: p.cleanGateHoldout,
|
|
156
159
|
elapsed_seconds: result.elapsedSeconds,
|
|
@@ -171,6 +174,15 @@ export function printWatchHuman(result) {
|
|
|
171
174
|
lines.push(` Findings: P0=${p.p0Count} P1=${p.p1Count}`);
|
|
172
175
|
lines.push(` Errored sentinel: ${p.errored}`);
|
|
173
176
|
lines.push(` CI failures: ${p.ciFailures}`);
|
|
177
|
+
if (p.ciReadyState !== null) {
|
|
178
|
+
lines.push(` CI ready_state: ${p.ciReadyState}`);
|
|
179
|
+
}
|
|
180
|
+
if (p.ciFailedChecks.length > 0) {
|
|
181
|
+
lines.push(` Failed checks: ${p.ciFailedChecks.join("; ")}`);
|
|
182
|
+
}
|
|
183
|
+
if (p.ciCapacityStalledChecks.length > 0) {
|
|
184
|
+
lines.push(` Capacity-stalled: ${p.ciCapacityStalledChecks.join("; ")}`);
|
|
185
|
+
}
|
|
174
186
|
if (p.cleanGateHoldout !== null) {
|
|
175
187
|
lines.push(` Clean-gate holdout: ${p.cleanGateHoldout}`);
|
|
176
188
|
}
|
package/dist/pr-watch/probe.js
CHANGED
|
@@ -14,6 +14,9 @@ function errorProbe(headSha, message) {
|
|
|
14
14
|
hasBlocking: false,
|
|
15
15
|
errored: false,
|
|
16
16
|
ciFailures: 0,
|
|
17
|
+
ciFailedChecks: [],
|
|
18
|
+
ciReadyState: null,
|
|
19
|
+
ciCapacityStalledChecks: [],
|
|
17
20
|
terminalCheckRun: false,
|
|
18
21
|
isClean: false,
|
|
19
22
|
cleanGateHoldout: null,
|
|
@@ -61,12 +64,18 @@ export function probeOnce(prNumber, repoArg, runGh = defaultRunGh) {
|
|
|
61
64
|
// ci_failures=0 / terminal so the Greptile verdict drives; the merge button
|
|
62
65
|
// still owns the hard CI gate via pr:merge-ready (#796).
|
|
63
66
|
let ciFailures = 0;
|
|
67
|
+
let ciFailedChecks = [];
|
|
68
|
+
let ciReadyState = null;
|
|
69
|
+
let ciCapacityStalledChecks = [];
|
|
64
70
|
let terminalCheckRun = true;
|
|
65
71
|
if (repo !== null) {
|
|
66
72
|
const check = fetchCheckRunsRest(headSha, repo, runGh);
|
|
67
73
|
if (check.summary !== null) {
|
|
68
74
|
const ci = evaluateCiGate(check.checkRuns, {});
|
|
69
|
-
|
|
75
|
+
ciFailedChecks = ci.summary.failed_required;
|
|
76
|
+
ciFailures = ciFailedChecks.length;
|
|
77
|
+
ciReadyState = ci.summary.ready_state;
|
|
78
|
+
ciCapacityStalledChecks = ci.summary.capacity_stalled_required;
|
|
70
79
|
terminalCheckRun = ci.summary.pending_required.length === 0;
|
|
71
80
|
}
|
|
72
81
|
}
|
|
@@ -91,6 +100,9 @@ export function probeOnce(prNumber, repoArg, runGh = defaultRunGh) {
|
|
|
91
100
|
hasBlocking: findings.has_blocking,
|
|
92
101
|
errored,
|
|
93
102
|
ciFailures,
|
|
103
|
+
ciFailedChecks,
|
|
104
|
+
ciReadyState,
|
|
105
|
+
ciCapacityStalledChecks,
|
|
94
106
|
terminalCheckRun,
|
|
95
107
|
isClean,
|
|
96
108
|
cleanGateHoldout,
|
package/dist/pr-watch/types.d.ts
CHANGED
|
@@ -13,6 +13,15 @@ export interface WatchProbe {
|
|
|
13
13
|
readonly hasBlocking: boolean;
|
|
14
14
|
readonly errored: boolean;
|
|
15
15
|
readonly ciFailures: number;
|
|
16
|
+
/** Failed required check identities (name + conclusion), when CI was probed. */
|
|
17
|
+
readonly ciFailedChecks: readonly string[];
|
|
18
|
+
/**
|
|
19
|
+
* CI gate ready_state from evaluateCiGate (#2169 / #2672).
|
|
20
|
+
* Includes `runner_capacity_stall` distinct from `not_ready_yet`.
|
|
21
|
+
*/
|
|
22
|
+
readonly ciReadyState: string | null;
|
|
23
|
+
/** Required checks classified as capacity-stalled (#2672). */
|
|
24
|
+
readonly ciCapacityStalledChecks: readonly string[];
|
|
16
25
|
/** All required CI check-runs have a terminal conclusion (none pending). */
|
|
17
26
|
readonly terminalCheckRun: boolean;
|
|
18
27
|
readonly isClean: boolean;
|