@taskless-app/swarm 0.6.50 → 0.6.52
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/cli.js +104 -98
- package/dist/overmind.js +221 -123
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -164,6 +164,104 @@ var init_types = __esm({
|
|
|
164
164
|
}
|
|
165
165
|
});
|
|
166
166
|
|
|
167
|
+
// ../swarm-core/src/verifyVerdict.ts
|
|
168
|
+
function isVerifyVerdict(v) {
|
|
169
|
+
return typeof v === "string" && VERIFY_VERDICT_SET.has(v);
|
|
170
|
+
}
|
|
171
|
+
function toVerifyVerdict(v) {
|
|
172
|
+
return isVerifyVerdict(v) ? v : null;
|
|
173
|
+
}
|
|
174
|
+
function isNegativeVerifyVerdict(v) {
|
|
175
|
+
return v === "not_reproduced" || v === "found_issues";
|
|
176
|
+
}
|
|
177
|
+
function verifyVerdictLabel(v) {
|
|
178
|
+
switch (v) {
|
|
179
|
+
case "confirmed":
|
|
180
|
+
return "\u041F\u041E\u0414\u0422\u0412\u0415\u0420\u0416\u0414\u0410\u042E";
|
|
181
|
+
case "not_reproduced":
|
|
182
|
+
return "\u041D\u0415 \u0412\u041E\u0421\u041F\u0420\u041E\u0418\u0417\u0412\u041E\u0414\u0418\u0422\u0421\u042F";
|
|
183
|
+
case "found_issues":
|
|
184
|
+
return "\u041D\u0410\u0428\u0401\u041B";
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
function verifyStats(runs) {
|
|
188
|
+
let confirmed = 0;
|
|
189
|
+
let notReproduced = 0;
|
|
190
|
+
let foundIssues = 0;
|
|
191
|
+
for (const r of runs) {
|
|
192
|
+
switch (toVerifyVerdict(r.verify_verdict)) {
|
|
193
|
+
case "confirmed":
|
|
194
|
+
confirmed++;
|
|
195
|
+
break;
|
|
196
|
+
case "not_reproduced":
|
|
197
|
+
notReproduced++;
|
|
198
|
+
break;
|
|
199
|
+
case "found_issues":
|
|
200
|
+
foundIssues++;
|
|
201
|
+
break;
|
|
202
|
+
default:
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
const negative = notReproduced + foundIssues;
|
|
207
|
+
const total = confirmed + negative;
|
|
208
|
+
return { total, confirmed, notReproduced, foundIssues, negative, negativeShare: total ? negative / total : 0 };
|
|
209
|
+
}
|
|
210
|
+
function verifyStatsByDrone(runs) {
|
|
211
|
+
const byName = /* @__PURE__ */ new Map();
|
|
212
|
+
for (const r of runs) {
|
|
213
|
+
if (!toVerifyVerdict(r.verify_verdict)) continue;
|
|
214
|
+
const name = (r.name ?? "").trim() || "(\u0438\u043C\u044F \u043D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043D\u043E)";
|
|
215
|
+
const bucket = byName.get(name);
|
|
216
|
+
if (bucket) bucket.push(r);
|
|
217
|
+
else byName.set(name, [r]);
|
|
218
|
+
}
|
|
219
|
+
return [...byName.entries()].map(([name, rs]) => ({ name, ...verifyStats(rs) })).sort((a, b) => b.negative - a.negative || b.total - a.total || a.name.localeCompare(b.name));
|
|
220
|
+
}
|
|
221
|
+
function verifyStatsLine(s) {
|
|
222
|
+
if (!s.total) return "\u0432\u0435\u0440\u0434\u0438\u043A\u0442\u043E\u0432 \u0432\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440\u0430 \u043F\u043E\u043A\u0430 \u043D\u0435\u0442";
|
|
223
|
+
const pct = Math.round(s.negativeShare * 100);
|
|
224
|
+
return `\u0432\u0435\u0440\u0434\u0438\u043A\u0442\u043E\u0432 ${s.total} \xB7 \u043E\u0442\u0440\u0438\u0446\u0430\u0442\u0435\u043B\u044C\u043D\u044B\u0445 ${s.negative} (${pct}%): \u043D\u0435 \u0432\u043E\u0441\u043F\u0440\u043E\u0438\u0437\u0432\u0435\u043B\u043E\u0441\u044C ${s.notReproduced}, \u043D\u0430\u0448\u043B\u0438 \u0434\u0435\u0444\u0435\u043A\u0442 ${s.foundIssues}`;
|
|
225
|
+
}
|
|
226
|
+
var VERIFY_VERDICTS, VERIFY_VERDICT_SET;
|
|
227
|
+
var init_verifyVerdict = __esm({
|
|
228
|
+
"../swarm-core/src/verifyVerdict.ts"() {
|
|
229
|
+
"use strict";
|
|
230
|
+
VERIFY_VERDICTS = ["confirmed", "not_reproduced", "found_issues"];
|
|
231
|
+
VERIFY_VERDICT_SET = new Set(VERIFY_VERDICTS);
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
// ../swarm-core/src/reviewPhase.ts
|
|
236
|
+
function reviewPhaseKindOf(label2) {
|
|
237
|
+
if (!label2) return null;
|
|
238
|
+
return PHASE_BY_LENGTH.find((k) => label2.startsWith(REVIEW_PHASE_TEXT[k])) ?? null;
|
|
239
|
+
}
|
|
240
|
+
var REVIEW_PHASE_TEXT, PHASE_BY_LENGTH;
|
|
241
|
+
var init_reviewPhase = __esm({
|
|
242
|
+
"../swarm-core/src/reviewPhase.ts"() {
|
|
243
|
+
"use strict";
|
|
244
|
+
init_verifyVerdict();
|
|
245
|
+
REVIEW_PHASE_TEXT = {
|
|
246
|
+
checks: "\u0418\u0434\u0443\u0442 \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0438",
|
|
247
|
+
awaiting_reviewer: "\u0417\u0435\u043B\u0451\u043D\u044B\u0439 \u2014 \u0436\u0434\u0451\u0442 \u0440\u0435\u0432\u044C\u044E\u0435\u0440\u0430",
|
|
248
|
+
ci_fixing: "CI \u043A\u0440\u0430\u0441\u043D\u044B\u0439 \u2014 \u0434\u0440\u043E\u043D \u0447\u0438\u043D\u0438\u0442",
|
|
249
|
+
in_review: "\u041D\u0430 \u0440\u0435\u0432\u044C\u044E",
|
|
250
|
+
changes: "\u041D\u0443\u0436\u043D\u044B \u043F\u0440\u0430\u0432\u043A\u0438 \u2014 \u0434\u0440\u043E\u043D \u043F\u0440\u0430\u0432\u0438\u0442",
|
|
251
|
+
verify_found: "\u0412\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440 \u043D\u0430\u0448\u0451\u043B \u2014 \u043D\u0443\u0436\u043D\u044B \u043F\u0440\u0430\u0432\u043A\u0438",
|
|
252
|
+
verify_not_reproduced: "\u0412\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440 \u043D\u0435 \u0432\u043E\u0441\u043F\u0440\u043E\u0438\u0437\u0432\u0451\u043B \u2014 \u043D\u0443\u0436\u043D\u044B \u043F\u0440\u0430\u0432\u043A\u0438",
|
|
253
|
+
awaiting_verify: "\u0417\u0435\u043B\u0451\u043D\u044B\u0439 \u2014 \u0436\u0434\u0451\u043C \u0432\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u044E",
|
|
254
|
+
verify_dead: "\u0412\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440 \u043D\u0435 \u0434\u0430\u043B \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \u2014 \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A",
|
|
255
|
+
ready: "\u0417\u0435\u043B\u0451\u043D\u044B\u0439 \u2014 \u0433\u043E\u0442\u043E\u0432 \u043A \u043C\u0435\u0440\u0436\u0443",
|
|
256
|
+
ready_awaiting_ci: "\u0413\u043E\u0442\u043E\u0432 \u043A \u043C\u0435\u0440\u0436\u0443 \u2014 \u0436\u0434\u0451\u0442 CI",
|
|
257
|
+
reviewer_dead: "\u0420\u0435\u0432\u044C\u044E\u0435\u0440 \u0443\u043C\u0435\u0440 \u2014 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \u043D\u0435\u0442, \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A"
|
|
258
|
+
};
|
|
259
|
+
PHASE_BY_LENGTH = Object.keys(REVIEW_PHASE_TEXT).sort(
|
|
260
|
+
(a, b) => REVIEW_PHASE_TEXT[b].length - REVIEW_PHASE_TEXT[a].length
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
|
|
167
265
|
// ../swarm-core/src/timeline.ts
|
|
168
266
|
function resolveDepRef(ref, lk) {
|
|
169
267
|
const mapped = lk.slugByCuid.get(ref);
|
|
@@ -335,7 +433,8 @@ function computeCounters(lanes) {
|
|
|
335
433
|
if (l.dependsOn.some((d) => !d.met)) c.waitingDep++;
|
|
336
434
|
else c.waitingSlot++;
|
|
337
435
|
} else if (l.status === "review") {
|
|
338
|
-
if (l.
|
|
436
|
+
if (reviewPhaseKindOf(l.reviewPhase) === "ready_awaiting_ci") c.waitingCI++;
|
|
437
|
+
else if (l.blocker.tone === "merge" || l.reviewVerdict === "approve") c.waitingMerge++;
|
|
339
438
|
else c.waitingCI++;
|
|
340
439
|
}
|
|
341
440
|
}
|
|
@@ -345,103 +444,11 @@ var DEFAULT_RUN_MS;
|
|
|
345
444
|
var init_timeline = __esm({
|
|
346
445
|
"../swarm-core/src/timeline.ts"() {
|
|
347
446
|
"use strict";
|
|
447
|
+
init_reviewPhase();
|
|
348
448
|
DEFAULT_RUN_MS = 25 * 6e4;
|
|
349
449
|
}
|
|
350
450
|
});
|
|
351
451
|
|
|
352
|
-
// ../swarm-core/src/verifyVerdict.ts
|
|
353
|
-
function isVerifyVerdict(v) {
|
|
354
|
-
return typeof v === "string" && VERIFY_VERDICT_SET.has(v);
|
|
355
|
-
}
|
|
356
|
-
function toVerifyVerdict(v) {
|
|
357
|
-
return isVerifyVerdict(v) ? v : null;
|
|
358
|
-
}
|
|
359
|
-
function isNegativeVerifyVerdict(v) {
|
|
360
|
-
return v === "not_reproduced" || v === "found_issues";
|
|
361
|
-
}
|
|
362
|
-
function verifyVerdictLabel(v) {
|
|
363
|
-
switch (v) {
|
|
364
|
-
case "confirmed":
|
|
365
|
-
return "\u041F\u041E\u0414\u0422\u0412\u0415\u0420\u0416\u0414\u0410\u042E";
|
|
366
|
-
case "not_reproduced":
|
|
367
|
-
return "\u041D\u0415 \u0412\u041E\u0421\u041F\u0420\u041E\u0418\u0417\u0412\u041E\u0414\u0418\u0422\u0421\u042F";
|
|
368
|
-
case "found_issues":
|
|
369
|
-
return "\u041D\u0410\u0428\u0401\u041B";
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
function verifyStats(runs) {
|
|
373
|
-
let confirmed = 0;
|
|
374
|
-
let notReproduced = 0;
|
|
375
|
-
let foundIssues = 0;
|
|
376
|
-
for (const r of runs) {
|
|
377
|
-
switch (toVerifyVerdict(r.verify_verdict)) {
|
|
378
|
-
case "confirmed":
|
|
379
|
-
confirmed++;
|
|
380
|
-
break;
|
|
381
|
-
case "not_reproduced":
|
|
382
|
-
notReproduced++;
|
|
383
|
-
break;
|
|
384
|
-
case "found_issues":
|
|
385
|
-
foundIssues++;
|
|
386
|
-
break;
|
|
387
|
-
default:
|
|
388
|
-
break;
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
const negative = notReproduced + foundIssues;
|
|
392
|
-
const total = confirmed + negative;
|
|
393
|
-
return { total, confirmed, notReproduced, foundIssues, negative, negativeShare: total ? negative / total : 0 };
|
|
394
|
-
}
|
|
395
|
-
function verifyStatsByDrone(runs) {
|
|
396
|
-
const byName = /* @__PURE__ */ new Map();
|
|
397
|
-
for (const r of runs) {
|
|
398
|
-
if (!toVerifyVerdict(r.verify_verdict)) continue;
|
|
399
|
-
const name = (r.name ?? "").trim() || "(\u0438\u043C\u044F \u043D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043D\u043E)";
|
|
400
|
-
const bucket = byName.get(name);
|
|
401
|
-
if (bucket) bucket.push(r);
|
|
402
|
-
else byName.set(name, [r]);
|
|
403
|
-
}
|
|
404
|
-
return [...byName.entries()].map(([name, rs]) => ({ name, ...verifyStats(rs) })).sort((a, b) => b.negative - a.negative || b.total - a.total || a.name.localeCompare(b.name));
|
|
405
|
-
}
|
|
406
|
-
function verifyStatsLine(s) {
|
|
407
|
-
if (!s.total) return "\u0432\u0435\u0440\u0434\u0438\u043A\u0442\u043E\u0432 \u0432\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440\u0430 \u043F\u043E\u043A\u0430 \u043D\u0435\u0442";
|
|
408
|
-
const pct = Math.round(s.negativeShare * 100);
|
|
409
|
-
return `\u0432\u0435\u0440\u0434\u0438\u043A\u0442\u043E\u0432 ${s.total} \xB7 \u043E\u0442\u0440\u0438\u0446\u0430\u0442\u0435\u043B\u044C\u043D\u044B\u0445 ${s.negative} (${pct}%): \u043D\u0435 \u0432\u043E\u0441\u043F\u0440\u043E\u0438\u0437\u0432\u0435\u043B\u043E\u0441\u044C ${s.notReproduced}, \u043D\u0430\u0448\u043B\u0438 \u0434\u0435\u0444\u0435\u043A\u0442 ${s.foundIssues}`;
|
|
410
|
-
}
|
|
411
|
-
var VERIFY_VERDICTS, VERIFY_VERDICT_SET;
|
|
412
|
-
var init_verifyVerdict = __esm({
|
|
413
|
-
"../swarm-core/src/verifyVerdict.ts"() {
|
|
414
|
-
"use strict";
|
|
415
|
-
VERIFY_VERDICTS = ["confirmed", "not_reproduced", "found_issues"];
|
|
416
|
-
VERIFY_VERDICT_SET = new Set(VERIFY_VERDICTS);
|
|
417
|
-
}
|
|
418
|
-
});
|
|
419
|
-
|
|
420
|
-
// ../swarm-core/src/reviewPhase.ts
|
|
421
|
-
var REVIEW_PHASE_TEXT, PHASE_BY_LENGTH;
|
|
422
|
-
var init_reviewPhase = __esm({
|
|
423
|
-
"../swarm-core/src/reviewPhase.ts"() {
|
|
424
|
-
"use strict";
|
|
425
|
-
init_verifyVerdict();
|
|
426
|
-
REVIEW_PHASE_TEXT = {
|
|
427
|
-
checks: "\u0418\u0434\u0443\u0442 \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0438",
|
|
428
|
-
awaiting_reviewer: "\u0417\u0435\u043B\u0451\u043D\u044B\u0439 \u2014 \u0436\u0434\u0451\u0442 \u0440\u0435\u0432\u044C\u044E\u0435\u0440\u0430",
|
|
429
|
-
ci_fixing: "CI \u043A\u0440\u0430\u0441\u043D\u044B\u0439 \u2014 \u0434\u0440\u043E\u043D \u0447\u0438\u043D\u0438\u0442",
|
|
430
|
-
in_review: "\u041D\u0430 \u0440\u0435\u0432\u044C\u044E",
|
|
431
|
-
changes: "\u041D\u0443\u0436\u043D\u044B \u043F\u0440\u0430\u0432\u043A\u0438 \u2014 \u0434\u0440\u043E\u043D \u043F\u0440\u0430\u0432\u0438\u0442",
|
|
432
|
-
verify_found: "\u0412\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440 \u043D\u0430\u0448\u0451\u043B \u2014 \u043D\u0443\u0436\u043D\u044B \u043F\u0440\u0430\u0432\u043A\u0438",
|
|
433
|
-
verify_not_reproduced: "\u0412\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440 \u043D\u0435 \u0432\u043E\u0441\u043F\u0440\u043E\u0438\u0437\u0432\u0451\u043B \u2014 \u043D\u0443\u0436\u043D\u044B \u043F\u0440\u0430\u0432\u043A\u0438",
|
|
434
|
-
awaiting_verify: "\u0417\u0435\u043B\u0451\u043D\u044B\u0439 \u2014 \u0436\u0434\u0451\u043C \u0432\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u044E",
|
|
435
|
-
verify_dead: "\u0412\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440 \u043D\u0435 \u0434\u0430\u043B \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \u2014 \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A",
|
|
436
|
-
ready: "\u0417\u0435\u043B\u0451\u043D\u044B\u0439 \u2014 \u0433\u043E\u0442\u043E\u0432 \u043A \u043C\u0435\u0440\u0436\u0443",
|
|
437
|
-
reviewer_dead: "\u0420\u0435\u0432\u044C\u044E\u0435\u0440 \u0443\u043C\u0435\u0440 \u2014 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \u043D\u0435\u0442, \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A"
|
|
438
|
-
};
|
|
439
|
-
PHASE_BY_LENGTH = Object.keys(REVIEW_PHASE_TEXT).sort(
|
|
440
|
-
(a, b) => REVIEW_PHASE_TEXT[b].length - REVIEW_PHASE_TEXT[a].length
|
|
441
|
-
);
|
|
442
|
-
}
|
|
443
|
-
});
|
|
444
|
-
|
|
445
452
|
// ../swarm-core/src/mergeGate.ts
|
|
446
453
|
function computeMergeGate(input) {
|
|
447
454
|
if (!input.hasRun) {
|
|
@@ -1716,7 +1723,7 @@ var init_effort = __esm({
|
|
|
1716
1723
|
});
|
|
1717
1724
|
|
|
1718
1725
|
// ../swarm-core/src/queuePlan.ts
|
|
1719
|
-
var MIN_MS, QUEUE_NOMINAL_RUN_MS, QUEUE_MIN_TAIL_MS
|
|
1726
|
+
var MIN_MS, QUEUE_NOMINAL_RUN_MS, QUEUE_MIN_TAIL_MS;
|
|
1720
1727
|
var init_queuePlan = __esm({
|
|
1721
1728
|
"../swarm-core/src/queuePlan.ts"() {
|
|
1722
1729
|
"use strict";
|
|
@@ -1727,7 +1734,6 @@ var init_queuePlan = __esm({
|
|
|
1727
1734
|
MIN_MS = 6e4;
|
|
1728
1735
|
QUEUE_NOMINAL_RUN_MS = 25 * MIN_MS;
|
|
1729
1736
|
QUEUE_MIN_TAIL_MS = 3 * MIN_MS;
|
|
1730
|
-
QUEUE_REVIEW_TAIL_MS = 10 * MIN_MS;
|
|
1731
1737
|
}
|
|
1732
1738
|
});
|
|
1733
1739
|
|
|
@@ -3486,7 +3492,7 @@ var init_package = __esm({
|
|
|
3486
3492
|
"package.json"() {
|
|
3487
3493
|
package_default = {
|
|
3488
3494
|
name: "@taskless-app/swarm",
|
|
3489
|
-
version: "0.6.
|
|
3495
|
+
version: "0.6.52",
|
|
3490
3496
|
type: "module",
|
|
3491
3497
|
description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
|
|
3492
3498
|
keywords: [
|
|
@@ -3537,7 +3543,7 @@ var init_package = __esm({
|
|
|
3537
3543
|
execa: "^10.0.0",
|
|
3538
3544
|
zod: "3.25.76"
|
|
3539
3545
|
},
|
|
3540
|
-
gitHead: "
|
|
3546
|
+
gitHead: "f77e0473fd2f80e4418ceadd1b29b9a70241fc04"
|
|
3541
3547
|
};
|
|
3542
3548
|
}
|
|
3543
3549
|
});
|
package/dist/overmind.js
CHANGED
|
@@ -133,6 +133,123 @@ var init_types = __esm({
|
|
|
133
133
|
}
|
|
134
134
|
});
|
|
135
135
|
|
|
136
|
+
// ../swarm-core/src/verifyVerdict.ts
|
|
137
|
+
function isVerifyVerdict(v) {
|
|
138
|
+
return typeof v === "string" && VERIFY_VERDICT_SET.has(v);
|
|
139
|
+
}
|
|
140
|
+
function toVerifyVerdict(v) {
|
|
141
|
+
return isVerifyVerdict(v) ? v : null;
|
|
142
|
+
}
|
|
143
|
+
function isNegativeVerifyVerdict(v) {
|
|
144
|
+
return v === "not_reproduced" || v === "found_issues";
|
|
145
|
+
}
|
|
146
|
+
function verifyVerdictLabel(v) {
|
|
147
|
+
switch (v) {
|
|
148
|
+
case "confirmed":
|
|
149
|
+
return "\u041F\u041E\u0414\u0422\u0412\u0415\u0420\u0416\u0414\u0410\u042E";
|
|
150
|
+
case "not_reproduced":
|
|
151
|
+
return "\u041D\u0415 \u0412\u041E\u0421\u041F\u0420\u041E\u0418\u0417\u0412\u041E\u0414\u0418\u0422\u0421\u042F";
|
|
152
|
+
case "found_issues":
|
|
153
|
+
return "\u041D\u0410\u0428\u0401\u041B";
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
function parseVerifyVerdictComment(text) {
|
|
157
|
+
const line = (text ?? "").split("\n").find((l) => l.trim().length > 0);
|
|
158
|
+
if (!line) return null;
|
|
159
|
+
let best = null;
|
|
160
|
+
for (const [verdict, marks] of VERDICT_MARKS) {
|
|
161
|
+
for (const mark of marks) {
|
|
162
|
+
const at = line.indexOf(mark);
|
|
163
|
+
if (at < 0) continue;
|
|
164
|
+
if (!best || at < best.at) best = { verdict, at };
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return best?.verdict ?? null;
|
|
168
|
+
}
|
|
169
|
+
var VERIFY_VERDICTS, VERIFY_VERDICT_SET, VERDICT_MARKS;
|
|
170
|
+
var init_verifyVerdict = __esm({
|
|
171
|
+
"../swarm-core/src/verifyVerdict.ts"() {
|
|
172
|
+
"use strict";
|
|
173
|
+
VERIFY_VERDICTS = ["confirmed", "not_reproduced", "found_issues"];
|
|
174
|
+
VERIFY_VERDICT_SET = new Set(VERIFY_VERDICTS);
|
|
175
|
+
VERDICT_MARKS = [
|
|
176
|
+
["not_reproduced", ["\u041D\u0415 \u0412\u041E\u0421\u041F\u0420\u041E\u0418\u0417\u0412\u041E\u0414\u0418\u0422\u0421\u042F", "NOT REPRODUCED", "\u041D\u0415 \u041F\u041E\u0414\u0422\u0412\u0415\u0420\u0416\u0414\u0410\u042E", "NOT CONFIRMED"]],
|
|
177
|
+
["found_issues", ["\u041D\u0410\u0428\u0401\u041B", "\u041D\u0410\u0428\u0415\u041B", "FOUND"]],
|
|
178
|
+
["confirmed", ["\u041F\u041E\u0414\u0422\u0412\u0415\u0420\u0416\u0414\u0410\u042E", "CONFIRMED"]]
|
|
179
|
+
];
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// ../swarm-core/src/reviewPhase.ts
|
|
184
|
+
function reviewPhaseKindOf(label2) {
|
|
185
|
+
if (!label2) return null;
|
|
186
|
+
return PHASE_BY_LENGTH.find((k) => label2.startsWith(REVIEW_PHASE_TEXT[k])) ?? null;
|
|
187
|
+
}
|
|
188
|
+
function computeReviewPhase(input) {
|
|
189
|
+
const { reviewVerdict, ciRed } = input;
|
|
190
|
+
const verifyVerdict = input.verifyVerdict ?? null;
|
|
191
|
+
if (!ciRed && isNegativeVerifyVerdict(verifyVerdict)) {
|
|
192
|
+
const kind = verifyVerdict === "found_issues" ? "verify_found" : "verify_not_reproduced";
|
|
193
|
+
return { kind, label: REVIEW_PHASE_TEXT[kind], ready: false };
|
|
194
|
+
}
|
|
195
|
+
if (reviewVerdict === "approve" && !ciRed) {
|
|
196
|
+
if (verifyVerdict === "confirmed" || input.verifyExpected === false) {
|
|
197
|
+
if (input.ciGreen !== true) {
|
|
198
|
+
return { kind: "ready_awaiting_ci", label: REVIEW_PHASE_TEXT.ready_awaiting_ci, ready: false };
|
|
199
|
+
}
|
|
200
|
+
return { kind: "ready", label: REVIEW_PHASE_TEXT.ready, ready: true };
|
|
201
|
+
}
|
|
202
|
+
if (input.verifierDead) {
|
|
203
|
+
return { kind: "verify_dead", label: REVIEW_PHASE_TEXT.verify_dead, ready: true };
|
|
204
|
+
}
|
|
205
|
+
return { kind: "awaiting_verify", label: REVIEW_PHASE_TEXT.awaiting_verify, ready: false };
|
|
206
|
+
}
|
|
207
|
+
if (ciRed) {
|
|
208
|
+
const jobs = (input.ciFailing ?? []).filter((j) => j && j !== "?");
|
|
209
|
+
const label3 = jobs.length ? `${REVIEW_PHASE_TEXT.ci_fixing}: ${jobs.join(", ")}` : REVIEW_PHASE_TEXT.ci_fixing;
|
|
210
|
+
return { kind: "ci_fixing", label: label3, ready: false };
|
|
211
|
+
}
|
|
212
|
+
if (reviewVerdict === "changes") {
|
|
213
|
+
return { kind: "changes", label: REVIEW_PHASE_TEXT.changes, ready: false };
|
|
214
|
+
}
|
|
215
|
+
if (input.reviewerName) {
|
|
216
|
+
return { kind: "in_review", label: `\u041D\u0430 \u0440\u0435\u0432\u044C\u044E \u0443 ${input.reviewerName}`, ready: false };
|
|
217
|
+
}
|
|
218
|
+
if (input.reviewerDead) {
|
|
219
|
+
return { kind: "reviewer_dead", label: REVIEW_PHASE_TEXT.reviewer_dead, ready: true };
|
|
220
|
+
}
|
|
221
|
+
const p = input.ciProgress;
|
|
222
|
+
if (p && p.total > 0 && p.pass >= p.total) {
|
|
223
|
+
return { kind: "awaiting_reviewer", label: REVIEW_PHASE_TEXT.awaiting_reviewer, ready: false };
|
|
224
|
+
}
|
|
225
|
+
const label2 = p && p.total > 0 ? `${REVIEW_PHASE_TEXT.checks} \u2014 CI ${p.pass}/${p.total}` : REVIEW_PHASE_TEXT.checks;
|
|
226
|
+
return { kind: "checks", label: label2, ready: false };
|
|
227
|
+
}
|
|
228
|
+
var REVIEW_PHASE_TEXT, PHASE_BY_LENGTH;
|
|
229
|
+
var init_reviewPhase = __esm({
|
|
230
|
+
"../swarm-core/src/reviewPhase.ts"() {
|
|
231
|
+
"use strict";
|
|
232
|
+
init_verifyVerdict();
|
|
233
|
+
REVIEW_PHASE_TEXT = {
|
|
234
|
+
checks: "\u0418\u0434\u0443\u0442 \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0438",
|
|
235
|
+
awaiting_reviewer: "\u0417\u0435\u043B\u0451\u043D\u044B\u0439 \u2014 \u0436\u0434\u0451\u0442 \u0440\u0435\u0432\u044C\u044E\u0435\u0440\u0430",
|
|
236
|
+
ci_fixing: "CI \u043A\u0440\u0430\u0441\u043D\u044B\u0439 \u2014 \u0434\u0440\u043E\u043D \u0447\u0438\u043D\u0438\u0442",
|
|
237
|
+
in_review: "\u041D\u0430 \u0440\u0435\u0432\u044C\u044E",
|
|
238
|
+
changes: "\u041D\u0443\u0436\u043D\u044B \u043F\u0440\u0430\u0432\u043A\u0438 \u2014 \u0434\u0440\u043E\u043D \u043F\u0440\u0430\u0432\u0438\u0442",
|
|
239
|
+
verify_found: "\u0412\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440 \u043D\u0430\u0448\u0451\u043B \u2014 \u043D\u0443\u0436\u043D\u044B \u043F\u0440\u0430\u0432\u043A\u0438",
|
|
240
|
+
verify_not_reproduced: "\u0412\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440 \u043D\u0435 \u0432\u043E\u0441\u043F\u0440\u043E\u0438\u0437\u0432\u0451\u043B \u2014 \u043D\u0443\u0436\u043D\u044B \u043F\u0440\u0430\u0432\u043A\u0438",
|
|
241
|
+
awaiting_verify: "\u0417\u0435\u043B\u0451\u043D\u044B\u0439 \u2014 \u0436\u0434\u0451\u043C \u0432\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u044E",
|
|
242
|
+
verify_dead: "\u0412\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440 \u043D\u0435 \u0434\u0430\u043B \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \u2014 \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A",
|
|
243
|
+
ready: "\u0417\u0435\u043B\u0451\u043D\u044B\u0439 \u2014 \u0433\u043E\u0442\u043E\u0432 \u043A \u043C\u0435\u0440\u0436\u0443",
|
|
244
|
+
ready_awaiting_ci: "\u0413\u043E\u0442\u043E\u0432 \u043A \u043C\u0435\u0440\u0436\u0443 \u2014 \u0436\u0434\u0451\u0442 CI",
|
|
245
|
+
reviewer_dead: "\u0420\u0435\u0432\u044C\u044E\u0435\u0440 \u0443\u043C\u0435\u0440 \u2014 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \u043D\u0435\u0442, \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A"
|
|
246
|
+
};
|
|
247
|
+
PHASE_BY_LENGTH = Object.keys(REVIEW_PHASE_TEXT).sort(
|
|
248
|
+
(a, b) => REVIEW_PHASE_TEXT[b].length - REVIEW_PHASE_TEXT[a].length
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
|
|
136
253
|
// ../swarm-core/src/timeline.ts
|
|
137
254
|
function resolveDepRef(ref, lk) {
|
|
138
255
|
const mapped = lk.slugByCuid.get(ref);
|
|
@@ -304,7 +421,8 @@ function computeCounters(lanes) {
|
|
|
304
421
|
if (l.dependsOn.some((d) => !d.met)) c.waitingDep++;
|
|
305
422
|
else c.waitingSlot++;
|
|
306
423
|
} else if (l.status === "review") {
|
|
307
|
-
if (l.
|
|
424
|
+
if (reviewPhaseKindOf(l.reviewPhase) === "ready_awaiting_ci") c.waitingCI++;
|
|
425
|
+
else if (l.blocker.tone === "merge" || l.reviewVerdict === "approve") c.waitingMerge++;
|
|
308
426
|
else c.waitingCI++;
|
|
309
427
|
}
|
|
310
428
|
}
|
|
@@ -314,119 +432,11 @@ var DEFAULT_RUN_MS;
|
|
|
314
432
|
var init_timeline = __esm({
|
|
315
433
|
"../swarm-core/src/timeline.ts"() {
|
|
316
434
|
"use strict";
|
|
435
|
+
init_reviewPhase();
|
|
317
436
|
DEFAULT_RUN_MS = 25 * 6e4;
|
|
318
437
|
}
|
|
319
438
|
});
|
|
320
439
|
|
|
321
|
-
// ../swarm-core/src/verifyVerdict.ts
|
|
322
|
-
function isVerifyVerdict(v) {
|
|
323
|
-
return typeof v === "string" && VERIFY_VERDICT_SET.has(v);
|
|
324
|
-
}
|
|
325
|
-
function toVerifyVerdict(v) {
|
|
326
|
-
return isVerifyVerdict(v) ? v : null;
|
|
327
|
-
}
|
|
328
|
-
function isNegativeVerifyVerdict(v) {
|
|
329
|
-
return v === "not_reproduced" || v === "found_issues";
|
|
330
|
-
}
|
|
331
|
-
function verifyVerdictLabel(v) {
|
|
332
|
-
switch (v) {
|
|
333
|
-
case "confirmed":
|
|
334
|
-
return "\u041F\u041E\u0414\u0422\u0412\u0415\u0420\u0416\u0414\u0410\u042E";
|
|
335
|
-
case "not_reproduced":
|
|
336
|
-
return "\u041D\u0415 \u0412\u041E\u0421\u041F\u0420\u041E\u0418\u0417\u0412\u041E\u0414\u0418\u0422\u0421\u042F";
|
|
337
|
-
case "found_issues":
|
|
338
|
-
return "\u041D\u0410\u0428\u0401\u041B";
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
function parseVerifyVerdictComment(text) {
|
|
342
|
-
const line = (text ?? "").split("\n").find((l) => l.trim().length > 0);
|
|
343
|
-
if (!line) return null;
|
|
344
|
-
let best = null;
|
|
345
|
-
for (const [verdict, marks] of VERDICT_MARKS) {
|
|
346
|
-
for (const mark of marks) {
|
|
347
|
-
const at = line.indexOf(mark);
|
|
348
|
-
if (at < 0) continue;
|
|
349
|
-
if (!best || at < best.at) best = { verdict, at };
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
return best?.verdict ?? null;
|
|
353
|
-
}
|
|
354
|
-
var VERIFY_VERDICTS, VERIFY_VERDICT_SET, VERDICT_MARKS;
|
|
355
|
-
var init_verifyVerdict = __esm({
|
|
356
|
-
"../swarm-core/src/verifyVerdict.ts"() {
|
|
357
|
-
"use strict";
|
|
358
|
-
VERIFY_VERDICTS = ["confirmed", "not_reproduced", "found_issues"];
|
|
359
|
-
VERIFY_VERDICT_SET = new Set(VERIFY_VERDICTS);
|
|
360
|
-
VERDICT_MARKS = [
|
|
361
|
-
["not_reproduced", ["\u041D\u0415 \u0412\u041E\u0421\u041F\u0420\u041E\u0418\u0417\u0412\u041E\u0414\u0418\u0422\u0421\u042F", "NOT REPRODUCED", "\u041D\u0415 \u041F\u041E\u0414\u0422\u0412\u0415\u0420\u0416\u0414\u0410\u042E", "NOT CONFIRMED"]],
|
|
362
|
-
["found_issues", ["\u041D\u0410\u0428\u0401\u041B", "\u041D\u0410\u0428\u0415\u041B", "FOUND"]],
|
|
363
|
-
["confirmed", ["\u041F\u041E\u0414\u0422\u0412\u0415\u0420\u0416\u0414\u0410\u042E", "CONFIRMED"]]
|
|
364
|
-
];
|
|
365
|
-
}
|
|
366
|
-
});
|
|
367
|
-
|
|
368
|
-
// ../swarm-core/src/reviewPhase.ts
|
|
369
|
-
function computeReviewPhase(input) {
|
|
370
|
-
const { reviewVerdict, ciRed } = input;
|
|
371
|
-
const verifyVerdict = input.verifyVerdict ?? null;
|
|
372
|
-
if (!ciRed && isNegativeVerifyVerdict(verifyVerdict)) {
|
|
373
|
-
const kind = verifyVerdict === "found_issues" ? "verify_found" : "verify_not_reproduced";
|
|
374
|
-
return { kind, label: REVIEW_PHASE_TEXT[kind], ready: false };
|
|
375
|
-
}
|
|
376
|
-
if (reviewVerdict === "approve" && !ciRed) {
|
|
377
|
-
if (verifyVerdict === "confirmed" || input.verifyExpected === false) {
|
|
378
|
-
return { kind: "ready", label: REVIEW_PHASE_TEXT.ready, ready: true };
|
|
379
|
-
}
|
|
380
|
-
if (input.verifierDead) {
|
|
381
|
-
return { kind: "verify_dead", label: REVIEW_PHASE_TEXT.verify_dead, ready: true };
|
|
382
|
-
}
|
|
383
|
-
return { kind: "awaiting_verify", label: REVIEW_PHASE_TEXT.awaiting_verify, ready: false };
|
|
384
|
-
}
|
|
385
|
-
if (ciRed) {
|
|
386
|
-
const jobs = (input.ciFailing ?? []).filter((j) => j && j !== "?");
|
|
387
|
-
const label3 = jobs.length ? `${REVIEW_PHASE_TEXT.ci_fixing}: ${jobs.join(", ")}` : REVIEW_PHASE_TEXT.ci_fixing;
|
|
388
|
-
return { kind: "ci_fixing", label: label3, ready: false };
|
|
389
|
-
}
|
|
390
|
-
if (reviewVerdict === "changes") {
|
|
391
|
-
return { kind: "changes", label: REVIEW_PHASE_TEXT.changes, ready: false };
|
|
392
|
-
}
|
|
393
|
-
if (input.reviewerName) {
|
|
394
|
-
return { kind: "in_review", label: `\u041D\u0430 \u0440\u0435\u0432\u044C\u044E \u0443 ${input.reviewerName}`, ready: false };
|
|
395
|
-
}
|
|
396
|
-
if (input.reviewerDead) {
|
|
397
|
-
return { kind: "reviewer_dead", label: REVIEW_PHASE_TEXT.reviewer_dead, ready: true };
|
|
398
|
-
}
|
|
399
|
-
const p = input.ciProgress;
|
|
400
|
-
if (p && p.total > 0 && p.pass >= p.total) {
|
|
401
|
-
return { kind: "awaiting_reviewer", label: REVIEW_PHASE_TEXT.awaiting_reviewer, ready: false };
|
|
402
|
-
}
|
|
403
|
-
const label2 = p && p.total > 0 ? `${REVIEW_PHASE_TEXT.checks} \u2014 CI ${p.pass}/${p.total}` : REVIEW_PHASE_TEXT.checks;
|
|
404
|
-
return { kind: "checks", label: label2, ready: false };
|
|
405
|
-
}
|
|
406
|
-
var REVIEW_PHASE_TEXT, PHASE_BY_LENGTH;
|
|
407
|
-
var init_reviewPhase = __esm({
|
|
408
|
-
"../swarm-core/src/reviewPhase.ts"() {
|
|
409
|
-
"use strict";
|
|
410
|
-
init_verifyVerdict();
|
|
411
|
-
REVIEW_PHASE_TEXT = {
|
|
412
|
-
checks: "\u0418\u0434\u0443\u0442 \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0438",
|
|
413
|
-
awaiting_reviewer: "\u0417\u0435\u043B\u0451\u043D\u044B\u0439 \u2014 \u0436\u0434\u0451\u0442 \u0440\u0435\u0432\u044C\u044E\u0435\u0440\u0430",
|
|
414
|
-
ci_fixing: "CI \u043A\u0440\u0430\u0441\u043D\u044B\u0439 \u2014 \u0434\u0440\u043E\u043D \u0447\u0438\u043D\u0438\u0442",
|
|
415
|
-
in_review: "\u041D\u0430 \u0440\u0435\u0432\u044C\u044E",
|
|
416
|
-
changes: "\u041D\u0443\u0436\u043D\u044B \u043F\u0440\u0430\u0432\u043A\u0438 \u2014 \u0434\u0440\u043E\u043D \u043F\u0440\u0430\u0432\u0438\u0442",
|
|
417
|
-
verify_found: "\u0412\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440 \u043D\u0430\u0448\u0451\u043B \u2014 \u043D\u0443\u0436\u043D\u044B \u043F\u0440\u0430\u0432\u043A\u0438",
|
|
418
|
-
verify_not_reproduced: "\u0412\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440 \u043D\u0435 \u0432\u043E\u0441\u043F\u0440\u043E\u0438\u0437\u0432\u0451\u043B \u2014 \u043D\u0443\u0436\u043D\u044B \u043F\u0440\u0430\u0432\u043A\u0438",
|
|
419
|
-
awaiting_verify: "\u0417\u0435\u043B\u0451\u043D\u044B\u0439 \u2014 \u0436\u0434\u0451\u043C \u0432\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u044E",
|
|
420
|
-
verify_dead: "\u0412\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440 \u043D\u0435 \u0434\u0430\u043B \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \u2014 \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A",
|
|
421
|
-
ready: "\u0417\u0435\u043B\u0451\u043D\u044B\u0439 \u2014 \u0433\u043E\u0442\u043E\u0432 \u043A \u043C\u0435\u0440\u0436\u0443",
|
|
422
|
-
reviewer_dead: "\u0420\u0435\u0432\u044C\u044E\u0435\u0440 \u0443\u043C\u0435\u0440 \u2014 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \u043D\u0435\u0442, \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A"
|
|
423
|
-
};
|
|
424
|
-
PHASE_BY_LENGTH = Object.keys(REVIEW_PHASE_TEXT).sort(
|
|
425
|
-
(a, b) => REVIEW_PHASE_TEXT[b].length - REVIEW_PHASE_TEXT[a].length
|
|
426
|
-
);
|
|
427
|
-
}
|
|
428
|
-
});
|
|
429
|
-
|
|
430
440
|
// ../swarm-core/src/mergeGate.ts
|
|
431
441
|
function computeMergeGate(input) {
|
|
432
442
|
if (!input.hasRun) {
|
|
@@ -1934,7 +1944,7 @@ var init_effort = __esm({
|
|
|
1934
1944
|
});
|
|
1935
1945
|
|
|
1936
1946
|
// ../swarm-core/src/queuePlan.ts
|
|
1937
|
-
var MIN_MS, QUEUE_NOMINAL_RUN_MS, QUEUE_MIN_TAIL_MS
|
|
1947
|
+
var MIN_MS, QUEUE_NOMINAL_RUN_MS, QUEUE_MIN_TAIL_MS;
|
|
1938
1948
|
var init_queuePlan = __esm({
|
|
1939
1949
|
"../swarm-core/src/queuePlan.ts"() {
|
|
1940
1950
|
"use strict";
|
|
@@ -1945,7 +1955,6 @@ var init_queuePlan = __esm({
|
|
|
1945
1955
|
MIN_MS = 6e4;
|
|
1946
1956
|
QUEUE_NOMINAL_RUN_MS = 25 * MIN_MS;
|
|
1947
1957
|
QUEUE_MIN_TAIL_MS = 3 * MIN_MS;
|
|
1948
|
-
QUEUE_REVIEW_TAIL_MS = 10 * MIN_MS;
|
|
1949
1958
|
}
|
|
1950
1959
|
});
|
|
1951
1960
|
|
|
@@ -12480,7 +12489,7 @@ function parkReasonUnretriable(reason) {
|
|
|
12480
12489
|
// package.json
|
|
12481
12490
|
var package_default = {
|
|
12482
12491
|
name: "@taskless-app/swarm",
|
|
12483
|
-
version: "0.6.
|
|
12492
|
+
version: "0.6.52",
|
|
12484
12493
|
type: "module",
|
|
12485
12494
|
description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
|
|
12486
12495
|
keywords: [
|
|
@@ -12531,7 +12540,7 @@ var package_default = {
|
|
|
12531
12540
|
execa: "^10.0.0",
|
|
12532
12541
|
zod: "3.25.76"
|
|
12533
12542
|
},
|
|
12534
|
-
gitHead: "
|
|
12543
|
+
gitHead: "f77e0473fd2f80e4418ceadd1b29b9a70241fc04"
|
|
12535
12544
|
};
|
|
12536
12545
|
|
|
12537
12546
|
// src/version.ts
|
|
@@ -14932,6 +14941,7 @@ function buildAnomalyCandidates(db, now, thresholds, opts) {
|
|
|
14932
14941
|
if (run.resolves_run_id != null || run.reviews_run_id != null) continue;
|
|
14933
14942
|
if (!run.pr_url) continue;
|
|
14934
14943
|
if (run.review_verdict === "approve") {
|
|
14944
|
+
if (reviewPhaseKindOf(run.review_phase) === "ready_awaiting_ci") continue;
|
|
14935
14945
|
candidates.push({
|
|
14936
14946
|
key: `pr_stuck_merge:${run.id}`,
|
|
14937
14947
|
minDurationMs: thresholds.prStuckMergeMs,
|
|
@@ -18093,6 +18103,14 @@ function compareQueuePriority(a, b) {
|
|
|
18093
18103
|
function orderQueue(runs) {
|
|
18094
18104
|
return [...runs].sort(compareQueuePriority);
|
|
18095
18105
|
}
|
|
18106
|
+
function placeReturners(returners, queued, unmet) {
|
|
18107
|
+
const awaited = new Set(queued.flatMap(unmet));
|
|
18108
|
+
const first = (r) => r.entry?.headPassed === true || awaited.has(r.run.task_slug);
|
|
18109
|
+
return {
|
|
18110
|
+
beforeHead: returners.filter(first),
|
|
18111
|
+
afterHead: returners.filter((r) => !first(r))
|
|
18112
|
+
};
|
|
18113
|
+
}
|
|
18096
18114
|
|
|
18097
18115
|
// src/watchdog.ts
|
|
18098
18116
|
init_src();
|
|
@@ -18476,6 +18494,7 @@ var CONFLICT_RESOLVE_COOLDOWN_MS = Number(process.env.SWARM_CONFLICT_COOLDOWN_MS
|
|
|
18476
18494
|
var RESOLVE_LIMIT = Number(process.env.SWARM_RESOLVE_LIMIT) || 3;
|
|
18477
18495
|
var BASE_MERGED_MARK = "\u0431\u0430\u0437\u0430 \u0432\u043B\u0438\u0442\u0430 \u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0447\u0435\u0441\u043A\u0438";
|
|
18478
18496
|
var BASE_NOOP_MARK = "\u0432\u043B\u0438\u0432\u0430\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E";
|
|
18497
|
+
var RESOLVER_RESOLVED_MARK = "\u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442 \u0441 main \u0440\u0430\u0437\u0440\u0435\u0448\u0451\u043D \u0440\u0435\u0437\u043E\u043B\u0432\u0435\u0440\u043E\u043C";
|
|
18479
18498
|
var REVIEW_SPAWN_LIMIT = Number(process.env.SWARM_REVIEW_SPAWN_LIMIT) || 3;
|
|
18480
18499
|
var REVIEW_CYCLE_LIMIT = Number(process.env.SWARM_REVIEW_CYCLE_LIMIT) || 3;
|
|
18481
18500
|
var CI_WAKE_LIMIT = Number(process.env.SWARM_CI_WAKE_LIMIT) || 3;
|
|
@@ -18602,6 +18621,26 @@ function lastCiCheckMs(db, runId) {
|
|
|
18602
18621
|
if (!evs.length) return null;
|
|
18603
18622
|
return Date.parse(evs[evs.length - 1].at.replace(" ", "T") + "Z");
|
|
18604
18623
|
}
|
|
18624
|
+
function headMovedBy(e) {
|
|
18625
|
+
if (e.kind !== "conflict") return false;
|
|
18626
|
+
const text = e.text ?? "";
|
|
18627
|
+
return text.startsWith(BASE_MERGED_MARK) || text.includes(RESOLVER_RESOLVED_MARK);
|
|
18628
|
+
}
|
|
18629
|
+
function ciGreenForHead(db, run, events) {
|
|
18630
|
+
if (ciWatchCloseKind(db, run) != null) return true;
|
|
18631
|
+
for (let i = events.length - 1; i >= 0; i -= 1) {
|
|
18632
|
+
const e = events[i];
|
|
18633
|
+
if (headMovedBy(e)) return false;
|
|
18634
|
+
if (e.kind === "ci_wake") {
|
|
18635
|
+
if ((e.text ?? "").startsWith("\u043F\u043E\u0431\u0443\u0434\u043A\u0430 \u0441\u043E\u0440\u0432\u0430\u043B\u0430\u0441\u044C")) continue;
|
|
18636
|
+
return false;
|
|
18637
|
+
}
|
|
18638
|
+
if (e.kind === "ci_checked") {
|
|
18639
|
+
return /^CI (pass|зелёный) /.test(e.text ?? "");
|
|
18640
|
+
}
|
|
18641
|
+
}
|
|
18642
|
+
return false;
|
|
18643
|
+
}
|
|
18605
18644
|
function reviewPhaseInputFor(deps, run, projectWantsVerify = true) {
|
|
18606
18645
|
const events = deps.db.events(run.id);
|
|
18607
18646
|
let ciRed = false;
|
|
@@ -18632,6 +18671,7 @@ function reviewPhaseInputFor(deps, run, projectWantsVerify = true) {
|
|
|
18632
18671
|
return {
|
|
18633
18672
|
reviewVerdict: run.review_verdict,
|
|
18634
18673
|
ciRed,
|
|
18674
|
+
ciGreen: ciGreenForHead(deps.db, run, events),
|
|
18635
18675
|
ciFailing,
|
|
18636
18676
|
ciProgress,
|
|
18637
18677
|
reviewerName: reviewer?.name ?? null,
|
|
@@ -19604,6 +19644,7 @@ async function handleCi(deps, run, now, worktreeExists, hasTranscript, droneCap)
|
|
|
19604
19644
|
return;
|
|
19605
19645
|
}
|
|
19606
19646
|
const ci = await deps.ci.checks(run.pr_url).catch(() => null);
|
|
19647
|
+
if (ci?.bucket !== "fail" && deps.returners?.get(run.id)?.kind === "ci") deps.returners.delete(run.id);
|
|
19607
19648
|
if (ci?.bucket === "denied") {
|
|
19608
19649
|
const seen = ciDeniedStreak(deps.db, run.id) + 1;
|
|
19609
19650
|
if (seen < CI_DENIED_LIMIT) {
|
|
@@ -19637,7 +19678,11 @@ async function handleCi(deps, run, now, worktreeExists, hasTranscript, droneCap)
|
|
|
19637
19678
|
return;
|
|
19638
19679
|
}
|
|
19639
19680
|
if (ci?.bucket === "fail") {
|
|
19640
|
-
|
|
19681
|
+
const capFull = countActiveDrones(deps.db) >= droneCap;
|
|
19682
|
+
if (await wakeAuthorOnRedCi(deps, run, now, ci, worktreeExists, hasTranscript, droneCap)) {
|
|
19683
|
+
deps.returners?.delete(run.id);
|
|
19684
|
+
} else {
|
|
19685
|
+
if (capFull || deps.returners?.get(run.id)?.kind === "ci") noteReturner(deps, run, now, { kind: "ci", ci, seenAt: now, since: now });
|
|
19641
19686
|
deps.db.addEvent(run.id, "ci_checked", `CI fail (${ci.pass}/${ci.total}) \u2014 \u0431\u0443\u0434\u0438\u0442\u044C \u0430\u0432\u0442\u043E\u0440\u0430 \u0441\u0435\u0439\u0447\u0430\u0441 \u043D\u0435\u0447\u0435\u043C/\u0440\u0430\u043D\u043E, \u0436\u0434\u0443 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0435\u0433\u043E \u043E\u043A\u043D\u0430`);
|
|
19642
19687
|
}
|
|
19643
19688
|
return;
|
|
@@ -20373,10 +20418,57 @@ async function failBeforeStart(deps, run, reason, opts = {}) {
|
|
|
20373
20418
|
}
|
|
20374
20419
|
return "failed";
|
|
20375
20420
|
}
|
|
20376
|
-
async function batchSpawnQueue(deps, droneCap) {
|
|
20377
|
-
|
|
20421
|
+
async function batchSpawnQueue(deps, droneCap, now) {
|
|
20422
|
+
const queue = orderQueue(deps.db.listRuns("queued"));
|
|
20423
|
+
const { beforeHead, afterHead } = placeReturners(pendingReturners(deps), queue, (q) => deps.db.unmetDeps(q.depends_on));
|
|
20424
|
+
await liftReturners(deps, beforeHead, now, droneCap);
|
|
20425
|
+
let afterHeadDue = afterHead.length > 0;
|
|
20426
|
+
for (const run of queue) {
|
|
20378
20427
|
if (countActiveDrones(deps.db) >= droneCap) break;
|
|
20379
|
-
await trySpawnQueuedRun(deps, run)
|
|
20428
|
+
if (await trySpawnQueuedRun(deps, run) === "spawned" && afterHeadDue) {
|
|
20429
|
+
afterHeadDue = false;
|
|
20430
|
+
for (const { entry } of afterHead) entry.headPassed = true;
|
|
20431
|
+
await liftReturners(deps, afterHead, now, droneCap);
|
|
20432
|
+
}
|
|
20433
|
+
}
|
|
20434
|
+
if (afterHeadDue) await liftReturners(deps, afterHead, now, droneCap);
|
|
20435
|
+
}
|
|
20436
|
+
var RETURNER_CI_TTL_MS = 3 * CI_CHECK_COOLDOWN_MS;
|
|
20437
|
+
function noteReturner(deps, run, now, entry) {
|
|
20438
|
+
const pool = deps.returners ??= /* @__PURE__ */ new Map();
|
|
20439
|
+
const prev = pool.get(run.id);
|
|
20440
|
+
pool.set(run.id, { ...entry, since: prev?.since ?? entry.since, ...prev?.headPassed ? { headPassed: true } : {} });
|
|
20441
|
+
}
|
|
20442
|
+
function pendingReturners(deps) {
|
|
20443
|
+
const pool = deps.returners;
|
|
20444
|
+
if (!pool?.size) return [];
|
|
20445
|
+
const out = [];
|
|
20446
|
+
for (const [id, entry] of pool) {
|
|
20447
|
+
const run = deps.db.getRun(id);
|
|
20448
|
+
if (!run || run.status !== "review" || !run.pr_url) pool.delete(id);
|
|
20449
|
+
else out.push({ run, entry });
|
|
20450
|
+
}
|
|
20451
|
+
return out.sort((a, b) => a.entry.since - b.entry.since || a.run.id - b.run.id);
|
|
20452
|
+
}
|
|
20453
|
+
async function liftReturners(deps, returners, now, droneCap) {
|
|
20454
|
+
for (const { run, entry } of returners) {
|
|
20455
|
+
if (countActiveDrones(deps.db) >= droneCap) return;
|
|
20456
|
+
if (!deps.respawn || deps.db.activeResolverFor(run.id)) continue;
|
|
20457
|
+
let lifted;
|
|
20458
|
+
if (entry.kind === "changes") {
|
|
20459
|
+
if (!needsFixIteration(run)) {
|
|
20460
|
+
deps.returners?.delete(run.id);
|
|
20461
|
+
continue;
|
|
20462
|
+
}
|
|
20463
|
+
lifted = await wakeAuthorOnChanges(deps, run, now, droneCap);
|
|
20464
|
+
} else {
|
|
20465
|
+
if (!deps.ci || now - entry.seenAt > RETURNER_CI_TTL_MS) {
|
|
20466
|
+
deps.returners?.delete(run.id);
|
|
20467
|
+
continue;
|
|
20468
|
+
}
|
|
20469
|
+
lifted = await wakeAuthorOnRedCi(deps, run, now, entry.ci, deps.worktreeExists ?? existsSync20, deps.hasTranscript ?? executorHasSession, droneCap);
|
|
20470
|
+
}
|
|
20471
|
+
if (lifted) deps.returners?.delete(run.id);
|
|
20380
20472
|
}
|
|
20381
20473
|
}
|
|
20382
20474
|
async function resumeWatchdogParked(deps, droneCap) {
|
|
@@ -20668,7 +20760,7 @@ async function manageRelief(deps, now, droneCap, stormActive) {
|
|
|
20668
20760
|
const pausedForRelief = deps.db.listRuns("paused").filter((r) => isReliefPause(r.stop_reason));
|
|
20669
20761
|
const recovering = pausedForRelief.length > 0 || state.overloadedAt != null && now - state.overloadedAt < cfg.rampWindowMs;
|
|
20670
20762
|
if (!recovering) {
|
|
20671
|
-
await batchSpawnQueue(deps, droneCap);
|
|
20763
|
+
await batchSpawnQueue(deps, droneCap, now);
|
|
20672
20764
|
return;
|
|
20673
20765
|
}
|
|
20674
20766
|
if (!rampDue(state.lastRampAt ?? null, now, cfg)) return;
|
|
@@ -21158,7 +21250,7 @@ async function tickOnce(deps) {
|
|
|
21158
21250
|
if (deps.resourcesOk) {
|
|
21159
21251
|
await manageRelief(deps, now, droneCap, stormActive);
|
|
21160
21252
|
} else if (!stormActive) {
|
|
21161
|
-
await batchSpawnQueue(deps, droneCap);
|
|
21253
|
+
await batchSpawnQueue(deps, droneCap, now);
|
|
21162
21254
|
}
|
|
21163
21255
|
if (!stormActive) {
|
|
21164
21256
|
await resumeWatchdogParked(deps, droneCap);
|
|
@@ -21175,7 +21267,7 @@ async function tickOnce(deps) {
|
|
|
21175
21267
|
});
|
|
21176
21268
|
deps.db.updateRun(run.id, { status: "done" });
|
|
21177
21269
|
deps.db.addEvent(run.id, "done", "\u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442 \u0440\u0430\u0437\u0440\u0435\u0448\u0451\u043D, main \u0432\u043B\u0438\u0442, PR \u043E\u0431\u043D\u043E\u0432\u043B\u0451\u043D");
|
|
21178
|
-
deps.db.addEvent(run.resolves_run_id, "conflict",
|
|
21270
|
+
deps.db.addEvent(run.resolves_run_id, "conflict", `${RESOLVER_RESOLVED_MARK} ${run.name} \u2014 PR \u043E\u0431\u043D\u043E\u0432\u043B\u0451\u043D`);
|
|
21179
21271
|
continue;
|
|
21180
21272
|
}
|
|
21181
21273
|
if (run.reviews_run_id != null) {
|
|
@@ -21228,6 +21320,7 @@ async function tickOnce(deps) {
|
|
|
21228
21320
|
const prUrl = run.pr_url;
|
|
21229
21321
|
const st = await deps.prStatus(prUrl);
|
|
21230
21322
|
run = syncBranchFromPr(deps.db, run, st.headBranch);
|
|
21323
|
+
if (st.conflicting) deps.returners?.delete(run.id);
|
|
21231
21324
|
if (st.merged) {
|
|
21232
21325
|
if (await finalizeMergedReview(deps, run)) narrate(deps, `PR ${droneLabel(run)} \u0441\u043C\u0435\u0440\u0436\u0435\u043D \u2014 ${run.task_slug} \u0437\u0430\u043A\u0440\u044B\u0442\u0430, \u0441\u043B\u043E\u0442 \u0441\u0432\u043E\u0431\u043E\u0434\u0435\u043D.`);
|
|
21233
21326
|
continue;
|
|
@@ -21334,8 +21427,13 @@ async function tickOnce(deps) {
|
|
|
21334
21427
|
} else if (!st.conflicting && deps.ci && deps.respawn && !stormActive) {
|
|
21335
21428
|
await handleCi(deps, run, now, worktreeExists, hasTranscript, droneCap);
|
|
21336
21429
|
}
|
|
21337
|
-
if (needsFixIteration(run) && !stormActive && !st.conflicting && !deps.db.activeResolverFor(run.id)
|
|
21338
|
-
|
|
21430
|
+
if (needsFixIteration(run) && !stormActive && !st.conflicting && !deps.db.activeResolverFor(run.id)) {
|
|
21431
|
+
const capFull = countActiveDrones(deps.db) >= droneCap;
|
|
21432
|
+
if (await wakeAuthorOnChanges(deps, run, now, droneCap)) {
|
|
21433
|
+
deps.returners?.delete(run.id);
|
|
21434
|
+
continue;
|
|
21435
|
+
}
|
|
21436
|
+
if (capFull) noteReturner(deps, run, now, { kind: "changes", since: now });
|
|
21339
21437
|
}
|
|
21340
21438
|
const reviewerGatesOk = !st.conflicting && !st.isDraft && !!deps.spawnReviewer && !stormActive && (!deps.reviewCutoffAt || run.updated_at >= deps.reviewCutoffAt) && deps.db.reviewerCountFor(run.id) === 0 && // ГЕЙТ ПЕРВЫМ: живой/успешный ревьюер уже есть — ни капа, ни спавна не трогаем
|
|
21341
21439
|
// …и НИ ОДНОГО живого/ждущего ревьюера ЛЮБОГО цикла (TASWM-299). reviewerCountFor
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@taskless-app/swarm",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.52",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
|
|
6
6
|
"keywords": [
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"execa": "^10.0.0",
|
|
40
40
|
"zod": "3.25.76"
|
|
41
41
|
},
|
|
42
|
-
"gitHead": "
|
|
42
|
+
"gitHead": "f77e0473fd2f80e4418ceadd1b29b9a70241fc04",
|
|
43
43
|
"scripts": {
|
|
44
44
|
"overmind": "NODE_OPTIONS=\"${NODE_OPTIONS:-} --conditions=development\" tsx src/overmind.ts",
|
|
45
45
|
"swarm": "NODE_OPTIONS=\"${NODE_OPTIONS:-} --conditions=development\" tsx src/cli.ts",
|