nightralph 0.0.50 → 0.0.53
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/README.md +70 -8
- package/dist/display.js +80 -62
- package/dist/display.js.map +2 -2
- package/dist/index.js +162 -94
- package/dist/index.js.map +2 -2
- package/dist/meta.json +6 -6
- package/dist/orchestrator.js +86 -33
- package/dist/orchestrator.js.map +2 -2
- package/dist/src/display.d.ts +35 -33
- package/dist/src/display.d.ts.map +1 -1
- package/dist/src/orchestrator.d.ts +10 -0
- package/dist/src/orchestrator.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -837,6 +837,14 @@ var DEFAULT_COLUMNS = 80;
|
|
|
837
837
|
var MIN_LOG_ROWS = 1;
|
|
838
838
|
var MAX_MESSAGE_BUFFER = 200;
|
|
839
839
|
var ANSI_RE = /\x1b\[[0-9;?]*[A-Za-z]/g;
|
|
840
|
+
function rowText(ticket) {
|
|
841
|
+
const label = ticketLabel(ticket);
|
|
842
|
+
if (ticket.status !== "blocked" || !ticket.blockedBy?.length) {
|
|
843
|
+
return label;
|
|
844
|
+
}
|
|
845
|
+
return `${label} (blocked by ${ticket.blockedBy.join(", ")})`;
|
|
846
|
+
}
|
|
847
|
+
__name(rowText, "rowText");
|
|
840
848
|
function fitLine(line, width) {
|
|
841
849
|
const visible = line.replace(ANSI_RE, "");
|
|
842
850
|
if (visible.length <= width) return line;
|
|
@@ -860,13 +868,16 @@ function renderDot(status) {
|
|
|
860
868
|
case "in-progress":
|
|
861
869
|
return color.yellow(FILLED_DOT);
|
|
862
870
|
// The agent and its test gate are finished; the branch
|
|
863
|
-
// waits for the rest of the wave before it is merged.
|
|
871
|
+
// waits for the rest of the wave before it is merged. It
|
|
872
|
+
// stays cyan through the merge and any conflict respawn.
|
|
864
873
|
case "awaiting-merge":
|
|
865
874
|
return color.cyan(FILLED_DOT);
|
|
866
875
|
case "done":
|
|
867
876
|
return color.green(FILLED_DOT);
|
|
868
877
|
case "failed":
|
|
869
878
|
return color.red(FILLED_DOT);
|
|
879
|
+
case "blocked":
|
|
880
|
+
return color.dim(FILLED_DOT);
|
|
870
881
|
}
|
|
871
882
|
}
|
|
872
883
|
__name(renderDot, "renderDot");
|
|
@@ -875,8 +886,8 @@ var StatusDisplay = class {
|
|
|
875
886
|
__name(this, "StatusDisplay");
|
|
876
887
|
}
|
|
877
888
|
wave = null;
|
|
878
|
-
|
|
879
|
-
|
|
889
|
+
tickets = [];
|
|
890
|
+
hasRun = false;
|
|
880
891
|
messages = [];
|
|
881
892
|
renderedLines = 0;
|
|
882
893
|
config;
|
|
@@ -893,20 +904,28 @@ var StatusDisplay = class {
|
|
|
893
904
|
const available = termHeight - overhead;
|
|
894
905
|
return Math.max(1, available);
|
|
895
906
|
}
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
};
|
|
905
|
-
this.remaining = (remaining ?? []).map((t) => ({
|
|
907
|
+
findTicket(num, feature) {
|
|
908
|
+
const found = feature ? this.tickets.find(
|
|
909
|
+
(t) => t.num === num && t.feature === feature
|
|
910
|
+
) : this.tickets.find((t) => t.num === num);
|
|
911
|
+
return found ?? null;
|
|
912
|
+
}
|
|
913
|
+
startRun(tickets) {
|
|
914
|
+
this.tickets = tickets.map((t) => ({
|
|
906
915
|
...t,
|
|
907
916
|
status: "pending"
|
|
908
917
|
}));
|
|
909
|
-
this.
|
|
918
|
+
this.hasRun = true;
|
|
919
|
+
this.viewportStart = 0;
|
|
920
|
+
}
|
|
921
|
+
startWave(waveNum, totalWaves, tickets, _progress) {
|
|
922
|
+
if (!this.hasRun) {
|
|
923
|
+
this.startRun(tickets);
|
|
924
|
+
}
|
|
925
|
+
this.wave = {
|
|
926
|
+
waveNum,
|
|
927
|
+
totalWaves
|
|
928
|
+
};
|
|
910
929
|
this.messages = [];
|
|
911
930
|
this.viewportStart = 0;
|
|
912
931
|
this.config.stream.write(ansi.cursorHide());
|
|
@@ -926,28 +945,31 @@ var StatusDisplay = class {
|
|
|
926
945
|
this.render();
|
|
927
946
|
}
|
|
928
947
|
setTicketStatus(num, status, feature) {
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
this.viewportStart = ticketIndex;
|
|
941
|
-
} else if (ticketIndex >= this.viewportStart + availableRows) {
|
|
942
|
-
this.viewportStart = ticketIndex - availableRows + 3;
|
|
943
|
-
}
|
|
944
|
-
this.viewportStart = Math.max(
|
|
945
|
-
0,
|
|
946
|
-
Math.min(this.viewportStart, totalTickets - 1)
|
|
947
|
-
);
|
|
948
|
+
const ticket = this.findTicket(num, feature);
|
|
949
|
+
if (!ticket) return;
|
|
950
|
+
ticket.status = status;
|
|
951
|
+
if (status === "in-progress") {
|
|
952
|
+
const ticketIndex = this.tickets.indexOf(ticket);
|
|
953
|
+
const availableRows = this.getAvailableRows();
|
|
954
|
+
const totalTickets = this.tickets.length;
|
|
955
|
+
if (ticketIndex < this.viewportStart) {
|
|
956
|
+
this.viewportStart = ticketIndex;
|
|
957
|
+
} else if (ticketIndex >= this.viewportStart + availableRows) {
|
|
958
|
+
this.viewportStart = ticketIndex - availableRows + 3;
|
|
948
959
|
}
|
|
949
|
-
this.
|
|
960
|
+
this.viewportStart = Math.max(
|
|
961
|
+
0,
|
|
962
|
+
Math.min(this.viewportStart, totalTickets - 1)
|
|
963
|
+
);
|
|
950
964
|
}
|
|
965
|
+
this.render();
|
|
966
|
+
}
|
|
967
|
+
setBlocked(num, feature, blockedBy) {
|
|
968
|
+
const ticket = this.findTicket(num, feature);
|
|
969
|
+
if (!ticket) return;
|
|
970
|
+
ticket.status = "blocked";
|
|
971
|
+
ticket.blockedBy = [...blockedBy];
|
|
972
|
+
this.render();
|
|
951
973
|
}
|
|
952
974
|
log(message) {
|
|
953
975
|
if (!this.wave) {
|
|
@@ -974,59 +996,52 @@ var StatusDisplay = class {
|
|
|
974
996
|
if (this.renderedLines > 0) {
|
|
975
997
|
output += ansi.eraseLines(this.renderedLines + 1);
|
|
976
998
|
}
|
|
977
|
-
const
|
|
999
|
+
const done = this.tickets.filter(
|
|
978
1000
|
(t) => t.status === "done"
|
|
979
1001
|
).length;
|
|
980
|
-
const
|
|
981
|
-
const
|
|
982
|
-
const pct = total === 0 ? 0 : Math.round(completed / total * 100);
|
|
1002
|
+
const total = this.tickets.length;
|
|
1003
|
+
const pct = total === 0 ? 0 : Math.round(done / total * 100);
|
|
983
1004
|
const width = Math.max(
|
|
984
1005
|
1,
|
|
985
1006
|
(this.config.stream.columns ?? DEFAULT_COLUMNS) - 1
|
|
986
1007
|
);
|
|
987
|
-
|
|
988
|
-
|
|
1008
|
+
const header = `Wave ${this.wave.waveNum}/${this.wave.totalWaves} - ${done}/${total} done - ${pct}%`;
|
|
1009
|
+
output += fitLine(header, width) + "\n";
|
|
989
1010
|
const availableRows = this.getAvailableRows();
|
|
990
|
-
const tickets = [
|
|
991
|
-
...this.wave.tickets,
|
|
992
|
-
...this.remaining
|
|
993
|
-
];
|
|
994
1011
|
let renderedTicketCount = 0;
|
|
995
|
-
if (tickets.length <= availableRows) {
|
|
996
|
-
for (const ticket of tickets) {
|
|
1012
|
+
if (this.tickets.length <= availableRows) {
|
|
1013
|
+
for (const ticket of this.tickets) {
|
|
997
1014
|
const dot = renderDot(ticket.status);
|
|
998
|
-
const
|
|
999
|
-
|
|
1000
|
-
width - 2
|
|
1001
|
-
);
|
|
1015
|
+
const text = rowText(ticket);
|
|
1016
|
+
const name = fitLine(text, width - 2);
|
|
1002
1017
|
output += `${dot} ${name}
|
|
1003
1018
|
`;
|
|
1004
1019
|
}
|
|
1005
|
-
renderedTicketCount = tickets.length;
|
|
1020
|
+
renderedTicketCount = this.tickets.length;
|
|
1006
1021
|
} else {
|
|
1007
1022
|
this.viewportStart = Math.max(
|
|
1008
1023
|
0,
|
|
1009
1024
|
Math.min(
|
|
1010
1025
|
this.viewportStart,
|
|
1011
|
-
tickets.length - 1
|
|
1026
|
+
this.tickets.length - 1
|
|
1012
1027
|
)
|
|
1013
1028
|
);
|
|
1014
1029
|
let hasMoreAbove = this.viewportStart > 0;
|
|
1015
1030
|
let tentativeCount = availableRows - (hasMoreAbove ? 1 : 0);
|
|
1016
|
-
let hasMoreBelow = this.viewportStart + tentativeCount < tickets.length;
|
|
1031
|
+
let hasMoreBelow = this.viewportStart + tentativeCount < this.tickets.length;
|
|
1017
1032
|
let ticketsToShow = Math.max(
|
|
1018
1033
|
0,
|
|
1019
1034
|
tentativeCount - (hasMoreBelow ? 1 : 0)
|
|
1020
1035
|
);
|
|
1021
1036
|
const maxStart = Math.max(
|
|
1022
1037
|
0,
|
|
1023
|
-
tickets.length - Math.max(1, ticketsToShow)
|
|
1038
|
+
this.tickets.length - Math.max(1, ticketsToShow)
|
|
1024
1039
|
);
|
|
1025
1040
|
if (this.viewportStart > maxStart) {
|
|
1026
1041
|
this.viewportStart = maxStart;
|
|
1027
1042
|
hasMoreAbove = this.viewportStart > 0;
|
|
1028
1043
|
tentativeCount = availableRows - (hasMoreAbove ? 1 : 0);
|
|
1029
|
-
hasMoreBelow = this.viewportStart + tentativeCount < tickets.length;
|
|
1044
|
+
hasMoreBelow = this.viewportStart + tentativeCount < this.tickets.length;
|
|
1030
1045
|
ticketsToShow = Math.max(
|
|
1031
1046
|
0,
|
|
1032
1047
|
tentativeCount - (hasMoreBelow ? 1 : 0)
|
|
@@ -1037,21 +1052,19 @@ var StatusDisplay = class {
|
|
|
1037
1052
|
output += ` ... ${moreCount} more above
|
|
1038
1053
|
`;
|
|
1039
1054
|
}
|
|
1040
|
-
const visibleTickets = tickets.slice(
|
|
1055
|
+
const visibleTickets = this.tickets.slice(
|
|
1041
1056
|
this.viewportStart,
|
|
1042
1057
|
this.viewportStart + ticketsToShow
|
|
1043
1058
|
);
|
|
1044
1059
|
for (const ticket of visibleTickets) {
|
|
1045
1060
|
const dot = renderDot(ticket.status);
|
|
1046
|
-
const
|
|
1047
|
-
|
|
1048
|
-
width - 2
|
|
1049
|
-
);
|
|
1061
|
+
const text = rowText(ticket);
|
|
1062
|
+
const name = fitLine(text, width - 2);
|
|
1050
1063
|
output += `${dot} ${name}
|
|
1051
1064
|
`;
|
|
1052
1065
|
}
|
|
1053
1066
|
if (hasMoreBelow) {
|
|
1054
|
-
const moreCount = tickets.length - (this.viewportStart + ticketsToShow);
|
|
1067
|
+
const moreCount = this.tickets.length - (this.viewportStart + ticketsToShow);
|
|
1055
1068
|
output += ` ... ${moreCount} more below
|
|
1056
1069
|
`;
|
|
1057
1070
|
}
|
|
@@ -1083,7 +1096,9 @@ var PlainDisplay = class {
|
|
|
1083
1096
|
static {
|
|
1084
1097
|
__name(this, "PlainDisplay");
|
|
1085
1098
|
}
|
|
1086
|
-
|
|
1099
|
+
startRun(_tickets) {
|
|
1100
|
+
}
|
|
1101
|
+
startWave(waveNum, _totalWaves, tickets, progress) {
|
|
1087
1102
|
const pct = progress && progress.total > 0 ? ` (${progress.completed}/${progress.total} done)` : "";
|
|
1088
1103
|
console.log(
|
|
1089
1104
|
`
|
|
@@ -1092,6 +1107,8 @@ Wave ${waveNum}${pct}: ` + tickets.map((t) => ticketLabel(t)).join(", ")
|
|
|
1092
1107
|
}
|
|
1093
1108
|
setTicketStatus(_num, _status, _feature) {
|
|
1094
1109
|
}
|
|
1110
|
+
setBlocked(_num, _feature, _blockedBy) {
|
|
1111
|
+
}
|
|
1095
1112
|
log(message) {
|
|
1096
1113
|
console.log(message);
|
|
1097
1114
|
}
|
|
@@ -1301,6 +1318,37 @@ function findReadyTickets(tickets) {
|
|
|
1301
1318
|
});
|
|
1302
1319
|
}
|
|
1303
1320
|
__name(findReadyTickets, "findReadyTickets");
|
|
1321
|
+
function findBlockedTickets(tickets, failedKeys) {
|
|
1322
|
+
const byKey = new Map(
|
|
1323
|
+
tickets.map((t) => [ticketKey(t.feature, t.num), t])
|
|
1324
|
+
);
|
|
1325
|
+
function collectDead(ticket, visited, found) {
|
|
1326
|
+
for (const b of ticket.blockers) {
|
|
1327
|
+
const key = ticketKey(ticket.feature, b);
|
|
1328
|
+
if (visited.has(key)) continue;
|
|
1329
|
+
visited.add(key);
|
|
1330
|
+
const blocker = byKey.get(key);
|
|
1331
|
+
if (!blocker || blocker.status === "done") continue;
|
|
1332
|
+
if (failedKeys.has(key) || blocker.status !== "ready-for-agent") {
|
|
1333
|
+
found.add(key);
|
|
1334
|
+
continue;
|
|
1335
|
+
}
|
|
1336
|
+
collectDead(blocker, visited, found);
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
__name(collectDead, "collectDead");
|
|
1340
|
+
const blocked = /* @__PURE__ */ new Map();
|
|
1341
|
+
for (const t of tickets) {
|
|
1342
|
+
const key = ticketKey(t.feature, t.num);
|
|
1343
|
+
if (t.status !== "ready-for-agent") continue;
|
|
1344
|
+
if (failedKeys.has(key)) continue;
|
|
1345
|
+
const found = /* @__PURE__ */ new Set();
|
|
1346
|
+
collectDead(t, /* @__PURE__ */ new Set([key]), found);
|
|
1347
|
+
if (found.size > 0) blocked.set(key, [...found]);
|
|
1348
|
+
}
|
|
1349
|
+
return blocked;
|
|
1350
|
+
}
|
|
1351
|
+
__name(findBlockedTickets, "findBlockedTickets");
|
|
1304
1352
|
var COMPLETED_RE = /^\s*[-*+]?\s*\[x\]\s+(.+)$/i;
|
|
1305
1353
|
var TOOL_USE_RE = /^\[[\w.]+\]/;
|
|
1306
1354
|
var CHECKBOX_RE = /^(\s*[-*+] )\[( |x|X)\]( .*)$/;
|
|
@@ -1837,7 +1885,7 @@ function spawnAgent(opts) {
|
|
|
1837
1885
|
detached: true
|
|
1838
1886
|
});
|
|
1839
1887
|
if (proc.pid !== void 0) runningAgents.add(proc.pid);
|
|
1840
|
-
proc.stdout.pipe(logStream);
|
|
1888
|
+
proc.stdout.pipe(logStream, { end: false });
|
|
1841
1889
|
const piFormatter = isPi ? createPiStreamFormatter() : null;
|
|
1842
1890
|
const completedItems = [];
|
|
1843
1891
|
const seenItems = /* @__PURE__ */ new Set();
|
|
@@ -1886,7 +1934,7 @@ function spawnAgent(opts) {
|
|
|
1886
1934
|
}
|
|
1887
1935
|
}
|
|
1888
1936
|
});
|
|
1889
|
-
proc.stderr.pipe(logStream);
|
|
1937
|
+
proc.stderr.pipe(logStream, { end: false });
|
|
1890
1938
|
const rlErr = createInterface({ input: proc.stderr });
|
|
1891
1939
|
rlErr.on("line", (line) => {
|
|
1892
1940
|
if (isClaude && !TOOL_USE_RE.test(line)) return;
|
|
@@ -2480,6 +2528,29 @@ function renderFinishSummary(tickets, totalCompleted, failedKeys) {
|
|
|
2480
2528
|
return lines.join("\n");
|
|
2481
2529
|
}
|
|
2482
2530
|
__name(renderFinishSummary, "renderFinishSummary");
|
|
2531
|
+
function sessionTickets(tickets) {
|
|
2532
|
+
return [...tickets].filter((t) => t.status === "ready-for-agent").sort((a, b) => a.feature.localeCompare(b.feature) || a.num - b.num);
|
|
2533
|
+
}
|
|
2534
|
+
__name(sessionTickets, "sessionTickets");
|
|
2535
|
+
function toRef(t) {
|
|
2536
|
+
return { num: t.num, filename: t.filename, feature: t.feature };
|
|
2537
|
+
}
|
|
2538
|
+
__name(toRef, "toRef");
|
|
2539
|
+
function reportBlocked(d, tickets, failedKeys) {
|
|
2540
|
+
const byKey = new Map(
|
|
2541
|
+
tickets.map((t) => [ticketKey(t.feature, t.num), t])
|
|
2542
|
+
);
|
|
2543
|
+
for (const [key, dead] of findBlockedTickets(
|
|
2544
|
+
tickets,
|
|
2545
|
+
failedKeys
|
|
2546
|
+
)) {
|
|
2547
|
+
const ticket = byKey.get(key);
|
|
2548
|
+
if (!ticket) continue;
|
|
2549
|
+
const labels = dead.map((k) => byKey.get(k)).filter((t) => t !== void 0).map((t) => ticketLabel(toRef(t)));
|
|
2550
|
+
d.setBlocked(ticket.num, ticket.feature, labels);
|
|
2551
|
+
}
|
|
2552
|
+
}
|
|
2553
|
+
__name(reportBlocked, "reportBlocked");
|
|
2483
2554
|
function runProgress(tickets) {
|
|
2484
2555
|
return {
|
|
2485
2556
|
completed: tickets.filter((t) => t.status === "done").length,
|
|
@@ -2500,7 +2571,9 @@ async function runWaves(tickets, specBody, agentCmd, model, timeout, logsDir, te
|
|
|
2500
2571
|
let totalCompleted = 0;
|
|
2501
2572
|
let waveNum = 0;
|
|
2502
2573
|
const failedKeys = /* @__PURE__ */ new Set();
|
|
2574
|
+
const session = sessionTickets(tickets);
|
|
2503
2575
|
while (true) {
|
|
2576
|
+
if (waveNum > 0) reportBlocked(d, tickets, failedKeys);
|
|
2504
2577
|
const ready = findReadyTickets(tickets).filter(
|
|
2505
2578
|
(t) => !failedKeys.has(
|
|
2506
2579
|
ticketKey(t.feature, t.num)
|
|
@@ -2508,12 +2581,10 @@ async function runWaves(tickets, specBody, agentCmd, model, timeout, logsDir, te
|
|
|
2508
2581
|
);
|
|
2509
2582
|
if (ready.length === 0) break;
|
|
2510
2583
|
waveNum++;
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
(t) => t.status === "ready-for-agent" && !readyKeys.has(ticketKey(t.feature, t.num))
|
|
2516
|
-
);
|
|
2584
|
+
if (waveNum === 1) {
|
|
2585
|
+
d.startRun(session.map(toRef));
|
|
2586
|
+
reportBlocked(d, tickets, failedKeys);
|
|
2587
|
+
}
|
|
2517
2588
|
d.startWave(
|
|
2518
2589
|
waveNum,
|
|
2519
2590
|
totalWaves,
|
|
@@ -2522,12 +2593,7 @@ async function runWaves(tickets, specBody, agentCmd, model, timeout, logsDir, te
|
|
|
2522
2593
|
filename: t.filename,
|
|
2523
2594
|
feature: t.feature
|
|
2524
2595
|
})),
|
|
2525
|
-
runProgress(
|
|
2526
|
-
remaining2.map((t) => ({
|
|
2527
|
-
num: t.num,
|
|
2528
|
-
filename: t.filename,
|
|
2529
|
-
feature: t.feature
|
|
2530
|
-
}))
|
|
2596
|
+
runProgress(session)
|
|
2531
2597
|
);
|
|
2532
2598
|
const results = await runPool(
|
|
2533
2599
|
ready,
|
|
@@ -2624,10 +2690,10 @@ async function runWaves(tickets, specBody, agentCmd, model, timeout, logsDir, te
|
|
|
2624
2690
|
);
|
|
2625
2691
|
}
|
|
2626
2692
|
if (waveCompleted === 0) {
|
|
2627
|
-
const
|
|
2693
|
+
const remaining2 = findReadyTickets(tickets).filter((t) => !failedKeys.has(
|
|
2628
2694
|
ticketKey(t.feature, t.num)
|
|
2629
2695
|
));
|
|
2630
|
-
if (
|
|
2696
|
+
if (remaining2.length === 0) {
|
|
2631
2697
|
d.log(
|
|
2632
2698
|
"\nNo tickets completed this wave and no ready tickets remain. Stopping."
|
|
2633
2699
|
);
|
|
@@ -2635,10 +2701,11 @@ async function runWaves(tickets, specBody, agentCmd, model, timeout, logsDir, te
|
|
|
2635
2701
|
}
|
|
2636
2702
|
d.log(
|
|
2637
2703
|
`
|
|
2638
|
-
No tickets completed this wave. ${
|
|
2704
|
+
No tickets completed this wave. ${remaining2.length} ready ticket${remaining2.length === 1 ? "" : "s"} remain; continuing.`
|
|
2639
2705
|
);
|
|
2640
2706
|
}
|
|
2641
2707
|
}
|
|
2708
|
+
if (waveNum > 0) reportBlocked(d, tickets, failedKeys);
|
|
2642
2709
|
const remaining = tickets.filter(
|
|
2643
2710
|
(t) => t.status === "ready-for-agent"
|
|
2644
2711
|
);
|
|
@@ -2731,7 +2798,9 @@ async function runWavesIsolated(opts) {
|
|
|
2731
2798
|
let totalCompleted = 0;
|
|
2732
2799
|
let waveNum = 0;
|
|
2733
2800
|
const failedKeys = /* @__PURE__ */ new Set();
|
|
2801
|
+
const session = sessionTickets(opts.tickets);
|
|
2734
2802
|
while (true) {
|
|
2803
|
+
if (waveNum > 0) reportBlocked(d, opts.tickets, failedKeys);
|
|
2735
2804
|
const ready = findReadyTickets(opts.tickets).filter(
|
|
2736
2805
|
(t) => !failedKeys.has(
|
|
2737
2806
|
ticketKey(t.feature, t.num)
|
|
@@ -2739,12 +2808,10 @@ async function runWavesIsolated(opts) {
|
|
|
2739
2808
|
);
|
|
2740
2809
|
if (ready.length === 0) break;
|
|
2741
2810
|
waveNum++;
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
(t) => t.status === "ready-for-agent" && !readyKeys.has(ticketKey(t.feature, t.num))
|
|
2747
|
-
);
|
|
2811
|
+
if (waveNum === 1) {
|
|
2812
|
+
d.startRun(session.map(toRef));
|
|
2813
|
+
reportBlocked(d, opts.tickets, failedKeys);
|
|
2814
|
+
}
|
|
2748
2815
|
d.startWave(
|
|
2749
2816
|
waveNum,
|
|
2750
2817
|
totalWaves,
|
|
@@ -2753,12 +2820,7 @@ async function runWavesIsolated(opts) {
|
|
|
2753
2820
|
filename: t.filename,
|
|
2754
2821
|
feature: t.feature
|
|
2755
2822
|
})),
|
|
2756
|
-
runProgress(
|
|
2757
|
-
remaining2.map((t) => ({
|
|
2758
|
-
num: t.num,
|
|
2759
|
-
filename: t.filename,
|
|
2760
|
-
feature: t.feature
|
|
2761
|
-
}))
|
|
2823
|
+
runProgress(session)
|
|
2762
2824
|
);
|
|
2763
2825
|
const settled = await runPool(
|
|
2764
2826
|
ready,
|
|
@@ -3020,6 +3082,11 @@ async function runWavesIsolated(opts) {
|
|
|
3020
3082
|
failedKeys.add(
|
|
3021
3083
|
ticketKey(ticket.feature, ticket.num)
|
|
3022
3084
|
);
|
|
3085
|
+
d.setTicketStatus(
|
|
3086
|
+
ticket.num,
|
|
3087
|
+
"failed",
|
|
3088
|
+
ticket.feature
|
|
3089
|
+
);
|
|
3023
3090
|
d.log(
|
|
3024
3091
|
` ${ticket.filename}: merge failed, staying ready-for-agent`
|
|
3025
3092
|
);
|
|
@@ -3067,12 +3134,12 @@ Merge stopped at ${mergeResult.stoppedAt.branch}`
|
|
|
3067
3134
|
}
|
|
3068
3135
|
}
|
|
3069
3136
|
if (waveCompleted === 0) {
|
|
3070
|
-
const
|
|
3137
|
+
const remaining2 = findReadyTickets(
|
|
3071
3138
|
opts.tickets
|
|
3072
3139
|
).filter((t) => !failedKeys.has(
|
|
3073
3140
|
ticketKey(t.feature, t.num)
|
|
3074
3141
|
));
|
|
3075
|
-
if (
|
|
3142
|
+
if (remaining2.length === 0) {
|
|
3076
3143
|
d.log(
|
|
3077
3144
|
"\nNo tickets completed this wave and no ready tickets remain. Stopping."
|
|
3078
3145
|
);
|
|
@@ -3080,10 +3147,11 @@ Merge stopped at ${mergeResult.stoppedAt.branch}`
|
|
|
3080
3147
|
}
|
|
3081
3148
|
d.log(
|
|
3082
3149
|
`
|
|
3083
|
-
No tickets completed this wave. ${
|
|
3150
|
+
No tickets completed this wave. ${remaining2.length} ready ticket${remaining2.length === 1 ? "" : "s"} remain; continuing.`
|
|
3084
3151
|
);
|
|
3085
3152
|
}
|
|
3086
3153
|
}
|
|
3154
|
+
if (waveNum > 0) reportBlocked(d, opts.tickets, failedKeys);
|
|
3087
3155
|
const remaining = opts.tickets.filter(
|
|
3088
3156
|
(t) => t.status === "ready-for-agent"
|
|
3089
3157
|
);
|