patchrelay 0.35.0 → 0.35.1
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
CHANGED
|
@@ -1,31 +1,35 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Box, Text } from "ink";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
3
|
+
const STATE_LABELS = {
|
|
4
|
+
delegated: "delegated",
|
|
5
|
+
implementing: "implementing",
|
|
6
|
+
pr_open: "PR open",
|
|
7
|
+
awaiting_queue: "merge queue",
|
|
8
|
+
done: "done",
|
|
9
|
+
changes_requested: "review fix",
|
|
10
|
+
repairing_ci: "CI repair",
|
|
11
|
+
repairing_queue: "queue repair",
|
|
12
|
+
awaiting_input: "needs input",
|
|
13
|
+
escalated: "escalated",
|
|
14
|
+
failed: "failed",
|
|
15
|
+
};
|
|
16
|
+
function displayLabel(state) {
|
|
17
|
+
return STATE_LABELS[state] ?? state;
|
|
12
18
|
}
|
|
13
|
-
function
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
return (_jsxs(Text, { color: statusColor(node.status), bold: node.status === "current", children: ["[", statusPrefix(node.status), " ", node.label, "]"] }));
|
|
25
|
-
}
|
|
26
|
-
function NodeRow({ label, nodes, connector = " -> ", }) {
|
|
27
|
-
return (_jsxs(Box, { children: [_jsx(Text, { dimColor: true, children: label.padEnd(11, " ") }), nodes.map((node, index) => (_jsxs(Box, { children: [index > 0 && _jsx(Text, { dimColor: true, children: connector }), _jsx(NodePill, { node: node })] }, node.state)))] }));
|
|
19
|
+
function NodeRow({ nodes, connector }) {
|
|
20
|
+
const visible = nodes.filter((n) => connector === " \u2192 " || n.status !== "upcoming");
|
|
21
|
+
if (visible.length === 0)
|
|
22
|
+
return _jsx(_Fragment, {});
|
|
23
|
+
return (_jsx(Box, { gap: 0, children: visible.map((node, i) => {
|
|
24
|
+
const dot = node.status === "upcoming" ? "\u25cb" : "\u25cf";
|
|
25
|
+
const color = node.status === "current" ? "cyan"
|
|
26
|
+
: node.status === "visited" ? "green"
|
|
27
|
+
: "gray";
|
|
28
|
+
return (_jsxs(Box, { gap: 0, children: [i > 0 && _jsx(Text, { dimColor: true, children: connector }), _jsx(Text, { color: color, bold: node.status === "current", children: dot }), _jsx(Text, { color: color, bold: node.status === "current", children: ` ${displayLabel(node.state)}` })] }, node.state));
|
|
29
|
+
}) }));
|
|
28
30
|
}
|
|
29
31
|
export function FactoryStateGraph({ main, prLoops, queueLoop, exits, }) {
|
|
30
|
-
|
|
32
|
+
const hasLoops = prLoops.some((n) => n.status !== "upcoming") || queueLoop.some((n) => n.status !== "upcoming");
|
|
33
|
+
const hasExits = exits.some((n) => n.status !== "upcoming");
|
|
34
|
+
return (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(NodeRow, { nodes: main, connector: " \\u2192 " }), hasLoops && (_jsx(Box, { gap: 0, paddingLeft: 2, children: _jsx(NodeRow, { nodes: [...prLoops, ...queueLoop], connector: " " }) })), hasExits && (_jsx(Box, { gap: 0, paddingLeft: 2, children: _jsx(NodeRow, { nodes: exits, connector: " " }) }))] }));
|
|
31
35
|
}
|
|
@@ -9,12 +9,11 @@ export function Timeline({ entries, follow }) {
|
|
|
9
9
|
const rows = stdout?.rows ?? 24;
|
|
10
10
|
const maxActive = Math.max(ACTIVE_TAIL, rows - 12);
|
|
11
11
|
const displayRows = useMemo(() => buildTimelineRows(entries), [entries]);
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const finalized = displayRows.slice(0, splitIndex);
|
|
12
|
+
// Always cap the rendered entries to prevent OOM/WASM crashes.
|
|
13
|
+
// In follow mode: older entries go to Static (terminal scrollback).
|
|
14
|
+
// Without follow: show last maxActive entries only.
|
|
15
|
+
const splitIndex = Math.max(0, displayRows.length - maxActive);
|
|
16
|
+
const finalized = follow ? displayRows.slice(0, splitIndex) : [];
|
|
18
17
|
const active = displayRows.slice(splitIndex);
|
|
19
18
|
if (displayRows.length === 0) {
|
|
20
19
|
return _jsx(Text, { dimColor: true, children: "No timeline events yet." });
|
package/dist/run-orchestrator.js
CHANGED
|
@@ -632,15 +632,31 @@ export class RunOrchestrator {
|
|
|
632
632
|
const hasQueueLabel = pr.labels?.some((l) => l.name === protocol.admissionLabel) ?? false;
|
|
633
633
|
if (!hasQueueLabel)
|
|
634
634
|
return;
|
|
635
|
-
//
|
|
636
|
-
|
|
635
|
+
// Detect queue issues: either GitHub reports DIRTY, or the steward
|
|
636
|
+
// eviction check run failed (webhook may have been missed).
|
|
637
|
+
const isDirty = pr.mergeStateStatus === "DIRTY" || pr.mergeable === "CONFLICTING";
|
|
638
|
+
let hasEvictionCheckRun = false;
|
|
639
|
+
if (!isDirty) {
|
|
640
|
+
// Check for missed eviction webhook by looking for the steward's
|
|
641
|
+
// check run on the PR head.
|
|
642
|
+
try {
|
|
643
|
+
const { stdout: checksOut } = await execCommand("gh", [
|
|
644
|
+
"api", `repos/${project.github.repoFullName}/commits/${pr.headRefOid}/check-runs`,
|
|
645
|
+
"--jq", `.check_runs[] | select(.name == "${protocol.evictionCheckName}" and .conclusion == "failure") | .name`,
|
|
646
|
+
], { timeoutMs: 10_000 });
|
|
647
|
+
hasEvictionCheckRun = checksOut.trim().length > 0;
|
|
648
|
+
}
|
|
649
|
+
catch {
|
|
650
|
+
// Best-effort check.
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
if (isDirty || hasEvictionCheckRun) {
|
|
637
654
|
const headRefOid = pr.headRefOid ?? "unknown";
|
|
638
|
-
|
|
639
|
-
// main-only advance with the same PR head is recognized as a new conflict.
|
|
655
|
+
const reason = hasEvictionCheckRun ? "queue_eviction_missed" : "preemptive_conflict";
|
|
640
656
|
const signature = `preemptive_queue_conflict:${headRefOid}`;
|
|
641
657
|
const pendingRunContext = {
|
|
642
658
|
source: "queue_health_monitor",
|
|
643
|
-
failureReason:
|
|
659
|
+
failureReason: reason,
|
|
644
660
|
failureHeadSha: headRefOid,
|
|
645
661
|
failureSignature: signature,
|
|
646
662
|
};
|
|
@@ -657,15 +673,17 @@ export class RunOrchestrator {
|
|
|
657
673
|
pendingRunType: "queue_repair",
|
|
658
674
|
pendingRunContext,
|
|
659
675
|
});
|
|
660
|
-
this.logger.info({ issueKey: issue.issueKey, prNumber: issue.prNumber, headRefOid }, "Queue health:
|
|
676
|
+
this.logger.info({ issueKey: issue.issueKey, prNumber: issue.prNumber, headRefOid, reason }, "Queue health: queue issue detected, dispatching repair");
|
|
661
677
|
this.feed?.publish({
|
|
662
678
|
level: "warn",
|
|
663
679
|
kind: "github",
|
|
664
680
|
issueKey: issue.issueKey,
|
|
665
681
|
projectId: issue.projectId,
|
|
666
682
|
stage: "repairing_queue",
|
|
667
|
-
status: "queue_health_conflict_detected",
|
|
668
|
-
summary:
|
|
683
|
+
status: hasEvictionCheckRun ? "queue_health_eviction_detected" : "queue_health_conflict_detected",
|
|
684
|
+
summary: hasEvictionCheckRun
|
|
685
|
+
? `Queue health: missed eviction detected on PR #${issue.prNumber}, dispatching repair`
|
|
686
|
+
: `Queue health: merge conflict detected on PR #${issue.prNumber}, dispatching preemptive repair`,
|
|
669
687
|
});
|
|
670
688
|
}
|
|
671
689
|
}
|