@deftai/directive-core 0.95.0 → 0.97.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/archive.d.ts +134 -0
- package/dist/cache/archive.js +636 -0
- package/dist/cache/index.d.ts +1 -0
- package/dist/cache/index.js +1 -0
- package/dist/cache/main.js +298 -1
- package/dist/check/gate-lists.js +8 -0
- package/dist/consumer-check-contract/evaluate.d.ts +124 -0
- package/dist/consumer-check-contract/evaluate.js +699 -0
- package/dist/consumer-check-contract/index.d.ts +5 -0
- package/dist/consumer-check-contract/index.js +5 -0
- package/dist/content-contracts/skills/helpers.d.ts +1 -1
- package/dist/content-contracts/skills/helpers.js +1 -0
- package/dist/delivery-attempt/disk-begin.d.ts +51 -0
- package/dist/delivery-attempt/disk-begin.js +68 -0
- package/dist/delivery-attempt/evaluate.d.ts +26 -0
- package/dist/delivery-attempt/evaluate.js +443 -0
- package/dist/delivery-attempt/fingerprint.d.ts +32 -0
- package/dist/delivery-attempt/fingerprint.js +100 -0
- package/dist/delivery-attempt/handoff.d.ts +25 -0
- package/dist/delivery-attempt/handoff.js +102 -0
- package/dist/delivery-attempt/index.d.ts +17 -0
- package/dist/delivery-attempt/index.js +17 -0
- package/dist/delivery-attempt/ledger.d.ts +169 -0
- package/dist/delivery-attempt/ledger.js +758 -0
- package/dist/delivery-attempt/material-delta.d.ts +38 -0
- package/dist/delivery-attempt/material-delta.js +126 -0
- package/dist/delivery-attempt/types.d.ts +210 -0
- package/dist/delivery-attempt/types.js +77 -0
- package/dist/doctor/index.d.ts +1 -0
- package/dist/doctor/index.js +1 -0
- package/dist/doctor/main.js +12 -0
- package/dist/doctor/openclaw-soft-rebind.d.ts +26 -0
- package/dist/doctor/openclaw-soft-rebind.js +164 -0
- package/dist/hooks/dispatcher.d.ts +2 -1
- package/dist/hooks/dispatcher.js +63 -9
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/init-deposit/gitignore.js +7 -0
- package/dist/init-deposit/hygiene.d.ts +1 -1
- package/dist/init-deposit/hygiene.js +16 -4
- package/dist/init-deposit/init-deposit.js +5 -0
- package/dist/init-deposit/refresh.js +3 -0
- package/dist/init-deposit/scaffold.js +6 -5
- package/dist/parent-turn-shape/evaluate.d.ts +84 -0
- package/dist/parent-turn-shape/evaluate.js +353 -0
- package/dist/parent-turn-shape/index.d.ts +8 -0
- package/dist/parent-turn-shape/index.js +8 -0
- package/dist/pr-merge-readiness/ci-gate.d.ts +29 -1
- package/dist/pr-merge-readiness/ci-gate.js +191 -24
- package/dist/pr-merge-readiness/compute.js +10 -1
- package/dist/pr-merge-readiness/index.d.ts +2 -1
- package/dist/pr-merge-readiness/index.js +2 -1
- package/dist/pr-merge-readiness/output.js +14 -0
- package/dist/pr-merge-readiness/platform-status.d.ts +29 -0
- package/dist/pr-merge-readiness/platform-status.js +49 -0
- package/dist/pr-watch/constants.d.ts +10 -0
- package/dist/pr-watch/constants.js +12 -1
- package/dist/pr-watch/main.js +16 -1
- package/dist/pr-watch/probe.js +13 -1
- package/dist/pr-watch/types.d.ts +3 -2
- package/dist/pr-watch/watch.js +14 -7
- package/dist/review-monitor/constants.js +3 -2
- package/dist/review-monitor/tier-detection.d.ts +7 -3
- package/dist/review-monitor/tier-detection.js +18 -1
- package/dist/review-monitor/verify.js +18 -0
- package/dist/scope/index.d.ts +2 -0
- package/dist/scope/index.js +2 -0
- package/dist/scope/main.d.ts +10 -0
- package/dist/scope/main.js +109 -24
- package/dist/scope/promote-from-issue.d.ts +49 -0
- package/dist/scope/promote-from-issue.js +367 -0
- package/dist/scope/promote-path.d.ts +39 -0
- package/dist/scope/promote-path.js +105 -0
- package/dist/scope-provenance/digest.d.ts +67 -0
- package/dist/scope-provenance/digest.js +188 -0
- package/dist/scope-provenance/evaluate.d.ts +82 -0
- package/dist/scope-provenance/evaluate.js +528 -0
- package/dist/scope-provenance/index.d.ts +6 -0
- package/dist/scope-provenance/index.js +6 -0
- package/dist/session/compact-ritual.d.ts +96 -0
- package/dist/session/compact-ritual.js +237 -0
- package/dist/session/compact-ritual.spec.d.ts +2 -0
- package/dist/session/compact-ritual.spec.js +21 -0
- package/dist/session/index.d.ts +2 -0
- package/dist/session/index.js +2 -0
- package/dist/session/openclaw-soft-rebind-deposit.d.ts +46 -0
- package/dist/session/openclaw-soft-rebind-deposit.js +165 -0
- package/dist/swarm/routing.d.ts +4 -2
- package/dist/swarm/routing.js +26 -4
- package/dist/test-boundary/evaluate.d.ts +54 -0
- package/dist/test-boundary/evaluate.js +368 -0
- package/dist/test-boundary/index.d.ts +6 -0
- package/dist/test-boundary/index.js +6 -0
- package/dist/test-boundary/policy.d.ts +52 -0
- package/dist/test-boundary/policy.js +182 -0
- package/dist/triage/actions/index.js +62 -2
- package/dist/triage/actions/types.d.ts +8 -1
- package/dist/triage/author-filter.d.ts +51 -0
- package/dist/triage/author-filter.js +152 -0
- package/dist/triage/bootstrap/gitignore.d.ts +1 -1
- package/dist/triage/bootstrap/gitignore.js +15 -1
- package/dist/triage/classify/index.d.ts +2 -2
- package/dist/triage/classify/index.js +2 -2
- package/dist/triage/classify/label-mirror.d.ts +68 -5
- package/dist/triage/classify/label-mirror.js +261 -31
- package/dist/triage/help/registry-data.d.ts +49 -38
- package/dist/triage/help/registry-data.js +115 -40
- package/dist/triage/index.d.ts +1 -0
- package/dist/triage/index.js +1 -0
- package/dist/triage/queue/index.d.ts +1 -0
- package/dist/triage/queue/index.js +1 -0
- package/dist/triage/queue/render.d.ts +2 -0
- package/dist/triage/queue/render.js +6 -0
- package/dist/vbrief-activate/activate.js +22 -6
- package/dist/xbrief/styles.js +33 -17
- package/package.json +11 -3
|
@@ -1,45 +1,137 @@
|
|
|
1
1
|
import { classifyCapacityStalledRequired, DEFAULT_CAPACITY_STALL_MS, } from "./runner-capacity-stall.js";
|
|
2
|
-
|
|
2
|
+
/** Real product/test failures (distinct from cancelled primaries, #3167). */
|
|
3
|
+
const FAILURE_CONCLUSIONS = new Set(["failure", "timed_out"]);
|
|
4
|
+
/** Capacity/outage cancels where failover may have been skipped (#3167). */
|
|
5
|
+
const CANCELLED_CONCLUSIONS = new Set(["cancelled"]);
|
|
3
6
|
const PENDING_STATUSES = new Set(["queued", "in_progress"]);
|
|
7
|
+
/** Non-blocking completed conclusions for general ready/pending accounting. */
|
|
8
|
+
const SUCCESS_CONCLUSIONS = new Set(["success", "neutral", "skipped"]);
|
|
9
|
+
/**
|
|
10
|
+
* Only true success clears a cancelled suite lane. `skipped` / `neutral` are
|
|
11
|
+
* not executed green failover evidence (#3167 Greptile P1).
|
|
12
|
+
*/
|
|
13
|
+
const AUTHORITATIVE_CLEAR_CONCLUSIONS = new Set(["success"]);
|
|
14
|
+
/**
|
|
15
|
+
* Bot-reviewer check-runs are not CI workflow jobs. Presence of only these
|
|
16
|
+
* (or empty check-runs) is the `ci_never_scheduled` weather signal (#3167).
|
|
17
|
+
*/
|
|
18
|
+
export function isBotReviewCheck(name) {
|
|
19
|
+
const n = name.toLowerCase();
|
|
20
|
+
return n.includes("greptile") || n.includes("slizard") || n.includes("coderabbit");
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Suite family for cancelled↔green sibling matching (#3167 / Greptile P1).
|
|
24
|
+
* Unrelated greens (CodeQL, Socket, …) must not clear a cancelled TypeScript/Go lane.
|
|
25
|
+
* Returns null when the check is not a TS/Go CI suite job.
|
|
26
|
+
*/
|
|
27
|
+
export function suiteFamilyOf(name) {
|
|
28
|
+
const n = name.toLowerCase();
|
|
29
|
+
if (n.includes("typescript") || n.includes("type script")) {
|
|
30
|
+
return "typescript";
|
|
31
|
+
}
|
|
32
|
+
// "Go (…)" primary/failover/aggregator; avoid bare "go" false positives (e.g. CodeQL go analyze).
|
|
33
|
+
if (/\bgo\s*\(/.test(n) || n.startsWith("go ") || n === "go") {
|
|
34
|
+
return "go";
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Branch-protection SoT aggregators only (#3168 map). Primary/failover lanes are
|
|
40
|
+
* not authoritative: a green primary must not clear a cancelled aggregator.
|
|
41
|
+
*/
|
|
42
|
+
export function isAuthoritativeSuiteAggregator(name) {
|
|
43
|
+
const n = name.toLowerCase().trim();
|
|
44
|
+
if (n.includes("primary") || n.includes("failover") || n.includes("blacksmith")) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
if (n === "typescript (build + lint + test)") {
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
if (n === "go (test + build)") {
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
// Tolerate minor naming drift while excluding lane suffixes.
|
|
54
|
+
if (suiteFamilyOf(name) === "typescript" &&
|
|
55
|
+
n.includes("build") &&
|
|
56
|
+
n.includes("lint") &&
|
|
57
|
+
n.includes("test")) {
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
if (suiteFamilyOf(name) === "go" && n.includes("test") && n.includes("build")) {
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
4
65
|
function isPending(status, conclusion) {
|
|
5
66
|
if (PENDING_STATUSES.has(status)) {
|
|
6
67
|
return true;
|
|
7
68
|
}
|
|
8
69
|
return status !== "completed" || conclusion === "none";
|
|
9
70
|
}
|
|
71
|
+
function emptySummary(readyState, ignored = []) {
|
|
72
|
+
return {
|
|
73
|
+
ready_state: readyState,
|
|
74
|
+
checked_count: 0,
|
|
75
|
+
ignored_checks: ignored,
|
|
76
|
+
failed_required: [],
|
|
77
|
+
pending_required: [],
|
|
78
|
+
capacity_stalled_required: [],
|
|
79
|
+
cancelled_required: [],
|
|
80
|
+
conclusions: [],
|
|
81
|
+
};
|
|
82
|
+
}
|
|
10
83
|
export function buildCiSummaryLine(summary) {
|
|
11
84
|
const passed = summary.checked_count - summary.failed_required.length - summary.pending_required.length;
|
|
12
85
|
const stallSuffix = summary.capacity_stalled_required.length > 0
|
|
13
86
|
? ` / ${summary.capacity_stalled_required.length} capacity-stalled`
|
|
14
87
|
: "";
|
|
88
|
+
const cancelSuffix = summary.cancelled_required.length > 0
|
|
89
|
+
? ` / ${summary.cancelled_required.length} cancelled`
|
|
90
|
+
: "";
|
|
91
|
+
const weather = summary.ready_state === "ci_never_scheduled"
|
|
92
|
+
? " (ci_never_scheduled)"
|
|
93
|
+
: summary.ready_state === "ci_cancelled_no_failover"
|
|
94
|
+
? " (ci_cancelled_no_failover)"
|
|
95
|
+
: summary.ready_state === "ci_failures"
|
|
96
|
+
? " (ci_failures)"
|
|
97
|
+
: "";
|
|
15
98
|
return (`CI check-runs: ${passed} passed / ` +
|
|
16
99
|
`${summary.failed_required.length} failed / ${summary.pending_required.length} pending` +
|
|
17
|
-
stallSuffix
|
|
100
|
+
stallSuffix +
|
|
101
|
+
cancelSuffix +
|
|
102
|
+
weather);
|
|
18
103
|
}
|
|
104
|
+
/**
|
|
105
|
+
* Classify required CI weather for a HEAD check-run list (#2169 / #2672 / #3167).
|
|
106
|
+
*
|
|
107
|
+
* Distinct ready_state codes:
|
|
108
|
+
* - `ci_never_scheduled` — no CI workflow check-runs (bots alone / empty)
|
|
109
|
+
* - `runner_capacity_stall` — queued past budget, no runner claimed
|
|
110
|
+
* - `ci_failures` — completed failure / timed_out with product evidence
|
|
111
|
+
* - `ci_cancelled_no_failover` — only cancelled failures; no green failover
|
|
112
|
+
* - `not_ready_yet` / `ready` / `blocked` / `skipped` — legacy + residual
|
|
113
|
+
*/
|
|
19
114
|
export function evaluateCiGate(checkRuns, options = {}) {
|
|
20
115
|
if (options.skipCi === true) {
|
|
21
116
|
return {
|
|
22
117
|
failures: [],
|
|
23
|
-
summary:
|
|
24
|
-
ready_state: "skipped",
|
|
25
|
-
checked_count: 0,
|
|
26
|
-
ignored_checks: [],
|
|
27
|
-
failed_required: [],
|
|
28
|
-
pending_required: [],
|
|
29
|
-
capacity_stalled_required: [],
|
|
30
|
-
conclusions: [],
|
|
31
|
-
},
|
|
118
|
+
summary: emptySummary("skipped"),
|
|
32
119
|
};
|
|
33
120
|
}
|
|
34
121
|
const ignoredSet = new Set(options.ignoreCheckNames ?? []);
|
|
35
122
|
const ignoredChecks = [];
|
|
36
|
-
const
|
|
123
|
+
const failedProduct = [];
|
|
124
|
+
const cancelledRequired = [];
|
|
37
125
|
const pendingRequired = [];
|
|
38
126
|
const pendingProbes = [];
|
|
39
127
|
const conclusions = [];
|
|
128
|
+
let workflowCheckCount = 0;
|
|
129
|
+
let operatorIgnoredOnly = false;
|
|
40
130
|
for (const run of checkRuns) {
|
|
41
|
-
const
|
|
42
|
-
|
|
131
|
+
const botReview = isBotReviewCheck(run.name);
|
|
132
|
+
const operatorIgnored = ignoredSet.has(run.name);
|
|
133
|
+
const ignored = operatorIgnored || botReview;
|
|
134
|
+
if (operatorIgnored) {
|
|
43
135
|
ignoredChecks.push(run.name);
|
|
44
136
|
}
|
|
45
137
|
const required = !ignored;
|
|
@@ -53,8 +145,16 @@ export function evaluateCiGate(checkRuns, options = {}) {
|
|
|
53
145
|
if (!required) {
|
|
54
146
|
continue;
|
|
55
147
|
}
|
|
56
|
-
|
|
57
|
-
|
|
148
|
+
workflowCheckCount += 1;
|
|
149
|
+
if (run.status === "completed" && SUCCESS_CONCLUSIONS.has(run.conclusion)) {
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
if (FAILURE_CONCLUSIONS.has(run.conclusion)) {
|
|
153
|
+
failedProduct.push(`${run.name} (${run.conclusion})`);
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
if (CANCELLED_CONCLUSIONS.has(run.conclusion)) {
|
|
157
|
+
cancelledRequired.push(`${run.name} (${run.conclusion})`);
|
|
58
158
|
continue;
|
|
59
159
|
}
|
|
60
160
|
if (isPending(run.status, run.conclusion)) {
|
|
@@ -62,23 +162,48 @@ export function evaluateCiGate(checkRuns, options = {}) {
|
|
|
62
162
|
pendingProbes.push(run);
|
|
63
163
|
}
|
|
64
164
|
}
|
|
165
|
+
// Operator --ci-ignore-check on every non-bot run is intentional, not weather.
|
|
166
|
+
operatorIgnoredOnly =
|
|
167
|
+
workflowCheckCount === 0 &&
|
|
168
|
+
checkRuns.some((r) => ignoredSet.has(r.name) && !isBotReviewCheck(r.name));
|
|
169
|
+
// #3167: no workflow CI check-runs at all (empty list or bots-only) → never scheduled.
|
|
170
|
+
// Distinct from "all remaining checks operator-ignored" (legacy ready).
|
|
171
|
+
if (workflowCheckCount === 0 && !operatorIgnoredOnly) {
|
|
172
|
+
return {
|
|
173
|
+
failures: [
|
|
174
|
+
"Required CI workflow check-runs never scheduled (ci_never_scheduled): " +
|
|
175
|
+
"no non-bot check-run for the current HEAD. " +
|
|
176
|
+
"Do not multi-hour empty-commit thrash; emit BLOCKED after thrash caps (#3167).",
|
|
177
|
+
],
|
|
178
|
+
summary: {
|
|
179
|
+
ready_state: "ci_never_scheduled",
|
|
180
|
+
checked_count: 0,
|
|
181
|
+
ignored_checks: ignoredChecks,
|
|
182
|
+
failed_required: [],
|
|
183
|
+
pending_required: [],
|
|
184
|
+
capacity_stalled_required: [],
|
|
185
|
+
cancelled_required: [],
|
|
186
|
+
conclusions,
|
|
187
|
+
},
|
|
188
|
+
};
|
|
189
|
+
}
|
|
65
190
|
const stallOpts = {
|
|
66
191
|
budgetMs: options.capacityStallBudgetMs ?? DEFAULT_CAPACITY_STALL_MS,
|
|
67
192
|
nowMs: options.nowMs,
|
|
68
193
|
};
|
|
69
194
|
const capacityStalledRequired = classifyCapacityStalledRequired(pendingProbes, stallOpts);
|
|
70
195
|
const failures = [];
|
|
71
|
-
if (failedRequired.length > 0) {
|
|
72
|
-
failures.push(`Required CI check-runs failed: ${failedRequired.join(", ")}. ` +
|
|
73
|
-
"Required checks fail closed by default (#2169).");
|
|
74
|
-
}
|
|
75
196
|
let readyState;
|
|
76
|
-
|
|
77
|
-
|
|
197
|
+
// Blocking failures listed for merge-ready / clean-gate (not diagnostic-only cancels).
|
|
198
|
+
let failedRequired = [];
|
|
199
|
+
if (failedProduct.length > 0) {
|
|
200
|
+
readyState = "ci_failures";
|
|
201
|
+
failedRequired = [...failedProduct, ...cancelledRequired];
|
|
202
|
+
failures.push(`Required CI check-runs failed (ci_failures): ${failedProduct.join(", ")}. ` +
|
|
203
|
+
"Required checks fail closed by default (#2169 / #3167).");
|
|
78
204
|
}
|
|
79
205
|
else if (pendingRequired.length > 0) {
|
|
80
|
-
//
|
|
81
|
-
// budget — distinct from ordinary not_ready_yet (under budget / in_progress).
|
|
206
|
+
// Pending wins over cancelled siblings (failover may still be arming).
|
|
82
207
|
const allPendingStalled = capacityStalledRequired.length > 0 && capacityStalledRequired.length === pendingProbes.length;
|
|
83
208
|
if (allPendingStalled) {
|
|
84
209
|
readyState = "runner_capacity_stall";
|
|
@@ -93,6 +218,47 @@ export function evaluateCiGate(checkRuns, options = {}) {
|
|
|
93
218
|
"Wait for required checks to finish before merge.");
|
|
94
219
|
}
|
|
95
220
|
}
|
|
221
|
+
else if (cancelledRequired.length > 0) {
|
|
222
|
+
// Cancelled suite jobs clear only when a NON-IGNORED AUTHORITATIVE aggregator
|
|
223
|
+
// for that suite family is green (#3167 Greptile P1s):
|
|
224
|
+
// - Unrelated greens (CodeQL, Socket) never clear
|
|
225
|
+
// - Green primary/failover never clear a cancelled aggregator
|
|
226
|
+
// - Operator-ignored aggregator never clears
|
|
227
|
+
// - Green aggregator clears cancelled primary/failover (failover path done)
|
|
228
|
+
// - Non-suite required cancellations always block (mixed cancel)
|
|
229
|
+
// Strip trailing " (conclusion)" without nested-paren regex (CodeQL js/polynomial-redos).
|
|
230
|
+
const stripConclusionParen = (label) => {
|
|
231
|
+
const open = label.lastIndexOf(" (");
|
|
232
|
+
if (open < 0 || !label.endsWith(")")) {
|
|
233
|
+
return label;
|
|
234
|
+
}
|
|
235
|
+
return label.slice(0, open);
|
|
236
|
+
};
|
|
237
|
+
const cancelledNonSuite = cancelledRequired.filter((label) => suiteFamilyOf(stripConclusionParen(label)) === null);
|
|
238
|
+
const cancelledFamilies = new Set(cancelledRequired
|
|
239
|
+
.map((label) => suiteFamilyOf(stripConclusionParen(label)))
|
|
240
|
+
.filter((f) => f !== null));
|
|
241
|
+
const greenAuthoritativeFamilies = new Set(conclusions
|
|
242
|
+
.filter((c) => c.required &&
|
|
243
|
+
!c.ignored &&
|
|
244
|
+
c.status === "completed" &&
|
|
245
|
+
AUTHORITATIVE_CLEAR_CONCLUSIONS.has(c.conclusion) &&
|
|
246
|
+
isAuthoritativeSuiteAggregator(c.name))
|
|
247
|
+
.map((c) => suiteFamilyOf(c.name)));
|
|
248
|
+
const unclearedFamilies = [...cancelledFamilies].filter((f) => !greenAuthoritativeFamilies.has(f));
|
|
249
|
+
if (unclearedFamilies.length > 0 || cancelledNonSuite.length > 0) {
|
|
250
|
+
readyState = "ci_cancelled_no_failover";
|
|
251
|
+
failedRequired = [...cancelledRequired];
|
|
252
|
+
failures.push(`Required CI check-runs cancelled without failover (ci_cancelled_no_failover): ` +
|
|
253
|
+
`${cancelledRequired.join(", ")}. ` +
|
|
254
|
+
"Cancelled required check with no green non-ignored authoritative suite aggregator (#3167 / #3168). " +
|
|
255
|
+
"Do not multi-hour re-push thrash; BLOCKED after thrash caps.");
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
// Every cancelled suite family has a green non-ignored aggregator; no non-suite cancels.
|
|
259
|
+
readyState = "ready";
|
|
260
|
+
}
|
|
261
|
+
}
|
|
96
262
|
else {
|
|
97
263
|
readyState = "ready";
|
|
98
264
|
}
|
|
@@ -105,6 +271,7 @@ export function evaluateCiGate(checkRuns, options = {}) {
|
|
|
105
271
|
failed_required: failedRequired,
|
|
106
272
|
pending_required: pendingRequired,
|
|
107
273
|
capacity_stalled_required: capacityStalledRequired,
|
|
274
|
+
cancelled_required: cancelledRequired,
|
|
108
275
|
conclusions,
|
|
109
276
|
},
|
|
110
277
|
};
|
|
@@ -6,6 +6,7 @@ import { fetchCheckRunsRest, fetchGreptileBodyRest, fetchGreptileCommentBody, fe
|
|
|
6
6
|
import { fetchUnresolvedGreptileInlineFindings, inlineFindingsToDict, } from "./greptile-inline.js";
|
|
7
7
|
import { fetchMergeability, isGithubMergeableClean, mergeabilityToDict, verdictBlockIsSoftOnly, verdictShaIsStale, } from "./mergeability.js";
|
|
8
8
|
import { emptyVerdict, parseGreptileBody } from "./parse.js";
|
|
9
|
+
import { attachPlatformStatusUrls } from "./platform-status.js";
|
|
9
10
|
import { evaluateSlizardGate, isSlizardCheck } from "./slizard-gate.js";
|
|
10
11
|
function buildGateResult(prNumber, repo, headSha, body, via, partialData = {}, error = null, inline = null, minConfidence) {
|
|
11
12
|
const verdict = parseGreptileBody(body);
|
|
@@ -40,9 +41,15 @@ function evaluateCiAndSlizard(checkRuns, options) {
|
|
|
40
41
|
ignoreCheckNames: [...(options.ignoreCheckNames ?? []), ...slizardNames],
|
|
41
42
|
};
|
|
42
43
|
const ciResult = evaluateCiGate(checkRuns, ciOptions);
|
|
44
|
+
const ci = {
|
|
45
|
+
...ciResult.summary,
|
|
46
|
+
summary_line: buildCiSummaryLine(ciResult.summary),
|
|
47
|
+
};
|
|
48
|
+
// #3180: static status URLs when weather-class (no network fetch).
|
|
49
|
+
attachPlatformStatusUrls(ci, ciResult.summary.ready_state);
|
|
43
50
|
return {
|
|
44
51
|
failures: [...ciResult.failures, ...slizardResult.failures],
|
|
45
|
-
ci
|
|
52
|
+
ci,
|
|
46
53
|
slizard: { ...slizardResult.summary },
|
|
47
54
|
};
|
|
48
55
|
}
|
|
@@ -85,6 +92,8 @@ function applyCiGateForHead(repo, headSha, runGh, options) {
|
|
|
85
92
|
ignored_checks: [...(options.ignoreCheckNames ?? [])],
|
|
86
93
|
failed_required: [],
|
|
87
94
|
pending_required: [],
|
|
95
|
+
capacity_stalled_required: [],
|
|
96
|
+
cancelled_required: [],
|
|
88
97
|
conclusions: [],
|
|
89
98
|
},
|
|
90
99
|
slizard: {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { buildCiSummaryLine, type CiCheckConclusion, type CiGateOptions, type CiGateResult, type CiGateSummary, type CiReadyState, evaluateCiGate, } from "./ci-gate.js";
|
|
1
|
+
export { buildCiSummaryLine, type CiCheckConclusion, type CiGateOptions, type CiGateResult, type CiGateSummary, type CiReadyState, evaluateCiGate, isAuthoritativeSuiteAggregator, isBotReviewCheck, suiteFamilyOf, } 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 { attachPlatformStatusUrls, CI_WEATHER_READY_STATES, type CiWeatherReadyState, isCiWeatherReadyState, PLATFORM_STATUS_BLACKSMITH_URL, PLATFORM_STATUS_GITHUB_URL, platformStatusUrlsForWeather, } from "./platform-status.js";
|
|
11
12
|
export { type CapacityStallOptions, type CapacityStallProbe, classifyCapacityStalledRequired, DEFAULT_CAPACITY_STALL_MS, isRunnerCapacityStalled, } from "./runner-capacity-stall.js";
|
|
12
13
|
export type { SlizardGateOptions, SlizardGateResult, SlizardGateSummary, SlizardVerdict, } from "./slizard-gate.js";
|
|
13
14
|
export { evaluateSlizardGate, isSlizardCheck, parseSlizardVerdict, SLIZARD_CHECK_NAME, } from "./slizard-gate.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { buildCiSummaryLine, evaluateCiGate, } from "./ci-gate.js";
|
|
1
|
+
export { buildCiSummaryLine, evaluateCiGate, isAuthoritativeSuiteAggregator, isBotReviewCheck, suiteFamilyOf, } 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,6 +8,7 @@ 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 { attachPlatformStatusUrls, CI_WEATHER_READY_STATES, isCiWeatherReadyState, PLATFORM_STATUS_BLACKSMITH_URL, PLATFORM_STATUS_GITHUB_URL, platformStatusUrlsForWeather, } from "./platform-status.js";
|
|
11
12
|
export { classifyCapacityStalledRequired, DEFAULT_CAPACITY_STALL_MS, isRunnerCapacityStalled, } from "./runner-capacity-stall.js";
|
|
12
13
|
export { evaluateSlizardGate, isSlizardCheck, parseSlizardVerdict, SLIZARD_CHECK_NAME, } from "./slizard-gate.js";
|
|
13
14
|
//# sourceMappingURL=index.js.map
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { EXIT_EXTERNAL_ERROR, EXIT_MERGE_BLOCKED, EXIT_OK, VIA_ERROR, VIA_FALLBACK2, } from "./constants.js";
|
|
2
2
|
import { isMergeReady } from "./evaluate.js";
|
|
3
|
+
import { isCiWeatherReadyState, PLATFORM_STATUS_BLACKSMITH_URL, PLATFORM_STATUS_GITHUB_URL, } from "./platform-status.js";
|
|
3
4
|
function verdictToDict(verdict) {
|
|
4
5
|
return {
|
|
5
6
|
found: verdict.found,
|
|
@@ -68,6 +69,19 @@ export function printHuman(result) {
|
|
|
68
69
|
else if (typeof ci.ready_state === "string") {
|
|
69
70
|
lines.push(` CI check-runs: ${ci.ready_state}`);
|
|
70
71
|
}
|
|
72
|
+
// #3180: surface static status URLs on weather-class CI (human + JSON partial_data).
|
|
73
|
+
const readyState = typeof ci.ready_state === "string" ? ci.ready_state : null;
|
|
74
|
+
if (isCiWeatherReadyState(readyState)) {
|
|
75
|
+
const gh = typeof ci.platform_status_github === "string"
|
|
76
|
+
? ci.platform_status_github
|
|
77
|
+
: PLATFORM_STATUS_GITHUB_URL;
|
|
78
|
+
const bs = typeof ci.platform_status_blacksmith === "string"
|
|
79
|
+
? ci.platform_status_blacksmith
|
|
80
|
+
: PLATFORM_STATUS_BLACKSMITH_URL;
|
|
81
|
+
lines.push(` Platform status GH: ${gh}`);
|
|
82
|
+
lines.push(` Platform status BS: ${bs}`);
|
|
83
|
+
lines.push(" Probe status pages before workflow edits (#3180)");
|
|
84
|
+
}
|
|
71
85
|
}
|
|
72
86
|
const slizardBlock = result.partialData.slizard;
|
|
73
87
|
if (slizardBlock !== null && typeof slizardBlock === "object" && !Array.isArray(slizardBlock)) {
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Static platform status page URLs for CI weather attribution (#3180).
|
|
3
|
+
*
|
|
4
|
+
* v1 surfaces URLs only — no network fetch. Agents MUST open these pages when
|
|
5
|
+
* weather-class `ci_ready_state` fires (or CI never starts for HEAD) and apply
|
|
6
|
+
* the attribution table in scm/github.md + review-cycle.
|
|
7
|
+
*/
|
|
8
|
+
/** Public GitHub Status (Actions / Webhooks components). */
|
|
9
|
+
export declare const PLATFORM_STATUS_GITHUB_URL = "https://www.githubstatus.com/";
|
|
10
|
+
/** Public Blacksmith Status (runner provider; may mirror GH Actions health). */
|
|
11
|
+
export declare const PLATFORM_STATUS_BLACKSMITH_URL = "https://status.blacksmith.sh/";
|
|
12
|
+
/**
|
|
13
|
+
* Weather-class ready_state values from evaluateCiGate (#3167).
|
|
14
|
+
* Status URLs are attached only for these (not ordinary ready / not_ready_yet).
|
|
15
|
+
*/
|
|
16
|
+
export declare const CI_WEATHER_READY_STATES: readonly ["ci_never_scheduled", "runner_capacity_stall", "ci_failures", "ci_cancelled_no_failover"];
|
|
17
|
+
export type CiWeatherReadyState = (typeof CI_WEATHER_READY_STATES)[number];
|
|
18
|
+
export declare function isCiWeatherReadyState(state: string | null | undefined): state is CiWeatherReadyState;
|
|
19
|
+
/** Static status URL fields for weather-class states; null when not weather. */
|
|
20
|
+
export declare function platformStatusUrlsForWeather(state: string | null | undefined): {
|
|
21
|
+
platform_status_github: string;
|
|
22
|
+
platform_status_blacksmith: string;
|
|
23
|
+
} | null;
|
|
24
|
+
/**
|
|
25
|
+
* Attach static status URLs onto a CI partial/summary record when weather-class.
|
|
26
|
+
* Mutates and returns the same object for call-site chaining.
|
|
27
|
+
*/
|
|
28
|
+
export declare function attachPlatformStatusUrls(ci: Record<string, unknown>, readyState?: string | null): Record<string, unknown>;
|
|
29
|
+
//# sourceMappingURL=platform-status.d.ts.map
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Static platform status page URLs for CI weather attribution (#3180).
|
|
3
|
+
*
|
|
4
|
+
* v1 surfaces URLs only — no network fetch. Agents MUST open these pages when
|
|
5
|
+
* weather-class `ci_ready_state` fires (or CI never starts for HEAD) and apply
|
|
6
|
+
* the attribution table in scm/github.md + review-cycle.
|
|
7
|
+
*/
|
|
8
|
+
/** Public GitHub Status (Actions / Webhooks components). */
|
|
9
|
+
export const PLATFORM_STATUS_GITHUB_URL = "https://www.githubstatus.com/";
|
|
10
|
+
/** Public Blacksmith Status (runner provider; may mirror GH Actions health). */
|
|
11
|
+
export const PLATFORM_STATUS_BLACKSMITH_URL = "https://status.blacksmith.sh/";
|
|
12
|
+
/**
|
|
13
|
+
* Weather-class ready_state values from evaluateCiGate (#3167).
|
|
14
|
+
* Status URLs are attached only for these (not ordinary ready / not_ready_yet).
|
|
15
|
+
*/
|
|
16
|
+
export const CI_WEATHER_READY_STATES = [
|
|
17
|
+
"ci_never_scheduled",
|
|
18
|
+
"runner_capacity_stall",
|
|
19
|
+
"ci_failures",
|
|
20
|
+
"ci_cancelled_no_failover",
|
|
21
|
+
];
|
|
22
|
+
const WEATHER_SET = new Set(CI_WEATHER_READY_STATES);
|
|
23
|
+
export function isCiWeatherReadyState(state) {
|
|
24
|
+
return state != null && WEATHER_SET.has(state);
|
|
25
|
+
}
|
|
26
|
+
/** Static status URL fields for weather-class states; null when not weather. */
|
|
27
|
+
export function platformStatusUrlsForWeather(state) {
|
|
28
|
+
if (!isCiWeatherReadyState(state)) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
platform_status_github: PLATFORM_STATUS_GITHUB_URL,
|
|
33
|
+
platform_status_blacksmith: PLATFORM_STATUS_BLACKSMITH_URL,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Attach static status URLs onto a CI partial/summary record when weather-class.
|
|
38
|
+
* Mutates and returns the same object for call-site chaining.
|
|
39
|
+
*/
|
|
40
|
+
export function attachPlatformStatusUrls(ci, readyState) {
|
|
41
|
+
const state = readyState ?? (typeof ci.ready_state === "string" ? ci.ready_state : null);
|
|
42
|
+
const urls = platformStatusUrlsForWeather(state);
|
|
43
|
+
if (urls !== null) {
|
|
44
|
+
ci.platform_status_github = urls.platform_status_github;
|
|
45
|
+
ci.platform_status_blacksmith = urls.platform_status_blacksmith;
|
|
46
|
+
}
|
|
47
|
+
return ci;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=platform-status.js.map
|
|
@@ -30,6 +30,16 @@ export declare const VERDICT_CI_BLOCKED = "CI_BLOCKED";
|
|
|
30
30
|
* claimed (#2672). Exit 2 — wait for auto-failover; never --skip-ci.
|
|
31
31
|
*/
|
|
32
32
|
export declare const VERDICT_RUNNER_CAPACITY_STALL = "RUNNER_CAPACITY_STALL";
|
|
33
|
+
/**
|
|
34
|
+
* No CI workflow check-run scheduled for HEAD (bots-only or empty) (#3167).
|
|
35
|
+
* Exit 2 — thrash-cap then BLOCKED; do not multi-hour empty-commit loops.
|
|
36
|
+
*/
|
|
37
|
+
export declare const VERDICT_CI_NEVER_SCHEDULED = "CI_NEVER_SCHEDULED";
|
|
38
|
+
/**
|
|
39
|
+
* Primary CI cancelled and no green required sibling / failover (#3167).
|
|
40
|
+
* Exit 2 — thrash-cap then BLOCKED; workflow arming is sibling #3168.
|
|
41
|
+
*/
|
|
42
|
+
export declare const VERDICT_CI_CANCELLED_NO_FAILOVER = "CI_CANCELLED_NO_FAILOVER";
|
|
33
43
|
/** --one-shot only: a single probe with no terminal verdict yet. */
|
|
34
44
|
export declare const VERDICT_PENDING = "PENDING";
|
|
35
45
|
/** External/config fault mid-probe (unresolvable repo/HEAD, gh unavailable). */
|
|
@@ -30,6 +30,16 @@ export const VERDICT_CI_BLOCKED = "CI_BLOCKED";
|
|
|
30
30
|
* claimed (#2672). Exit 2 — wait for auto-failover; never --skip-ci.
|
|
31
31
|
*/
|
|
32
32
|
export const VERDICT_RUNNER_CAPACITY_STALL = "RUNNER_CAPACITY_STALL";
|
|
33
|
+
/**
|
|
34
|
+
* No CI workflow check-run scheduled for HEAD (bots-only or empty) (#3167).
|
|
35
|
+
* Exit 2 — thrash-cap then BLOCKED; do not multi-hour empty-commit loops.
|
|
36
|
+
*/
|
|
37
|
+
export const VERDICT_CI_NEVER_SCHEDULED = "CI_NEVER_SCHEDULED";
|
|
38
|
+
/**
|
|
39
|
+
* Primary CI cancelled and no green required sibling / failover (#3167).
|
|
40
|
+
* Exit 2 — thrash-cap then BLOCKED; workflow arming is sibling #3168.
|
|
41
|
+
*/
|
|
42
|
+
export const VERDICT_CI_CANCELLED_NO_FAILOVER = "CI_CANCELLED_NO_FAILOVER";
|
|
33
43
|
/** --one-shot only: a single probe with no terminal verdict yet. */
|
|
34
44
|
export const VERDICT_PENDING = "PENDING";
|
|
35
45
|
/** External/config fault mid-probe (unresolvable repo/HEAD, gh unavailable). */
|
|
@@ -62,7 +72,8 @@ export const WATCH_HELP = "usage: task pr:watch -- <pr_number> [options]\n" +
|
|
|
62
72
|
"exit codes:\n" +
|
|
63
73
|
" 0 CLEAN SHA-matched review, confidence >= policy min (default 4; dogfood 5), no P0/P1, CI green\n" +
|
|
64
74
|
" 1 NEW_P0_P1 Blocking findings on the current (SHA-matched) review\n" +
|
|
65
|
-
" 2 ERRORED | STALL | TIMEOUT | CI_BLOCKED | RUNNER_CAPACITY_STALL
|
|
75
|
+
" 2 ERRORED | STALL | TIMEOUT | CI_BLOCKED | RUNNER_CAPACITY_STALL |\n" +
|
|
76
|
+
" CI_NEVER_SCHEDULED | CI_CANCELLED_NO_FAILOVER | config / usage error\n";
|
|
66
77
|
/**
|
|
67
78
|
* Consecutive polls where the CLEAN gate is wedged on HEAD (!has_blocking &&
|
|
68
79
|
* !is_clean with a holdout other than sha_match) before STALL (#1039). Stale-SHA
|
package/dist/pr-watch/main.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import { defaultRunGh } from "../pr-merge-readiness/gh.js";
|
|
4
|
+
import { platformStatusUrlsForWeather } from "../pr-merge-readiness/platform-status.js";
|
|
4
5
|
import { DEFAULT_MAX_WAIT_MINUTES, DEFAULT_POLL_SECONDS, EXIT_CLEAN, EXIT_TERMINAL_ERROR, WATCH_HELP, } from "./constants.js";
|
|
5
6
|
import { watch } from "./watch.js";
|
|
6
7
|
function fail(base, error) {
|
|
@@ -140,7 +141,7 @@ function pythonJsonDumps(value) {
|
|
|
140
141
|
/** AC-4 stable --json shape (same field set as the #1039 Tier-1 instrumentation line). */
|
|
141
142
|
export function watchResultToJson(result) {
|
|
142
143
|
const p = result.probe;
|
|
143
|
-
|
|
144
|
+
const payload = {
|
|
144
145
|
verdict: result.verdict,
|
|
145
146
|
pr_number: result.prNumber,
|
|
146
147
|
head_sha: p.headSha,
|
|
@@ -159,6 +160,13 @@ export function watchResultToJson(result) {
|
|
|
159
160
|
elapsed_seconds: result.elapsedSeconds,
|
|
160
161
|
poll_count: result.pollCount,
|
|
161
162
|
};
|
|
163
|
+
// #3180: static status URLs on weather-class ci_ready_state (no network fetch).
|
|
164
|
+
const statusUrls = platformStatusUrlsForWeather(p.ciReadyState);
|
|
165
|
+
if (statusUrls !== null) {
|
|
166
|
+
payload.platform_status_github = statusUrls.platform_status_github;
|
|
167
|
+
payload.platform_status_blacksmith = statusUrls.platform_status_blacksmith;
|
|
168
|
+
}
|
|
169
|
+
return payload;
|
|
162
170
|
}
|
|
163
171
|
export function emitWatchJson(result) {
|
|
164
172
|
return `${pythonJsonDumps(watchResultToJson(result))}\n`;
|
|
@@ -183,6 +191,13 @@ export function printWatchHuman(result) {
|
|
|
183
191
|
if (p.ciCapacityStalledChecks.length > 0) {
|
|
184
192
|
lines.push(` Capacity-stalled: ${p.ciCapacityStalledChecks.join("; ")}`);
|
|
185
193
|
}
|
|
194
|
+
// #3180: probe these pages before workflow thrash on weather states.
|
|
195
|
+
const statusUrls = platformStatusUrlsForWeather(p.ciReadyState);
|
|
196
|
+
if (statusUrls !== null) {
|
|
197
|
+
lines.push(` Platform status GH: ${statusUrls.platform_status_github}`);
|
|
198
|
+
lines.push(` Platform status BS: ${statusUrls.platform_status_blacksmith}`);
|
|
199
|
+
lines.push(" Probe status pages before workflow edits (#3180)");
|
|
200
|
+
}
|
|
186
201
|
if (p.cleanGateHoldout !== null) {
|
|
187
202
|
lines.push(` Clean-gate holdout: ${p.cleanGateHoldout}`);
|
|
188
203
|
}
|
package/dist/pr-watch/probe.js
CHANGED
|
@@ -82,7 +82,7 @@ export function probeOnce(prNumber, repoArg, runGh = defaultRunGh, projectRoot =
|
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
const shaMatch = lastReviewedSha !== null && lastReviewedSha === headSha;
|
|
85
|
-
|
|
85
|
+
let [isClean, cleanGateHoldout] = evaluateCleanGate({
|
|
86
86
|
lastReviewedSha,
|
|
87
87
|
headSha,
|
|
88
88
|
hasBlocking: findings.has_blocking,
|
|
@@ -92,6 +92,18 @@ export function probeOnce(prNumber, repoArg, runGh = defaultRunGh, projectRoot =
|
|
|
92
92
|
terminalCheckRun,
|
|
93
93
|
minConfidence,
|
|
94
94
|
});
|
|
95
|
+
// #3167: weather not-ready states must never surface as CLEAN even when the
|
|
96
|
+
// Greptile side of the clean gate is satisfied (empty CI was previously ready).
|
|
97
|
+
if (isClean &&
|
|
98
|
+
(ciReadyState === "ci_never_scheduled" ||
|
|
99
|
+
ciReadyState === "runner_capacity_stall" ||
|
|
100
|
+
ciReadyState === "ci_cancelled_no_failover" ||
|
|
101
|
+
ciReadyState === "ci_failures" ||
|
|
102
|
+
ciReadyState === "blocked" ||
|
|
103
|
+
ciReadyState === "not_ready_yet")) {
|
|
104
|
+
isClean = false;
|
|
105
|
+
cleanGateHoldout = ciReadyState === "not_ready_yet" ? "terminal_check_run" : ciReadyState;
|
|
106
|
+
}
|
|
95
107
|
return {
|
|
96
108
|
found,
|
|
97
109
|
headSha,
|
package/dist/pr-watch/types.d.ts
CHANGED
|
@@ -16,8 +16,9 @@ export interface WatchProbe {
|
|
|
16
16
|
/** Failed required check identities (name + conclusion), when CI was probed. */
|
|
17
17
|
readonly ciFailedChecks: readonly string[];
|
|
18
18
|
/**
|
|
19
|
-
* CI gate ready_state from evaluateCiGate (#2169 / #2672).
|
|
20
|
-
*
|
|
19
|
+
* CI gate ready_state from evaluateCiGate (#2169 / #2672 / #3167).
|
|
20
|
+
* Weather codes: `ci_never_scheduled`, `runner_capacity_stall`,
|
|
21
|
+
* `ci_failures`, `ci_cancelled_no_failover` (plus `not_ready_yet` / `ready`).
|
|
21
22
|
*/
|
|
22
23
|
readonly ciReadyState: string | null;
|
|
23
24
|
/** Required checks classified as capacity-stalled (#2672). */
|
package/dist/pr-watch/watch.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { defaultRunGh } from "../pr-merge-readiness/gh.js";
|
|
2
|
-
import { DEFAULT_CI_BLOCKED_THRESHOLD, DEFAULT_MAX_WAIT_MINUTES, DEFAULT_POLL_SECONDS, DEFAULT_STALL_THRESHOLD, EXIT_CLEAN, EXIT_NEW_P0_P1, EXIT_TERMINAL_ERROR, VERDICT_CI_BLOCKED, VERDICT_CLEAN, VERDICT_CONFIG, VERDICT_ERRORED, VERDICT_NEW_P0_P1, VERDICT_PENDING, VERDICT_RUNNER_CAPACITY_STALL, VERDICT_STALL, VERDICT_TIMEOUT, } from "./constants.js";
|
|
2
|
+
import { DEFAULT_CI_BLOCKED_THRESHOLD, DEFAULT_MAX_WAIT_MINUTES, DEFAULT_POLL_SECONDS, DEFAULT_STALL_THRESHOLD, EXIT_CLEAN, EXIT_NEW_P0_P1, EXIT_TERMINAL_ERROR, VERDICT_CI_BLOCKED, VERDICT_CI_CANCELLED_NO_FAILOVER, VERDICT_CI_NEVER_SCHEDULED, VERDICT_CLEAN, VERDICT_CONFIG, VERDICT_ERRORED, VERDICT_NEW_P0_P1, VERDICT_PENDING, VERDICT_RUNNER_CAPACITY_STALL, VERDICT_STALL, VERDICT_TIMEOUT, } from "./constants.js";
|
|
3
3
|
import { probeOnce } from "./probe.js";
|
|
4
4
|
const systemMonotonicClock = {
|
|
5
5
|
now() {
|
|
@@ -81,20 +81,27 @@ export function watch(prNumber, repo, options = {}) {
|
|
|
81
81
|
if (probe.errored) {
|
|
82
82
|
return build(VERDICT_ERRORED, EXIT_TERMINAL_ERROR, probe, poll);
|
|
83
83
|
}
|
|
84
|
-
// #2672:
|
|
85
|
-
//
|
|
84
|
+
// #2672 / #3167: CI weather states are distinct terminal exits (exit 2).
|
|
85
|
+
// Agents must thrash-cap / BLOCKED rather than multi-hour empty-commit loops.
|
|
86
86
|
if (probe.ciReadyState === "runner_capacity_stall") {
|
|
87
87
|
return build(VERDICT_RUNNER_CAPACITY_STALL, EXIT_TERMINAL_ERROR, probe, poll);
|
|
88
88
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
89
|
+
if (probe.ciReadyState === "ci_never_scheduled") {
|
|
90
|
+
return build(VERDICT_CI_NEVER_SCHEDULED, EXIT_TERMINAL_ERROR, probe, poll);
|
|
91
|
+
}
|
|
92
|
+
if (probe.ciReadyState === "ci_cancelled_no_failover") {
|
|
93
|
+
return build(VERDICT_CI_CANCELLED_NO_FAILOVER, EXIT_TERMINAL_ERROR, probe, poll);
|
|
94
|
+
}
|
|
95
|
+
// #2688 / #3167: Greptile side satisfied on HEAD but CI red (ci_failures) —
|
|
96
|
+
// fail loud toward a fix loop instead of burning max-wait-minutes.
|
|
97
|
+
const ciProductBlocked = probe.cleanGateHoldout === "ci_failures" || probe.ciReadyState === "ci_failures";
|
|
98
|
+
if (ciProductBlocked) {
|
|
92
99
|
ciBlockedStreak += 1;
|
|
93
100
|
}
|
|
94
101
|
else {
|
|
95
102
|
ciBlockedStreak = 0;
|
|
96
103
|
}
|
|
97
|
-
if (oneShot &&
|
|
104
|
+
if (oneShot && ciProductBlocked) {
|
|
98
105
|
return build(VERDICT_CI_BLOCKED, EXIT_TERMINAL_ERROR, probe, poll);
|
|
99
106
|
}
|
|
100
107
|
if (!oneShot && ciBlockedStreak >= DEFAULT_CI_BLOCKED_THRESHOLD) {
|
|
@@ -40,7 +40,7 @@ export const REVIEW_MONITOR_HELP = "usage: task verify:review-monitor -- --pr <N
|
|
|
40
40
|
"\n" +
|
|
41
41
|
"Claim a lease after spawning Approach 1:\n" +
|
|
42
42
|
" task review-monitor:register -- --pr <N> --monitor-agent-id <id> \\\n" +
|
|
43
|
-
" --platform-primitive cursor-task|spawn_subagent|start_agent|sessions_spawn \\\n" +
|
|
43
|
+
" --platform-primitive cursor-task|claude-agent|spawn_subagent|start_agent|sessions_spawn \\\n" +
|
|
44
44
|
" [--head-sha SHA] [--repo OWNER/REPO] [--force]\n" +
|
|
45
45
|
"\n" +
|
|
46
46
|
"Release when done:\n" +
|
|
@@ -55,7 +55,8 @@ export const REGISTER_HELP = "usage: task review-monitor:register -- --pr <N> --
|
|
|
55
55
|
" --pr N Pull request number\n" +
|
|
56
56
|
" --monitor-agent-id ID Stable poller agent id / Task handle\n" +
|
|
57
57
|
" --platform-primitive P start_agent | spawn_subagent | cursor-task |\n" +
|
|
58
|
-
"
|
|
58
|
+
" claude-agent (#3134) | sessions_spawn |\n" +
|
|
59
|
+
" openclaw-sessions-spawn (#2876)\n" +
|
|
59
60
|
"\n" +
|
|
60
61
|
"options:\n" +
|
|
61
62
|
" --repo OWNER/REPO Repository (default: origin / DEFT_TRIAGE_REPO)\n" +
|