patchrelay 0.36.0 → 0.36.2
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/build-info.json +3 -3
- package/dist/cli/cluster-health.js +65 -0
- package/dist/cli/watch/IssueRow.js +17 -5
- package/dist/cli/watch/detail-rows.js +17 -5
- package/dist/cli/watch/pr-status.js +19 -2
- package/dist/cli/watch/watch-state.js +1 -1
- package/dist/db.js +3 -0
- package/dist/issue-session-events.js +4 -3
- package/dist/linear-session-reporting.js +3 -2
- package/dist/presentation-text.js +12 -0
- package/dist/service.js +2 -0
- package/dist/status-note.js +2 -2
- package/dist/waiting-reason.js +8 -0
- package/package.json +1 -1
package/dist/build-info.json
CHANGED
|
@@ -96,6 +96,7 @@ export async function collectClusterHealth(config, db, runCommand) {
|
|
|
96
96
|
});
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
|
+
checks.push(...await collectActiveOverlapFindings(snapshots, runCommand));
|
|
99
100
|
for (const snapshot of snapshots) {
|
|
100
101
|
if (!snapshot.issue.prNumber) {
|
|
101
102
|
continue;
|
|
@@ -523,6 +524,70 @@ async function collectReviewQuillAttemptOwners(snapshots, config, runCommand) {
|
|
|
523
524
|
}
|
|
524
525
|
return owners;
|
|
525
526
|
}
|
|
527
|
+
async function collectActiveOverlapFindings(snapshots, runCommand) {
|
|
528
|
+
const findings = [];
|
|
529
|
+
const diffsByProject = new Map();
|
|
530
|
+
for (const snapshot of snapshots) {
|
|
531
|
+
const { issue } = snapshot;
|
|
532
|
+
if (issue.activeRunId === undefined || !issue.worktreePath) {
|
|
533
|
+
continue;
|
|
534
|
+
}
|
|
535
|
+
const files = await listModifiedTrackedFiles(runCommand, issue.worktreePath);
|
|
536
|
+
if (files.size === 0) {
|
|
537
|
+
continue;
|
|
538
|
+
}
|
|
539
|
+
const projectDiffs = diffsByProject.get(issue.projectId) ?? [];
|
|
540
|
+
projectDiffs.push({ issue, files });
|
|
541
|
+
diffsByProject.set(issue.projectId, projectDiffs);
|
|
542
|
+
}
|
|
543
|
+
for (const [projectId, diffs] of diffsByProject) {
|
|
544
|
+
for (let leftIndex = 0; leftIndex < diffs.length; leftIndex += 1) {
|
|
545
|
+
const left = diffs[leftIndex];
|
|
546
|
+
for (let rightIndex = leftIndex + 1; rightIndex < diffs.length; rightIndex += 1) {
|
|
547
|
+
const right = diffs[rightIndex];
|
|
548
|
+
const overlap = [...left.files].filter((file) => right.files.has(file)).sort();
|
|
549
|
+
if (overlap.length === 0) {
|
|
550
|
+
continue;
|
|
551
|
+
}
|
|
552
|
+
findings.push({
|
|
553
|
+
status: "warn",
|
|
554
|
+
scope: "issue:overlap",
|
|
555
|
+
message: `Active work overlaps with ${right.issue.issueKey ?? right.issue.linearIssueId}: ${overlap.slice(0, 3).join(", ")}${overlap.length > 3 ? " ..." : ""}`,
|
|
556
|
+
...(left.issue.issueKey ? { issueKey: left.issue.issueKey } : {}),
|
|
557
|
+
projectId,
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
return findings;
|
|
563
|
+
}
|
|
564
|
+
async function listModifiedTrackedFiles(runCommand, worktreePath) {
|
|
565
|
+
let result;
|
|
566
|
+
try {
|
|
567
|
+
result = await runCommand("git", ["-C", worktreePath, "status", "--porcelain", "--untracked-files=no"]);
|
|
568
|
+
}
|
|
569
|
+
catch {
|
|
570
|
+
return new Set();
|
|
571
|
+
}
|
|
572
|
+
if (result.exitCode !== 0) {
|
|
573
|
+
return new Set();
|
|
574
|
+
}
|
|
575
|
+
const files = new Set();
|
|
576
|
+
for (const line of result.stdout.split("\n")) {
|
|
577
|
+
if (line.trim().length === 0)
|
|
578
|
+
continue;
|
|
579
|
+
const rawPath = line.slice(3).trim();
|
|
580
|
+
if (!rawPath)
|
|
581
|
+
continue;
|
|
582
|
+
const normalized = rawPath.includes(" -> ")
|
|
583
|
+
? rawPath.split(" -> ").at(-1)?.trim()
|
|
584
|
+
: rawPath;
|
|
585
|
+
if (normalized) {
|
|
586
|
+
files.add(normalized);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
return files;
|
|
590
|
+
}
|
|
526
591
|
function getGateCheckNames(project) {
|
|
527
592
|
const configured = project?.gateChecks?.map((entry) => entry.trim()).filter(Boolean) ?? [];
|
|
528
593
|
return configured.length > 0 ? configured : ["verify"];
|
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { Box, Text } from "ink";
|
|
3
3
|
import { summarizeIssueStatusNote } from "./issue-status-note.js";
|
|
4
4
|
import { relativeTime, truncate } from "./format-utils.js";
|
|
5
|
-
import { hasDisplayPrBlocker, isRereviewNeeded, prChecksFact } from "./pr-status.js";
|
|
5
|
+
import { hasDisplayPrBlocker, isApprovedReviewState, isAwaitingReviewState, isChangesRequestedReviewState, isRereviewNeeded, prChecksFact, } from "./pr-status.js";
|
|
6
6
|
// ─── State display ──────────────────────────────────────────────
|
|
7
7
|
const TERMINAL_STATES = new Set(["done", "failed", "escalated"]);
|
|
8
8
|
function needsOperatorIntervention(issue) {
|
|
@@ -17,6 +17,8 @@ function effectiveState(issue) {
|
|
|
17
17
|
return "blocked";
|
|
18
18
|
if (issue.sessionState === "waiting_input")
|
|
19
19
|
return "awaiting_input";
|
|
20
|
+
if (issue.prNumber !== undefined)
|
|
21
|
+
return issue.factoryState;
|
|
20
22
|
if (issue.readyForExecution && !issue.activeRunType && !hasDisplayPrBlocker(issue))
|
|
21
23
|
return "ready";
|
|
22
24
|
return issue.factoryState;
|
|
@@ -80,18 +82,22 @@ function buildFacts(issue, selected) {
|
|
|
80
82
|
facts.push({ text: "operator action needed", color: "red" });
|
|
81
83
|
}
|
|
82
84
|
// Review state — only show when it matters (not yet approved, or changes requested)
|
|
83
|
-
if (issue.prReviewState
|
|
85
|
+
if (isApprovedReviewState(issue.prReviewState)) {
|
|
84
86
|
facts.push({ text: "approved", color: "green" });
|
|
85
87
|
}
|
|
86
88
|
else if (rereviewNeeded) {
|
|
87
89
|
facts.push({ text: "re-review needed", color: "yellow" });
|
|
88
90
|
}
|
|
89
|
-
else if (issue.prReviewState
|
|
91
|
+
else if (isChangesRequestedReviewState(issue.prReviewState)) {
|
|
90
92
|
facts.push({ text: "changes requested", color: "yellow" });
|
|
91
93
|
}
|
|
92
|
-
else if (issue.prNumber !== undefined
|
|
94
|
+
else if (issue.prNumber !== undefined
|
|
95
|
+
&& (isAwaitingReviewState(issue.prReviewState) || (!issue.prReviewState && !TERMINAL_STATES.has(effectiveState(issue))))) {
|
|
93
96
|
facts.push({ text: "awaiting review", color: "yellow" });
|
|
94
97
|
}
|
|
98
|
+
if (issue.factoryState === "awaiting_queue") {
|
|
99
|
+
facts.push({ text: "merge queue", color: "cyan" });
|
|
100
|
+
}
|
|
95
101
|
// Check status — compact
|
|
96
102
|
const checksFact = prChecksFact(issue);
|
|
97
103
|
if (checksFact) {
|
|
@@ -110,6 +116,10 @@ function blockerText(issue) {
|
|
|
110
116
|
return issue.waitingReason ?? "Waiting for input";
|
|
111
117
|
if (needsOperatorIntervention(issue))
|
|
112
118
|
return issue.statusNote ?? issue.waitingReason ?? "Needs operator intervention";
|
|
119
|
+
if (issue.waitingReason && issue.activeRunType && issue.factoryState === "pr_open")
|
|
120
|
+
return issue.waitingReason;
|
|
121
|
+
if (issue.waitingReason && issue.activeRunType && issue.factoryState === "awaiting_queue")
|
|
122
|
+
return issue.waitingReason;
|
|
113
123
|
if (issue.waitingReason && !issue.activeRunType)
|
|
114
124
|
return issue.waitingReason;
|
|
115
125
|
if (issue.blockedByCount > 0)
|
|
@@ -129,8 +139,10 @@ function blockerText(issue) {
|
|
|
129
139
|
}
|
|
130
140
|
if (rereviewNeeded)
|
|
131
141
|
return "Awaiting re-review after requested changes";
|
|
132
|
-
if (issue.prReviewState
|
|
142
|
+
if (isChangesRequestedReviewState(issue.prReviewState))
|
|
133
143
|
return "Review changes requested";
|
|
144
|
+
if (issue.prNumber !== undefined && isAwaitingReviewState(issue.prReviewState))
|
|
145
|
+
return "Awaiting review";
|
|
134
146
|
return null;
|
|
135
147
|
}
|
|
136
148
|
// ─── Render ─────────────────────────────────────────────────────
|
|
@@ -3,7 +3,7 @@ import { buildTimelineRows } from "./timeline-presentation.js";
|
|
|
3
3
|
import { planStepColor, planStepSymbol } from "./plan-helpers.js";
|
|
4
4
|
import { progressBar } from "./format-utils.js";
|
|
5
5
|
import { describePatchRelayFreshness } from "./freshness.js";
|
|
6
|
-
import { hasDisplayPrBlocker, isRereviewNeeded, prChecksFact } from "./pr-status.js";
|
|
6
|
+
import { hasDisplayPrBlocker, isApprovedReviewState, isAwaitingReviewState, isChangesRequestedReviewState, isRereviewNeeded, prChecksFact, } from "./pr-status.js";
|
|
7
7
|
import { renderRichTextLines, renderTextLines } from "./render-rich-text.js";
|
|
8
8
|
const SESSION_DISPLAY = {
|
|
9
9
|
idle: { label: "idle", color: "blueBright" },
|
|
@@ -400,12 +400,17 @@ function buildFactSegments(issue, issueContext) {
|
|
|
400
400
|
const rereviewNeeded = isRereviewNeeded(issue);
|
|
401
401
|
if (issue.prNumber !== undefined)
|
|
402
402
|
facts.push([{ text: `PR #${issue.prNumber}`, color: "cyan" }]);
|
|
403
|
-
if (issue.prReviewState
|
|
403
|
+
if (isApprovedReviewState(issue.prReviewState))
|
|
404
404
|
facts.push([{ text: "approved", color: "green" }]);
|
|
405
405
|
else if (rereviewNeeded)
|
|
406
406
|
facts.push([{ text: "re-review needed", color: "yellow" }]);
|
|
407
|
-
else if (issue.prReviewState
|
|
407
|
+
else if (isChangesRequestedReviewState(issue.prReviewState))
|
|
408
408
|
facts.push([{ text: "changes requested", color: "yellow" }]);
|
|
409
|
+
else if (issue.prNumber !== undefined
|
|
410
|
+
&& (isAwaitingReviewState(issue.prReviewState) || (!issue.prReviewState && issue.factoryState === "pr_open")))
|
|
411
|
+
facts.push([{ text: "awaiting review", color: "yellow" }]);
|
|
412
|
+
if (issue.factoryState === "awaiting_queue")
|
|
413
|
+
facts.push([{ text: "merge queue", color: "cyan" }]);
|
|
409
414
|
if (issue.waitingReason && issue.sessionState === "waiting_input")
|
|
410
415
|
facts.push([{ text: issue.waitingReason, color: "yellow" }]);
|
|
411
416
|
const checks = prChecksFact({
|
|
@@ -512,6 +517,8 @@ function effectiveState(issue) {
|
|
|
512
517
|
return "blocked";
|
|
513
518
|
if (issue.sessionState === "waiting_input")
|
|
514
519
|
return "awaiting_input";
|
|
520
|
+
if (issue.prNumber !== undefined)
|
|
521
|
+
return issue.factoryState;
|
|
515
522
|
if (issue.readyForExecution && !issue.activeRunType && !hasDisplayPrBlocker(issue))
|
|
516
523
|
return "ready";
|
|
517
524
|
return issue.factoryState;
|
|
@@ -523,6 +530,10 @@ function blockerText(issue, issueContext) {
|
|
|
523
530
|
if (issue.sessionState === "failed" || issue.factoryState === "failed" || issue.factoryState === "escalated") {
|
|
524
531
|
return issue.statusNote ?? issue.waitingReason ?? "Needs operator intervention";
|
|
525
532
|
}
|
|
533
|
+
if (issue.waitingReason && issue.activeRunType && issue.factoryState === "pr_open")
|
|
534
|
+
return issue.waitingReason;
|
|
535
|
+
if (issue.waitingReason && issue.activeRunType && issue.factoryState === "awaiting_queue")
|
|
536
|
+
return issue.waitingReason;
|
|
526
537
|
if (issue.waitingReason && !issue.activeRunType)
|
|
527
538
|
return issue.waitingReason;
|
|
528
539
|
if (issue.blockedByCount > 0)
|
|
@@ -545,10 +556,11 @@ function blockerText(issue, issueContext) {
|
|
|
545
556
|
}
|
|
546
557
|
if (rereviewNeeded)
|
|
547
558
|
return "Awaiting re-review after requested changes";
|
|
548
|
-
if (issue.prReviewState
|
|
559
|
+
if (isChangesRequestedReviewState(issue.prReviewState))
|
|
549
560
|
return "Review changes requested";
|
|
550
|
-
if (issue.prNumber !== undefined && !issue.prReviewState && effectiveState(issue) !== "done")
|
|
561
|
+
if (issue.prNumber !== undefined && (isAwaitingReviewState(issue.prReviewState) || (!issue.prReviewState && effectiveState(issue) !== "done"))) {
|
|
551
562
|
return "Awaiting review";
|
|
563
|
+
}
|
|
552
564
|
return null;
|
|
553
565
|
}
|
|
554
566
|
function cleanCommand(raw) {
|
|
@@ -7,6 +7,20 @@ function isFailingCheckStatus(status) {
|
|
|
7
7
|
function isPendingCheckStatus(status) {
|
|
8
8
|
return status === "pending" || status === "in_progress";
|
|
9
9
|
}
|
|
10
|
+
function normalizeReviewState(state) {
|
|
11
|
+
const normalized = state?.trim().toLowerCase();
|
|
12
|
+
return normalized && normalized.length > 0 ? normalized : undefined;
|
|
13
|
+
}
|
|
14
|
+
export function isApprovedReviewState(state) {
|
|
15
|
+
return normalizeReviewState(state) === "approved";
|
|
16
|
+
}
|
|
17
|
+
export function isChangesRequestedReviewState(state) {
|
|
18
|
+
return normalizeReviewState(state) === "changes_requested";
|
|
19
|
+
}
|
|
20
|
+
export function isAwaitingReviewState(state) {
|
|
21
|
+
const normalized = normalizeReviewState(state);
|
|
22
|
+
return normalized === "review_required" || normalized === "commented";
|
|
23
|
+
}
|
|
10
24
|
export function hasPendingPrChecks(issue) {
|
|
11
25
|
const summary = issue.prChecksSummary;
|
|
12
26
|
if (summary?.total) {
|
|
@@ -29,7 +43,7 @@ export function arePrChecksCompleteAndGreen(issue) {
|
|
|
29
43
|
return isPassingCheckStatus(issue.prCheckStatus);
|
|
30
44
|
}
|
|
31
45
|
export function isRereviewNeeded(issue) {
|
|
32
|
-
return issue.prReviewState
|
|
46
|
+
return isChangesRequestedReviewState(issue.prReviewState)
|
|
33
47
|
&& arePrChecksCompleteAndGreen(issue)
|
|
34
48
|
&& !issue.activeRunType;
|
|
35
49
|
}
|
|
@@ -61,10 +75,13 @@ export function hasDisplayPrBlocker(issue) {
|
|
|
61
75
|
if (issue.prNumber === undefined || issue.activeRunType) {
|
|
62
76
|
return false;
|
|
63
77
|
}
|
|
78
|
+
if (issue.factoryState === "pr_open" || issue.factoryState === "awaiting_queue" || issue.factoryState === "repairing_queue") {
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
64
81
|
if (hasPendingPrChecks(issue) || hasFailedPrChecks(issue)) {
|
|
65
82
|
return true;
|
|
66
83
|
}
|
|
67
|
-
if (issue.prReviewState
|
|
84
|
+
if (isChangesRequestedReviewState(issue.prReviewState) && !isRereviewNeeded(issue)) {
|
|
68
85
|
return true;
|
|
69
86
|
}
|
|
70
87
|
if (!issue.prReviewState && issue.factoryState === "pr_open") {
|
|
@@ -67,7 +67,7 @@ export function computeAggregates(issues) {
|
|
|
67
67
|
active++;
|
|
68
68
|
if (!issue.activeRunType && issue.blockedByCount > 0)
|
|
69
69
|
blocked++;
|
|
70
|
-
if (!issue.activeRunType && issue.readyForExecution && !isDone && !isFailed)
|
|
70
|
+
if (!issue.activeRunType && issue.prNumber === undefined && issue.readyForExecution && !isDone && !isFailed)
|
|
71
71
|
ready++;
|
|
72
72
|
if (isDone)
|
|
73
73
|
done++;
|
package/dist/db.js
CHANGED
|
@@ -1189,6 +1189,9 @@ export class PatchRelayDatabase {
|
|
|
1189
1189
|
return explicitSummaryText;
|
|
1190
1190
|
}
|
|
1191
1191
|
const latestSummary = extractLatestAssistantSummary(latestRun);
|
|
1192
|
+
if (latestRun && (latestRun.status === "queued" || latestRun.status === "running")) {
|
|
1193
|
+
return latestSummary;
|
|
1194
|
+
}
|
|
1192
1195
|
if (this.shouldKeepPreviousIssueSummary(issue, latestRun)) {
|
|
1193
1196
|
return this.findLatestCompletedRunSummary(issue.projectId, issue.linearIssueId)
|
|
1194
1197
|
?? existingSummaryText
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { sanitizeOperatorFacingText } from "./presentation-text.js";
|
|
1
2
|
const TERMINAL_SESSION_EVENTS = new Set([
|
|
2
3
|
"stop_requested",
|
|
3
4
|
"undelegated",
|
|
@@ -116,7 +117,7 @@ export function extractLatestAssistantSummary(run) {
|
|
|
116
117
|
try {
|
|
117
118
|
const parsed = JSON.parse(run.summaryJson);
|
|
118
119
|
if (typeof parsed.latestAssistantMessage === "string" && parsed.latestAssistantMessage.trim()) {
|
|
119
|
-
return parsed.latestAssistantMessage;
|
|
120
|
+
return sanitizeOperatorFacingText(parsed.latestAssistantMessage);
|
|
120
121
|
}
|
|
121
122
|
}
|
|
122
123
|
catch {
|
|
@@ -129,14 +130,14 @@ export function extractLatestAssistantSummary(run) {
|
|
|
129
130
|
if (Array.isArray(parsed.assistantMessages)) {
|
|
130
131
|
const latest = parsed.assistantMessages.findLast((value) => typeof value === "string" && value.trim());
|
|
131
132
|
if (typeof latest === "string")
|
|
132
|
-
return latest;
|
|
133
|
+
return sanitizeOperatorFacingText(latest);
|
|
133
134
|
}
|
|
134
135
|
}
|
|
135
136
|
catch {
|
|
136
137
|
// ignore malformed report json
|
|
137
138
|
}
|
|
138
139
|
}
|
|
139
|
-
return run.failureReason;
|
|
140
|
+
return sanitizeOperatorFacingText(run.failureReason);
|
|
140
141
|
}
|
|
141
142
|
function parseEventJson(raw) {
|
|
142
143
|
if (!raw)
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { formatRunTypeLabel } from "./agent-session-plan.js";
|
|
2
|
+
import { sanitizeOperatorFacingText } from "./presentation-text.js";
|
|
2
3
|
function lowerRunTypeLabel(runType) {
|
|
3
4
|
return formatRunTypeLabel(runType).toLowerCase();
|
|
4
5
|
}
|
|
5
6
|
function trimSummary(summary, maxLength = 300) {
|
|
6
|
-
const value = summary
|
|
7
|
+
const value = sanitizeOperatorFacingText(summary);
|
|
7
8
|
if (!value) {
|
|
8
9
|
return undefined;
|
|
9
10
|
}
|
|
@@ -153,7 +154,7 @@ export function summarizeIssueStateForLinear(issue) {
|
|
|
153
154
|
case "waiting_input":
|
|
154
155
|
return issue.waitingReason ?? (issue.prNumber ? `PR #${issue.prNumber} is waiting for input.` : "Waiting for input.");
|
|
155
156
|
case "running":
|
|
156
|
-
return issue.prNumber ? `PR #${issue.prNumber} is actively running.` : "Actively running.";
|
|
157
|
+
return issue.waitingReason ?? (issue.prNumber ? `PR #${issue.prNumber} is actively running.` : "Actively running.");
|
|
157
158
|
case "idle":
|
|
158
159
|
return issue.waitingReason ?? (issue.prNumber ? `PR #${issue.prNumber} is idle.` : "Idle.");
|
|
159
160
|
case "done":
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
function unwrapShellWrappedCommand(text) {
|
|
2
|
+
return text
|
|
3
|
+
.replace(/`(?:\/bin\/bash|bash|\/bin\/sh|sh)\s+-lc\s+'([^`\n]+)'`/g, "`$1`")
|
|
4
|
+
.replace(/`(?:\/bin\/bash|bash|\/bin\/sh|sh)\s+-lc\s+"([^`\n]+)"`/g, "`$1`");
|
|
5
|
+
}
|
|
6
|
+
export function sanitizeOperatorFacingText(text) {
|
|
7
|
+
const trimmed = text?.trim();
|
|
8
|
+
if (!trimmed) {
|
|
9
|
+
return undefined;
|
|
10
|
+
}
|
|
11
|
+
return unwrapShellWrappedCommand(trimmed);
|
|
12
|
+
}
|
package/dist/service.js
CHANGED
|
@@ -31,6 +31,8 @@ function shouldSuppressStatusNote(params) {
|
|
|
31
31
|
if (!note)
|
|
32
32
|
return true;
|
|
33
33
|
return note === "codex turn was interrupted"
|
|
34
|
+
|| note.startsWith("zombie: never started")
|
|
35
|
+
|| note === "stale thread after restart"
|
|
34
36
|
|| note === "patchrelay received your mention. delegate the issue to patchrelay to start work.";
|
|
35
37
|
}
|
|
36
38
|
export function parseCiSnapshotSummary(snapshotJson) {
|
package/dist/status-note.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { extractLatestAssistantSummary } from "./issue-session-events.js";
|
|
2
|
+
import { sanitizeOperatorFacingText } from "./presentation-text.js";
|
|
2
3
|
function clean(value) {
|
|
3
|
-
|
|
4
|
-
return trimmed ? trimmed : undefined;
|
|
4
|
+
return sanitizeOperatorFacingText(value);
|
|
5
5
|
}
|
|
6
6
|
function eventStatusNote(event) {
|
|
7
7
|
if (!event)
|
package/dist/waiting-reason.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export const PATCHRELAY_WAITING_REASONS = {
|
|
2
2
|
activeWork: "PatchRelay is actively working",
|
|
3
|
+
finalizingPublishedPr: "PatchRelay is finalizing a published PR",
|
|
4
|
+
finalizingMergedChange: "PatchRelay is finalizing a merged change",
|
|
3
5
|
waitingForOperatorInput: "Waiting on operator input",
|
|
4
6
|
waitingForReviewFeedback: "Waiting to address review feedback",
|
|
5
7
|
waitingForReviewOnNewHead: "Waiting on review of a newer pushed head",
|
|
@@ -12,6 +14,12 @@ export const PATCHRELAY_WAITING_REASONS = {
|
|
|
12
14
|
};
|
|
13
15
|
export function derivePatchRelayWaitingReason(params) {
|
|
14
16
|
if (params.activeRunType) {
|
|
17
|
+
if (params.prNumber !== undefined && (params.factoryState === "pr_open" || params.factoryState === "awaiting_queue")) {
|
|
18
|
+
return PATCHRELAY_WAITING_REASONS.finalizingPublishedPr;
|
|
19
|
+
}
|
|
20
|
+
if (params.factoryState === "done") {
|
|
21
|
+
return PATCHRELAY_WAITING_REASONS.finalizingMergedChange;
|
|
22
|
+
}
|
|
15
23
|
return `PatchRelay is running ${humanize(params.activeRunType)}`;
|
|
16
24
|
}
|
|
17
25
|
if (params.activeRunId !== undefined) {
|