@taskless-app/swarm 0.6.49 → 0.6.51
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 +326 -116
- package/dist/overmind.js +630 -158
- 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) {
|
|
@@ -1288,6 +1295,10 @@ function pathZoneCandidate(path) {
|
|
|
1288
1295
|
if (parts[0] === "src" && parts.length >= 3 && !GENERIC_DIRS.has(parts[1])) return parts[1];
|
|
1289
1296
|
return parts[0];
|
|
1290
1297
|
}
|
|
1298
|
+
function dirOf(path) {
|
|
1299
|
+
const i = path.lastIndexOf("/");
|
|
1300
|
+
return i < 0 ? "" : path.slice(0, i);
|
|
1301
|
+
}
|
|
1291
1302
|
function tokenize(text) {
|
|
1292
1303
|
const out = [];
|
|
1293
1304
|
for (const raw of text.split(/[^\p{L}\p{N}]+/u)) {
|
|
@@ -1521,13 +1532,61 @@ function similarTasks(model, text, limit = 5) {
|
|
|
1521
1532
|
}
|
|
1522
1533
|
return scored.sort((a, b) => b.score - a.score).slice(0, limit);
|
|
1523
1534
|
}
|
|
1535
|
+
function historyFiles(model) {
|
|
1536
|
+
let idx = historyIndex.get(model);
|
|
1537
|
+
if (!idx) {
|
|
1538
|
+
const files = new Set(model.entries.flatMap((e) => e.files));
|
|
1539
|
+
const byBase = /* @__PURE__ */ new Map();
|
|
1540
|
+
for (const f of files) {
|
|
1541
|
+
const base3 = f.slice(f.lastIndexOf("/") + 1);
|
|
1542
|
+
byBase.set(base3, [...byBase.get(base3) ?? [], f]);
|
|
1543
|
+
}
|
|
1544
|
+
idx = { files, byBase };
|
|
1545
|
+
historyIndex.set(model, idx);
|
|
1546
|
+
}
|
|
1547
|
+
return idx;
|
|
1548
|
+
}
|
|
1549
|
+
function namedPaths(model, text, fileExists) {
|
|
1550
|
+
const { files, byBase } = historyFiles(model);
|
|
1551
|
+
const known = (p) => files.has(p) || (fileExists?.(p) ?? false);
|
|
1552
|
+
const raw = [...new Set((text.match(PATH_CANDIDATE) ?? []).map((c) => c.replace(/^\.?\/+/, "").replace(/\.+$/, "")))].filter((c) => c && !c.split("/").includes(".."));
|
|
1553
|
+
raw.sort((a, b) => b.split("/").length - a.split("/").length);
|
|
1554
|
+
const out = [];
|
|
1555
|
+
const dirs = /* @__PURE__ */ new Set();
|
|
1556
|
+
const take = (p) => {
|
|
1557
|
+
if (out.includes(p)) return;
|
|
1558
|
+
out.push(p);
|
|
1559
|
+
dirs.add(dirOf(p));
|
|
1560
|
+
};
|
|
1561
|
+
for (const c of raw) {
|
|
1562
|
+
if (known(c)) {
|
|
1563
|
+
take(c);
|
|
1564
|
+
continue;
|
|
1565
|
+
}
|
|
1566
|
+
const base3 = c.slice(c.lastIndexOf("/") + 1);
|
|
1567
|
+
let matches = (byBase.get(base3) ?? []).filter((f) => f === c || f.endsWith(`/${c}`));
|
|
1568
|
+
if (matches.length > 1) matches = matches.filter((f) => dirs.has(dirOf(f)));
|
|
1569
|
+
if (matches.length === 1) {
|
|
1570
|
+
take(matches[0]);
|
|
1571
|
+
continue;
|
|
1572
|
+
}
|
|
1573
|
+
if (!c.includes("/") && fileExists) {
|
|
1574
|
+
const sibling = [...dirs].map((d) => d ? `${d}/${c}` : c).filter(fileExists);
|
|
1575
|
+
if (sibling.length === 1) take(sibling[0]);
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
return out;
|
|
1579
|
+
}
|
|
1524
1580
|
function predictFiles(model, opts) {
|
|
1525
1581
|
if (!hasZoneModel(model)) return [];
|
|
1526
1582
|
const limit = opts.limit ?? 40;
|
|
1527
1583
|
const best = /* @__PURE__ */ new Map();
|
|
1584
|
+
const named = opts.text ? namedPaths(model, opts.text, opts.fileExists) : [];
|
|
1585
|
+
for (const path of named) best.set(path, { path, weight: 1, why: "\u043D\u0430\u0437\u0432\u0430\u043D \u0432 \u0437\u0430\u0434\u0430\u0447\u0435", named: true });
|
|
1586
|
+
const factor = named.length ? NAMED_BACKGROUND_FACTOR : 1;
|
|
1528
1587
|
const put = (path, weight, why) => {
|
|
1529
1588
|
const cur = best.get(path);
|
|
1530
|
-
if (!cur || cur.weight < weight) best.set(path, { path, weight, why });
|
|
1589
|
+
if (!cur || cur.weight < weight * factor) best.set(path, { path, weight: weight * factor, why });
|
|
1531
1590
|
};
|
|
1532
1591
|
if (opts.text) {
|
|
1533
1592
|
const sim = similarTasks(model, opts.text, opts.similar ?? 5);
|
|
@@ -1564,7 +1623,7 @@ function checkZoneAgreement(model, entries = model.entries) {
|
|
|
1564
1623
|
const n = rows.length;
|
|
1565
1624
|
return { checked: n, matched, compatible, rate: n ? matched / n : 0, looseRate: n ? (matched + compatible) / n : 0, rows };
|
|
1566
1625
|
}
|
|
1567
|
-
var SPINE_ZONE, DEFAULTS, EMPTY_ZONE_MODEL, GENERIC_DIRS, WORKSPACE_DIRS, GENERIC_TEST_DIRS, STOP_TERMS;
|
|
1626
|
+
var SPINE_ZONE, DEFAULTS, EMPTY_ZONE_MODEL, GENERIC_DIRS, WORKSPACE_DIRS, GENERIC_TEST_DIRS, STOP_TERMS, PATH_CANDIDATE, historyIndex, NAMED_BACKGROUND_FACTOR;
|
|
1568
1627
|
var init_codeZones = __esm({
|
|
1569
1628
|
"../swarm-core/src/codeZones.ts"() {
|
|
1570
1629
|
"use strict";
|
|
@@ -1643,6 +1702,9 @@ var init_codeZones = __esm({
|
|
|
1643
1702
|
"\u043F\u0435\u0440\u0444",
|
|
1644
1703
|
"wip"
|
|
1645
1704
|
]);
|
|
1705
|
+
PATH_CANDIDATE = /[\w@.\-/]+\.[A-Za-z][A-Za-z0-9]{0,5}/g;
|
|
1706
|
+
historyIndex = /* @__PURE__ */ new WeakMap();
|
|
1707
|
+
NAMED_BACKGROUND_FACTOR = 0.1;
|
|
1646
1708
|
}
|
|
1647
1709
|
});
|
|
1648
1710
|
|
|
@@ -1661,7 +1723,7 @@ var init_effort = __esm({
|
|
|
1661
1723
|
});
|
|
1662
1724
|
|
|
1663
1725
|
// ../swarm-core/src/queuePlan.ts
|
|
1664
|
-
var MIN_MS, QUEUE_NOMINAL_RUN_MS, QUEUE_MIN_TAIL_MS
|
|
1726
|
+
var MIN_MS, QUEUE_NOMINAL_RUN_MS, QUEUE_MIN_TAIL_MS;
|
|
1665
1727
|
var init_queuePlan = __esm({
|
|
1666
1728
|
"../swarm-core/src/queuePlan.ts"() {
|
|
1667
1729
|
"use strict";
|
|
@@ -1672,7 +1734,6 @@ var init_queuePlan = __esm({
|
|
|
1672
1734
|
MIN_MS = 6e4;
|
|
1673
1735
|
QUEUE_NOMINAL_RUN_MS = 25 * MIN_MS;
|
|
1674
1736
|
QUEUE_MIN_TAIL_MS = 3 * MIN_MS;
|
|
1675
|
-
QUEUE_REVIEW_TAIL_MS = 10 * MIN_MS;
|
|
1676
1737
|
}
|
|
1677
1738
|
});
|
|
1678
1739
|
|
|
@@ -3431,7 +3492,7 @@ var init_package = __esm({
|
|
|
3431
3492
|
"package.json"() {
|
|
3432
3493
|
package_default = {
|
|
3433
3494
|
name: "@taskless-app/swarm",
|
|
3434
|
-
version: "0.6.
|
|
3495
|
+
version: "0.6.51",
|
|
3435
3496
|
type: "module",
|
|
3436
3497
|
description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
|
|
3437
3498
|
keywords: [
|
|
@@ -3482,7 +3543,7 @@ var init_package = __esm({
|
|
|
3482
3543
|
execa: "^10.0.0",
|
|
3483
3544
|
zod: "3.25.76"
|
|
3484
3545
|
},
|
|
3485
|
-
gitHead: "
|
|
3546
|
+
gitHead: "24aad254ba4f3f71904310b73435e647a08f22f5"
|
|
3486
3547
|
};
|
|
3487
3548
|
}
|
|
3488
3549
|
});
|
|
@@ -4817,6 +4878,10 @@ function rowToRun(row) {
|
|
|
4817
4878
|
// вычисляемая фаза ревью (TASWM-230)
|
|
4818
4879
|
review_cycle: row.review_cycle ?? 0,
|
|
4819
4880
|
// старые прогоны без колонки → 0
|
|
4881
|
+
// TASWM-577: намерение смержить (человек отметил «пора влить»). В sqlite-бэкенде оно
|
|
4882
|
+
// живёт только здесь — ставить его некому, кроме самого демона, зато читает его
|
|
4883
|
+
// реконсайл, и колонка нужна, чтобы отметка пережила рестарт.
|
|
4884
|
+
merge_requested_at: row.merge_requested_at ?? null,
|
|
4820
4885
|
task_title: row.task_title ?? null,
|
|
4821
4886
|
task_desc: row.task_desc ?? null,
|
|
4822
4887
|
// TASWM-556: когда заведена сама задача — по ней проверка дубля отличает свой
|
|
@@ -4892,6 +4957,7 @@ CREATE TABLE IF NOT EXISTS runs (
|
|
|
4892
4957
|
verifies_run_id INTEGER,
|
|
4893
4958
|
verify_verdict TEXT,
|
|
4894
4959
|
review_cycle INTEGER NOT NULL DEFAULT 0,
|
|
4960
|
+
merge_requested_at TEXT,
|
|
4895
4961
|
task_title TEXT,
|
|
4896
4962
|
task_desc TEXT,
|
|
4897
4963
|
task_created_at TEXT,
|
|
@@ -4986,6 +5052,8 @@ CREATE TABLE IF NOT EXISTS imports (
|
|
|
4986
5052
|
"ALTER TABLE runs ADD COLUMN verify_verdict TEXT",
|
|
4987
5053
|
// Авто-ревью v2 (TASWM-162): номер цикла ревью — пере-ревью после фикс-итерации.
|
|
4988
5054
|
"ALTER TABLE runs ADD COLUMN review_cycle INTEGER NOT NULL DEFAULT 0",
|
|
5055
|
+
// TASWM-577: намерение смержить — по нему рой вливает PR сам, по очереди мержа.
|
|
5056
|
+
"ALTER TABLE runs ADD COLUMN merge_requested_at TEXT",
|
|
4989
5057
|
// Вычисляемая фаза ревью (TASWM-230): человеко-фраза «чего ждёт PR», её считает демон.
|
|
4990
5058
|
"ALTER TABLE runs ADD COLUMN review_phase TEXT",
|
|
4991
5059
|
// Приоритетная очередь (TASWM-269): явный приоритет прогона, выше — раньше из queued.
|
|
@@ -7317,6 +7385,12 @@ function depsFailureRetriable(code) {
|
|
|
7317
7385
|
function runRef(runKey) {
|
|
7318
7386
|
return { __runRef: runKey };
|
|
7319
7387
|
}
|
|
7388
|
+
function isoOrNull(v) {
|
|
7389
|
+
if (v == null) return null;
|
|
7390
|
+
const raw = v instanceof Date ? v.toISOString() : typeof v === "string" ? v : "";
|
|
7391
|
+
const at = Date.parse(raw);
|
|
7392
|
+
return Number.isNaN(at) ? null : new Date(at).toISOString();
|
|
7393
|
+
}
|
|
7320
7394
|
function isRunRef(v) {
|
|
7321
7395
|
return typeof v === "object" && v !== null && "__runRef" in v;
|
|
7322
7396
|
}
|
|
@@ -7784,6 +7858,17 @@ var SwarmClient = class _SwarmClient {
|
|
|
7784
7858
|
runKeyOf(id) {
|
|
7785
7859
|
return this.runs.get(id)?.runKey ?? null;
|
|
7786
7860
|
}
|
|
7861
|
+
/**
|
|
7862
|
+
* Cuid прогона В ПРОДЕ — им адресуются прод-РУЧКИ, а не MCP-тулы (TASWM-577: демон
|
|
7863
|
+
* вливает PR тем же `POST /swarm/merge`, что и кнопка, и снимает намерение тем же
|
|
7864
|
+
* `DELETE /swarm/merge-request/:runId`). null — прогон до прода ещё не долетел
|
|
7865
|
+
* (создан этим демоном, флаш не прошёл): адресовать его там нечем, и вызывающий
|
|
7866
|
+
* обязан прочитать это как «сейчас нельзя», а не как ошибку.
|
|
7867
|
+
*/
|
|
7868
|
+
prodRunId(id) {
|
|
7869
|
+
const key = this.runs.get(id)?.runKey;
|
|
7870
|
+
return key ? this.prodIdFor(key) ?? null : null;
|
|
7871
|
+
}
|
|
7787
7872
|
runByKey(key) {
|
|
7788
7873
|
const direct = [...this.runs.values()].find((r) => r.runKey === key);
|
|
7789
7874
|
if (direct) return this.mat(direct);
|
|
@@ -9330,6 +9415,9 @@ var SwarmClient = class _SwarmClient {
|
|
|
9330
9415
|
verify_verdict: null,
|
|
9331
9416
|
review_phase: null,
|
|
9332
9417
|
// вычисляемая фаза ревью (TASWM-230) — прод хранит и раздаёт
|
|
9418
|
+
// TASWM-577: намерение смержить — прод-поле (его ставит человек ручкой
|
|
9419
|
+
// /swarm/merge-request), заполняется ниже из ответа.
|
|
9420
|
+
merge_requested_at: null,
|
|
9333
9421
|
// Локальная бухгалтерия (в прод-схему не уходит) — на гидрации из прода дефолт 0;
|
|
9334
9422
|
// существующая локальная запись НЕ перетирается (поле не в field-copy ниже),
|
|
9335
9423
|
// в отличие от respawns, — авто-ревью v2, TASWM-162.
|
|
@@ -9365,6 +9453,7 @@ var SwarmClient = class _SwarmClient {
|
|
|
9365
9453
|
local.task_title = pr.taskTitle ?? local.task_title;
|
|
9366
9454
|
local.task_desc = pr.taskDesc ?? local.task_desc;
|
|
9367
9455
|
local.task_created_at = pr.taskCreatedAt ?? local.task_created_at;
|
|
9456
|
+
if ("mergeRequestedAt" in pr) local.merge_requested_at = isoOrNull(pr.mergeRequestedAt);
|
|
9368
9457
|
local.name = pr.droneName ?? local.name;
|
|
9369
9458
|
local.kind = pr.role ?? local.kind;
|
|
9370
9459
|
const heroProd = pr.heroId ?? null;
|
|
@@ -10397,22 +10486,10 @@ function mergeQueueCandidates(runs, repo, repoFallback) {
|
|
|
10397
10486
|
}
|
|
10398
10487
|
var BASE_TTL_MS = 2e4;
|
|
10399
10488
|
function makeMergeQueueResolver(deps) {
|
|
10400
|
-
const
|
|
10489
|
+
const read = deps.headState ?? makeHeadStateReader(deps);
|
|
10401
10490
|
const evaluate = async (e, prUrl, ready) => {
|
|
10402
|
-
const
|
|
10403
|
-
|
|
10404
|
-
const ttl = deps.ttlMs ?? BASE_TTL_MS;
|
|
10405
|
-
if (!cached || now - cached.at >= ttl) {
|
|
10406
|
-
const base3 = await deps.baseSync(prUrl).catch(() => null);
|
|
10407
|
-
let checks = "none";
|
|
10408
|
-
if (base3?.behindBy === 0 && deps.checks) {
|
|
10409
|
-
const ci = await deps.checks(prUrl).catch(() => null);
|
|
10410
|
-
checks = ci ? ci.bucket : null;
|
|
10411
|
-
}
|
|
10412
|
-
cached = { at: now, base: base3, checks };
|
|
10413
|
-
memo.set(prUrl, cached);
|
|
10414
|
-
}
|
|
10415
|
-
return computeMergeQueue({ entry: e, ready, behindBy: cached.base ? cached.base.behindBy : null, checks: cached.checks });
|
|
10491
|
+
const st = await read(prUrl);
|
|
10492
|
+
return computeMergeQueue({ entry: e, ready, behindBy: st.behindBy, checks: st.checks });
|
|
10416
10493
|
};
|
|
10417
10494
|
return async (run) => {
|
|
10418
10495
|
if (!deps.baseSync || !run.pr_url) return null;
|
|
@@ -10447,6 +10524,124 @@ function makeMergeQueueResolver(deps) {
|
|
|
10447
10524
|
function entryOf(r) {
|
|
10448
10525
|
return { runId: r.id, prNumber: prNumberOf(r.pr_url), slug: r.task_slug };
|
|
10449
10526
|
}
|
|
10527
|
+
function makeHeadStateReader(deps) {
|
|
10528
|
+
const memo = /* @__PURE__ */ new Map();
|
|
10529
|
+
return async (prUrl) => {
|
|
10530
|
+
const now = Date.now();
|
|
10531
|
+
const ttl = deps.ttlMs ?? BASE_TTL_MS;
|
|
10532
|
+
const cached = memo.get(prUrl);
|
|
10533
|
+
if (cached && now - cached.at < ttl) return { behindBy: cached.behindBy, checks: cached.checks, headSha: cached.headSha };
|
|
10534
|
+
const base3 = deps.baseSync ? await deps.baseSync(prUrl).catch(() => null) : null;
|
|
10535
|
+
let checks = "none";
|
|
10536
|
+
if (base3?.behindBy === 0 && deps.checks) {
|
|
10537
|
+
const ci = await deps.checks(prUrl).catch(() => null);
|
|
10538
|
+
checks = ci ? ci.bucket : null;
|
|
10539
|
+
}
|
|
10540
|
+
const fresh = { behindBy: base3 ? base3.behindBy : null, checks, headSha: base3?.headSha ?? null };
|
|
10541
|
+
memo.set(prUrl, { at: now, ...fresh });
|
|
10542
|
+
return fresh;
|
|
10543
|
+
};
|
|
10544
|
+
}
|
|
10545
|
+
function hasMergeIntent(r) {
|
|
10546
|
+
return !!(r.merge_requested_at ?? "").trim();
|
|
10547
|
+
}
|
|
10548
|
+
var NO_CI_GRACE_MS = 10 * 6e4;
|
|
10549
|
+
var shortSha = (sha) => sha.slice(0, 7);
|
|
10550
|
+
function makeMergeIntentResolver(deps) {
|
|
10551
|
+
const read = deps.headState ?? makeHeadStateReader(deps);
|
|
10552
|
+
const firstSeen = /* @__PURE__ */ new Map();
|
|
10553
|
+
const ciSettled = async (prUrl, st, now) => {
|
|
10554
|
+
const sha = st.headSha;
|
|
10555
|
+
if (!sha) {
|
|
10556
|
+
return { action: "wait", code: "checks_unknown", reason: "gh \u043D\u0435 \u0441\u043A\u0430\u0437\u0430\u043B HEAD PR \u2014 \u0431\u0435\u0437 \u043D\u0435\u0433\u043E \u043D\u0435 \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C, \u0434\u043E\u0431\u0435\u0436\u0430\u043B \u043B\u0438 \u043D\u0430 \u043D\u0451\u043C CI. \u0421\u043F\u0440\u043E\u0448\u0443 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u043C \u0442\u0438\u043A\u043E\u043C" };
|
|
10557
|
+
}
|
|
10558
|
+
if (!deps.actionsRuns) {
|
|
10559
|
+
return { action: "wait", code: "checks_unknown", reason: "\u0440\u043E\u044E \u043D\u0435\u0447\u0435\u043C \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C \u043F\u0440\u043E\u0433\u043E\u043D\u044B GitHub Actions \u043D\u0430 HEAD PR \u2014 \u043F\u043E \u043E\u0434\u043D\u043E\u043C\u0443 \u0430\u0433\u0440\u0435\u0433\u0430\u0442\u0443 \u043F\u0440\u043E\u0432\u0435\u0440\u043E\u043A \u0432\u0441\u043B\u0435\u043F\u0443\u044E \u043D\u0435 \u0432\u043B\u0438\u0432\u0430\u044E" };
|
|
10560
|
+
}
|
|
10561
|
+
const runs = await deps.actionsRuns(prUrl, sha).catch(() => null);
|
|
10562
|
+
if (!runs) {
|
|
10563
|
+
return { action: "wait", code: "checks_unknown", reason: `\u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0441\u043F\u0440\u043E\u0441\u0438\u0442\u044C \u043F\u0440\u043E\u0433\u043E\u043D\u044B GitHub Actions \u043D\u0430 ${shortSha(sha)} \u2014 \u0441\u043F\u0440\u043E\u0448\u0443 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u043C \u0442\u0438\u043A\u043E\u043C` };
|
|
10564
|
+
}
|
|
10565
|
+
if (runs.failed.length > 0) {
|
|
10566
|
+
return { action: "clear", code: "checks_red", reason: `\u043D\u0430 ${shortSha(sha)} \u0443\u043F\u0430\u043B \u043F\u0440\u043E\u0433\u043E\u043D GitHub Actions: ${runs.failed.join(", ")} \u2014 \u0432\u043B\u0438\u0432\u0430\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E, \u043D\u0443\u0436\u043D\u0430 \u0434\u043E\u0440\u0430\u0431\u043E\u0442\u043A\u0430` };
|
|
10567
|
+
}
|
|
10568
|
+
if (runs.active.length > 0) {
|
|
10569
|
+
return {
|
|
10570
|
+
action: "wait",
|
|
10571
|
+
code: "checks_pending",
|
|
10572
|
+
reason: `\u043D\u0430 ${shortSha(sha)} \u0435\u0449\u0451 \u0438\u0434\u0443\u0442 \u043F\u0440\u043E\u0433\u043E\u043D\u044B GitHub Actions: ${runs.active.join(", ")} \u2014 \u0432\u0438\u0434\u0438\u043C\u044B\u0435 \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0438 \u043C\u043E\u0433\u0443\u0442 \u0431\u044B\u0442\u044C \u0437\u0435\u043B\u0451\u043D\u044B\u043C\u0438, \u043F\u043E\u043A\u0430 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0430\u044F \u0432\u043E\u043B\u043D\u0430 \u0434\u0436\u043E\u0431 \u043D\u0435 \u0441\u043E\u0437\u0434\u0430\u043D\u0430`
|
|
10573
|
+
};
|
|
10574
|
+
}
|
|
10575
|
+
if (st.checks === "none") {
|
|
10576
|
+
if (runs.total > 0) {
|
|
10577
|
+
return { action: "wait", code: "checks_pending", reason: `\u043F\u0440\u043E\u0433\u043E\u043D\u044B GitHub Actions \u043D\u0430 ${shortSha(sha)} \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043B\u0438\u0441\u044C, \u0430 \u043F\u0440\u043E\u0432\u0435\u0440\u043E\u043A \u0443 PR \u0435\u0449\u0451 \u043D\u0435 \u0432\u0438\u0434\u043D\u043E \u2014 \u0436\u0434\u0443, \u043F\u043E\u043A\u0430 GitHub \u0438\u0445 \u043F\u043E\u043A\u0430\u0436\u0435\u0442` };
|
|
10578
|
+
}
|
|
10579
|
+
const seen = firstSeen.get(prUrl);
|
|
10580
|
+
const waited = seen && seen.sha === sha ? now - seen.at : 0;
|
|
10581
|
+
const graceMin = NO_CI_GRACE_MS / 6e4;
|
|
10582
|
+
if (waited < NO_CI_GRACE_MS) {
|
|
10583
|
+
return {
|
|
10584
|
+
action: "wait",
|
|
10585
|
+
code: "checks_pending",
|
|
10586
|
+
reason: `\u043D\u0430 ${shortSha(sha)} \u043F\u043E\u043A\u0430 \u043D\u0438 \u043E\u0434\u043D\u043E\u0439 \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0438 \u0438 \u043D\u0438 \u043E\u0434\u043D\u043E\u0433\u043E \u043F\u0440\u043E\u0433\u043E\u043D\u0430 GitHub Actions \u2014 CI \u043C\u043E\u0433 \u0435\u0449\u0451 \u043D\u0435 \u0437\u0430\u0432\u0435\u0441\u0442\u0438 \u0434\u0436\u043E\u0431\u044B \u043F\u043E\u0441\u043B\u0435 \u043F\u0443\u0448\u0430. \u0416\u0434\u0443 \u0434\u043E ${graceMin} \u043C\u0438\u043D (\u043F\u0440\u043E\u0448\u043B\u043E ${Math.floor(waited / 6e4)})`
|
|
10587
|
+
};
|
|
10588
|
+
}
|
|
10589
|
+
return { action: "merge", code: "queue_head", reason: `\u0435\u0433\u043E \u0447\u0435\u0440\u0451\u0434 \u0438 \u0431\u0430\u0437\u0430 \u0441\u0432\u0435\u0436\u0430\u044F, \u0430 ${shortSha(sha)} ${graceMin} \u043C\u0438\u043D \u043F\u0440\u043E\u0441\u0442\u043E\u044F\u043B \u0431\u0435\u0437 \u0435\u0434\u0438\u043D\u043E\u0439 \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0438 \u0438 \u043F\u0440\u043E\u0433\u043E\u043D\u0430 GitHub Actions \u2014 CI \u0443 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F \u043D\u0435\u0442` };
|
|
10590
|
+
}
|
|
10591
|
+
return { action: "merge", code: "queue_head", reason: `\u0435\u0433\u043E \u0447\u0435\u0440\u0451\u0434, \u0431\u0430\u0437\u0430 \u0441\u0432\u0435\u0436\u0430\u044F, \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0438 \u043D\u0430 ${shortSha(sha)} \u0437\u0435\u043B\u0451\u043D\u044B\u0435 \u0438 \u0432\u0441\u0435 \u043F\u0440\u043E\u0433\u043E\u043D\u044B GitHub Actions \u043D\u0430 \u043D\u0451\u043C \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043D\u044B` };
|
|
10592
|
+
};
|
|
10593
|
+
const confirmLive = async (prUrl, sha, verdict) => {
|
|
10594
|
+
if (!sha) {
|
|
10595
|
+
return { action: "wait", code: "checks_unknown", reason: "gh \u043D\u0435 \u0441\u043A\u0430\u0437\u0430\u043B HEAD PR \u2014 \u043D\u0435 \u043F\u043E\u043D\u044F\u0442\u044C, \u043F\u0440\u043E \u043A\u0430\u043A\u043E\u0439 \u043A\u043E\u043C\u043C\u0438\u0442 \u044D\u0442\u043E\u0442 \u0432\u0435\u0440\u0434\u0438\u043A\u0442; \u0441\u043F\u0440\u043E\u0448\u0443 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u043C \u0442\u0438\u043A\u043E\u043C" };
|
|
10596
|
+
}
|
|
10597
|
+
const live = deps.baseSync ? await deps.baseSync(prUrl).catch(() => null) : null;
|
|
10598
|
+
if (!live || !live.headSha) {
|
|
10599
|
+
return { action: "wait", code: "base_unknown", reason: "\u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043F\u0435\u0440\u0435\u0441\u043F\u0440\u043E\u0441\u0438\u0442\u044C \u0436\u0438\u0432\u043E\u0439 HEAD PR \u2014 \u0441\u043F\u0440\u043E\u0448\u0443 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u043C \u0442\u0438\u043A\u043E\u043C" };
|
|
10600
|
+
}
|
|
10601
|
+
if (verdict.action === "merge" && live.behindBy > 0) {
|
|
10602
|
+
return {
|
|
10603
|
+
action: "refresh_base",
|
|
10604
|
+
code: "base_stale",
|
|
10605
|
+
reason: `\u0431\u0430\u0437\u0430 \u0443\u0448\u043B\u0430 \u0432\u043F\u0435\u0440\u0451\u0434 \u043D\u0430 ${live.behindBy} \u043A\u043E\u043C\u043C\u0438\u0442(\u043E\u0432) \u043F\u043E\u0441\u043B\u0435 \u043F\u0440\u0435\u0434\u044B\u0434\u0443\u0449\u0435\u0433\u043E \u043C\u0435\u0440\u0436\u0430 \u2014 \u0441\u043D\u0430\u0447\u0430\u043B\u0430 \u0432\u043B\u0438\u0442\u044C main \u0438 \u0434\u043E\u0436\u0434\u0430\u0442\u044C\u0441\u044F CI \u043D\u0430 \u043E\u0431\u043D\u043E\u0432\u043B\u0451\u043D\u043D\u043E\u0439 \u0431\u0430\u0437\u0435`
|
|
10606
|
+
};
|
|
10607
|
+
}
|
|
10608
|
+
if (live.headSha !== sha) {
|
|
10609
|
+
return { action: "wait", code: "checks_pending", reason: `\u043D\u0430 PR \u043D\u043E\u0432\u044B\u0439 \u043A\u043E\u043C\u043C\u0438\u0442 ${shortSha(live.headSha)} \u2014 \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u044E \u0437\u0430\u043D\u043E\u0432\u043E` };
|
|
10610
|
+
}
|
|
10611
|
+
return verdict;
|
|
10612
|
+
};
|
|
10613
|
+
return async (run) => {
|
|
10614
|
+
if (!deps.baseSync || !run.pr_url || !hasMergeIntent(run)) return null;
|
|
10615
|
+
const num = prNumberOf(run.pr_url);
|
|
10616
|
+
if (num == null) return null;
|
|
10617
|
+
const marked = mergeQueueCandidates(deps.listRuns(), effectiveRepoOf(run.repo, deps.repoFallback), deps.repoFallback).filter(hasMergeIntent);
|
|
10618
|
+
const mine = marked.find((c) => prNumberOf(c.pr_url) === num);
|
|
10619
|
+
if (!mine) return null;
|
|
10620
|
+
const queue = orderMergeQueue(marked.map(entryOf));
|
|
10621
|
+
const entry = entryOf(mine);
|
|
10622
|
+
const position = queue.findIndex((e) => e.runId === entry.runId) + 1;
|
|
10623
|
+
const head = queue[0];
|
|
10624
|
+
if (head.runId !== entry.runId) {
|
|
10625
|
+
return {
|
|
10626
|
+
action: "wait",
|
|
10627
|
+
code: "queue_wait",
|
|
10628
|
+
reason: `\u0440\u043E\u0439 \u0432\u043B\u0438\u0432\u0430\u0435\u0442 \u043F\u043E \u043E\u0434\u043D\u043E\u043C\u0443: \u0441\u0435\u0439\u0447\u0430\u0441 \u043E\u0447\u0435\u0440\u0435\u0434\u044C PR #${head.prNumber}${head.slug ? ` (${head.slug})` : ""}, \u044D\u0442\u043E\u0442 \u2014 ${position}-\u0439 \u0438\u0437 ${queue.length}`,
|
|
10629
|
+
position,
|
|
10630
|
+
size: queue.length
|
|
10631
|
+
};
|
|
10632
|
+
}
|
|
10633
|
+
const st = await read(mine.pr_url);
|
|
10634
|
+
const now = deps.now ? deps.now() : Date.now();
|
|
10635
|
+
if (st.headSha) {
|
|
10636
|
+
const seen = firstSeen.get(mine.pr_url);
|
|
10637
|
+
if (!seen || seen.sha !== st.headSha) firstSeen.set(mine.pr_url, { sha: st.headSha, at: now });
|
|
10638
|
+
}
|
|
10639
|
+
const d = computeMergeQueue({ entry, ready: queue, behindBy: st.behindBy, checks: st.checks });
|
|
10640
|
+
const verdict = d.code === "queue_head" ? await ciSettled(mine.pr_url, st, now) : { action: d.code === "base_stale" ? "refresh_base" : d.code === "checks_red" ? "clear" : "wait", code: d.code, reason: d.reason };
|
|
10641
|
+
const final = verdict.action === "merge" || verdict.action === "clear" ? await confirmLive(mine.pr_url, st.headSha, verdict) : verdict;
|
|
10642
|
+
return { ...final, position, size: queue.length, ...st.headSha ? { headSha: st.headSha } : {} };
|
|
10643
|
+
};
|
|
10644
|
+
}
|
|
10450
10645
|
|
|
10451
10646
|
// src/mergeGate.ts
|
|
10452
10647
|
function findRunByPr(runs, ref, scope = {}) {
|
|
@@ -10475,9 +10670,15 @@ function makeMergeGateParts(deps) {
|
|
|
10475
10670
|
});
|
|
10476
10671
|
};
|
|
10477
10672
|
const repoOfRun = deps.repoFullNameOf ? (r) => deps.repoFullNameOf(effectiveRepoOf(r.repo, deps.repoFallback)) : void 0;
|
|
10673
|
+
const headState = makeHeadStateReader({
|
|
10674
|
+
...deps.baseSync ? { baseSync: deps.baseSync } : {},
|
|
10675
|
+
...deps.checks ? { checks: deps.checks } : {},
|
|
10676
|
+
...deps.ttlMs != null ? { ttlMs: deps.ttlMs } : {}
|
|
10677
|
+
});
|
|
10478
10678
|
const queueFor = makeMergeQueueResolver({
|
|
10479
10679
|
listRuns: deps.listRuns,
|
|
10480
10680
|
gateAllows: async (r) => (await gateFor(r)).allowed,
|
|
10681
|
+
headState,
|
|
10481
10682
|
...deps.repoFallback ? { repoFallback: deps.repoFallback } : {},
|
|
10482
10683
|
...deps.baseSync ? { baseSync: deps.baseSync } : {},
|
|
10483
10684
|
...deps.checks ? { checks: deps.checks } : {},
|
|
@@ -10503,7 +10704,16 @@ function makeMergeGateParts(deps) {
|
|
|
10503
10704
|
}
|
|
10504
10705
|
};
|
|
10505
10706
|
};
|
|
10506
|
-
|
|
10707
|
+
const intentFor = makeMergeIntentResolver({
|
|
10708
|
+
listRuns: deps.listRuns,
|
|
10709
|
+
headState,
|
|
10710
|
+
...deps.repoFallback ? { repoFallback: deps.repoFallback } : {},
|
|
10711
|
+
...deps.baseSync ? { baseSync: deps.baseSync } : {},
|
|
10712
|
+
...deps.checks ? { checks: deps.checks } : {},
|
|
10713
|
+
...deps.actionsRuns ? { actionsRuns: deps.actionsRuns } : {},
|
|
10714
|
+
...deps.ttlMs != null ? { ttlMs: deps.ttlMs } : {}
|
|
10715
|
+
});
|
|
10716
|
+
return { gate, queue: queueFor, intent: intentFor };
|
|
10507
10717
|
}
|
|
10508
10718
|
|
|
10509
10719
|
// src/web.ts
|