@yemi33/minions 0.1.521 → 0.1.523
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/CHANGELOG.md +9 -1
- package/engine/ado.js +14 -11
- package/engine/cleanup.js +24 -1
- package/engine/lifecycle.js +3 -2
- package/engine.js +11 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 0.1.
|
|
3
|
+
## 0.1.523 (2026-04-07)
|
|
4
|
+
|
|
5
|
+
### Other
|
|
6
|
+
- perf: parallelize ADO PR polling within each project
|
|
7
|
+
|
|
8
|
+
## 0.1.522 (2026-04-07)
|
|
4
9
|
|
|
5
10
|
### Features
|
|
6
11
|
- test isolation — tests no longer pollute production data
|
|
7
12
|
|
|
13
|
+
### Fixes
|
|
14
|
+
- reconcile failed work items that have attached PRs (#407) (#430)
|
|
15
|
+
|
|
8
16
|
### Other
|
|
9
17
|
- perf: reduce curl timeout to 5s, cache getDispatch() reads
|
|
10
18
|
|
package/engine/ado.js
CHANGED
|
@@ -85,17 +85,20 @@ async function forEachActivePr(config, token, callback) {
|
|
|
85
85
|
if (activePrs.length === 0) continue;
|
|
86
86
|
|
|
87
87
|
let projectUpdated = 0;
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
const
|
|
96
|
-
if (
|
|
97
|
-
|
|
98
|
-
|
|
88
|
+
const orgBase = getAdoOrgBase(project);
|
|
89
|
+
|
|
90
|
+
// Parallelize PR polling within each project (max 5 concurrent to avoid rate limits)
|
|
91
|
+
const CONCURRENCY = 5;
|
|
92
|
+
for (let i = 0; i < activePrs.length; i += CONCURRENCY) {
|
|
93
|
+
const batch = activePrs.slice(i, i + CONCURRENCY);
|
|
94
|
+
const results = await Promise.allSettled(batch.map(async (pr) => {
|
|
95
|
+
const prNum = (pr.id || '').replace('PR-', '');
|
|
96
|
+
if (!prNum) return false;
|
|
97
|
+
return callback(project, pr, prNum, orgBase);
|
|
98
|
+
}));
|
|
99
|
+
for (const r of results) {
|
|
100
|
+
if (r.status === 'fulfilled' && r.value) projectUpdated++;
|
|
101
|
+
if (r.status === 'rejected') log('warn', `PR poll error: ${r.reason?.message || r.reason}`);
|
|
99
102
|
}
|
|
100
103
|
}
|
|
101
104
|
|
package/engine/cleanup.js
CHANGED
|
@@ -431,7 +431,30 @@ function runCleanup(config, verbose = false) {
|
|
|
431
431
|
}
|
|
432
432
|
} catch (e) { log('warn', 'KB watchdog check: ' + e.message); }
|
|
433
433
|
|
|
434
|
-
//
|
|
434
|
+
// 6a. Reconcile failed work items that have an attached PR (#407)
|
|
435
|
+
// If a work item is 'failed' but already has _pr, it should be 'done'.
|
|
436
|
+
for (const project of projects) {
|
|
437
|
+
try {
|
|
438
|
+
const wiPath = projectWorkItemsPath(project);
|
|
439
|
+
const items = safeJson(wiPath) || [];
|
|
440
|
+
let reconciled = 0;
|
|
441
|
+
for (const item of items) {
|
|
442
|
+
if (item.status === shared.WI_STATUS.FAILED && item._pr) {
|
|
443
|
+
item.status = shared.WI_STATUS.DONE;
|
|
444
|
+
if (item.failReason) delete item.failReason;
|
|
445
|
+
if (item.failedAt) delete item.failedAt;
|
|
446
|
+
if (!item.completedAt) item.completedAt = shared.ts();
|
|
447
|
+
reconciled++;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
if (reconciled > 0) {
|
|
451
|
+
safeWrite(wiPath, items);
|
|
452
|
+
log('info', `Reconciled ${reconciled} failed-with-PR item(s) → done in ${project.name}`);
|
|
453
|
+
}
|
|
454
|
+
} catch (e) { log('warn', 'reconcile failed-with-PR: ' + e.message); }
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// 6b. Migrate legacy work-item statuses to canonical 'done'
|
|
435
458
|
// in-pr, implemented, complete → done (one-time correction per item)
|
|
436
459
|
const LEGACY_DONE_ALIASES = new Set(['in-pr', 'implemented', 'complete']);
|
|
437
460
|
for (const project of projects) {
|
package/engine/lifecycle.js
CHANGED
|
@@ -1372,7 +1372,8 @@ function syncPrdFromPrs(config) {
|
|
|
1372
1372
|
for (const project of allProjects) {
|
|
1373
1373
|
const wiPath = shared.projectWorkItemsPath(project);
|
|
1374
1374
|
const items = safeJson(wiPath) || [];
|
|
1375
|
-
const hasReconcilable = items.some(wi =>
|
|
1375
|
+
const hasReconcilable = items.some(wi =>
|
|
1376
|
+
(wi.status === WI_STATUS.PENDING && !wi._pr) || wi.status === WI_STATUS.FAILED);
|
|
1376
1377
|
if (!hasReconcilable) continue;
|
|
1377
1378
|
let reconciled = 0;
|
|
1378
1379
|
const reconciledItems = mutateJsonFileLocked(wiPath, data => {
|
|
@@ -1389,7 +1390,7 @@ function syncPrdFromPrs(config) {
|
|
|
1389
1390
|
}
|
|
1390
1391
|
}
|
|
1391
1392
|
if (totalReconciled > 0) {
|
|
1392
|
-
log('info', `PR sync: reconciled ${totalReconciled}
|
|
1393
|
+
log('info', `PR sync: reconciled ${totalReconciled} work item(s) to done`);
|
|
1393
1394
|
}
|
|
1394
1395
|
} catch (err) {
|
|
1395
1396
|
// Non-fatal — log and continue
|
package/engine.js
CHANGED
|
@@ -878,6 +878,16 @@ function reconcileItemsWithPrs(items, allPrs, { onlyIds } = {}) {
|
|
|
878
878
|
if (wi.status !== WI_STATUS.PENDING && wi.status !== WI_STATUS.FAILED) continue;
|
|
879
879
|
if (onlyIds && !onlyIds.has(wi.id)) continue;
|
|
880
880
|
|
|
881
|
+
// Short-circuit: failed item already has a PR attached — mark done directly (#407)
|
|
882
|
+
if (wi.status === WI_STATUS.FAILED && wi._pr) {
|
|
883
|
+
wi.status = WI_STATUS.DONE;
|
|
884
|
+
if (wi.failReason) delete wi.failReason;
|
|
885
|
+
if (wi.failedAt) delete wi.failedAt;
|
|
886
|
+
if (!wi.completedAt) wi.completedAt = ts();
|
|
887
|
+
reconciled++;
|
|
888
|
+
continue;
|
|
889
|
+
}
|
|
890
|
+
|
|
881
891
|
let exactPr = allPrs.find(pr => (pr.prdItems || []).includes(wi.id));
|
|
882
892
|
if (!exactPr) {
|
|
883
893
|
const linkedPrId = Object.keys(prLinks).find(prId => prLinks[prId] === wi.id);
|
|
@@ -893,7 +903,7 @@ function reconcileItemsWithPrs(items, allPrs, { onlyIds } = {}) {
|
|
|
893
903
|
// Clear failure artifacts if reconciling a previously failed item
|
|
894
904
|
if (wi.failReason) delete wi.failReason;
|
|
895
905
|
if (wi.failedAt) delete wi.failedAt;
|
|
896
|
-
if (!wi.completedAt) wi.completedAt =
|
|
906
|
+
if (!wi.completedAt) wi.completedAt = ts();
|
|
897
907
|
reconciled++;
|
|
898
908
|
}
|
|
899
909
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.523",
|
|
4
4
|
"description": "Multi-agent AI dev team that runs from ~/.minions/ — five autonomous agents share a single engine, dashboard, and knowledge base",
|
|
5
5
|
"bin": {
|
|
6
6
|
"minions": "bin/minions.js"
|