@yemi33/minions 0.1.1673 → 0.1.1674
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 +5 -0
- package/dashboard/js/render-work-items.js +6 -6
- package/docs/rfc-completion-json.md +2 -2
- package/engine/cleanup.js +43 -47
- package/engine/copilot-models.json +1 -1
- package/engine/lifecycle.js +34 -24
- package/engine/shared.js +1 -1
- package/engine.js +21 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -30,7 +30,7 @@ function wiRetryBtn(item) {
|
|
|
30
30
|
|
|
31
31
|
function wiRow(item) {
|
|
32
32
|
const statusBadge = (s) => {
|
|
33
|
-
const cls = s === 'failed' ? 'rejected' : s === '
|
|
33
|
+
const cls = s === 'failed' ? 'rejected' : s === 'dispatched' ? 'building' : s === 'pending' || s === 'queued' ? 'active' : s === 'done' ? 'approved' : s === 'decomposed' ? 'approved' : 'draft';
|
|
34
34
|
return '<span class="pr-badge ' + cls + '">' + escapeHtml(s) + '</span>';
|
|
35
35
|
};
|
|
36
36
|
const typeBadge = (t) => '<span class="dispatch-type ' + (t || 'implement') + '">' + escapeHtml(t || 'implement') + '</span>';
|
|
@@ -67,10 +67,10 @@ function wiRow(item) {
|
|
|
67
67
|
(item.acceptanceCriteria && item.acceptanceCriteria.length ? '<span title="' + item.acceptanceCriteria.length + ' acceptance criteria">☑' + item.acceptanceCriteria.length + '</span>' : '') +
|
|
68
68
|
'</td>' +
|
|
69
69
|
'<td style="white-space:nowrap">' +
|
|
70
|
-
((item.status === 'pending' || item.status === 'failed'
|
|
71
|
-
((item.status === 'done' || item.status === 'failed'
|
|
72
|
-
((item.status === 'done' || item.status === 'failed'
|
|
73
|
-
((item.status === 'pending' || item.status === 'dispatched' || item.status === 'queued' || item.status === 'failed'
|
|
70
|
+
((item.status === 'pending' || item.status === 'failed') ? '<button class="pr-pager-btn" style="font-size:9px;padding:1px 6px;color:var(--blue);border-color:var(--blue);margin-right:4px" onclick="event.stopPropagation();editWorkItem(\'' + escapeHtml(item.id) + '\',\'' + escapeHtml(item._source || '') + '\')" title="Edit work item">✎</button>' : '') +
|
|
71
|
+
((item.status === 'done' || item.status === 'failed') ? '<button class="pr-pager-btn" style="font-size:9px;padding:1px 6px;color:var(--muted);border-color:var(--border);margin-right:4px" onclick="event.stopPropagation();archiveWorkItem(\'' + escapeHtml(item.id) + '\',\'' + escapeHtml(item._source || '') + '\')" title="Archive work item">📦</button>' : '') +
|
|
72
|
+
((item.status === 'done' || item.status === 'failed') && !item._humanFeedback ? '<button class="pr-pager-btn" style="font-size:9px;padding:1px 6px;color:var(--green);border-color:var(--green);margin-right:4px" onclick="event.stopPropagation();feedbackWorkItem(\'' + escapeHtml(item.id) + '\',\'' + escapeHtml(item._source || '') + '\')" title="Give feedback">👍👎</button>' : (item._humanFeedback ? '<span style="font-size:9px" title="Feedback given">' + (item._humanFeedback.rating === 'up' ? '👍' : '👎') + '</span> ' : '')) +
|
|
73
|
+
((item.status === 'pending' || item.status === 'dispatched' || item.status === 'queued' || item.status === 'failed') ? '<button class="pr-pager-btn" style="font-size:9px;padding:1px 6px;color:var(--orange);border-color:var(--orange);margin-right:4px" onclick="event.stopPropagation();cancelWorkItem(\'' + escapeHtml(item.id) + '\',\'' + escapeHtml(item._source || '') + '\')" title="Cancel work item and kill agent">🚫</button>' : '') +
|
|
74
74
|
'<button class="pr-pager-btn" style="font-size:9px;padding:1px 6px;color:var(--red);border-color:var(--red)" onclick="event.stopPropagation();deleteWorkItem(\'' + escapeHtml(item.id) + '\',\'' + escapeHtml(item._source || '') + '\')" title="Delete work item and kill agent">✕</button>' +
|
|
75
75
|
'</td>' +
|
|
76
76
|
'</tr>';
|
|
@@ -79,7 +79,7 @@ function wiRow(item) {
|
|
|
79
79
|
function renderWorkItems(items) {
|
|
80
80
|
items = items.filter(function(w) { return !isDeleted('wi:' + w.id); });
|
|
81
81
|
// Sort: active/dispatched first, then by most recent activity
|
|
82
|
-
const statusOrder = { dispatched: 0, pending: 1, queued: 1,
|
|
82
|
+
const statusOrder = { dispatched: 0, pending: 1, queued: 1, failed: 2, done: 3 };
|
|
83
83
|
items.sort((a, b) => {
|
|
84
84
|
const sa = statusOrder[a.status] ?? 2, sb = statusOrder[b.status] ?? 2;
|
|
85
85
|
if (sa !== sb) return sa - sb;
|
|
@@ -187,7 +187,7 @@ The agent must not write the file in pieces. Empty, truncated, or malformed JSON
|
|
|
187
187
|
| `partial` | Some progress; agent ran out of turns or hit a known stop point | Auto-retry per `RECOVERY_RECIPES` (`engine/recovery.js`) |
|
|
188
188
|
| `failed` | Hard failure; no recovery attempted by agent | Use `failure.class` to pick recipe |
|
|
189
189
|
| `noop` | Idempotent bail (review already posted, plan already shipped, etc.) | Mark WI `done` without retry, no failure metric |
|
|
190
|
-
| `needs-review` | Agent could not classify; flag for human | Set WI `
|
|
190
|
+
| `needs-review` | Agent could not classify; flag for human | Set WI `failed` with an explicit `failReason` |
|
|
191
191
|
|
|
192
192
|
`noop` collapses the current `isReviewBailout` (lifecycle.js:907), the `verify-plan-already-shipped` family of skills, and the "shared-branch redispatch" skill into a single explicit signal. Any agent that detects "the work is already done" returns `status: "noop"` and a one-line `summary` — the engine takes the success path without retry.
|
|
193
193
|
|
|
@@ -370,7 +370,7 @@ The current ` ```completion ` block in `playbooks/fix.md:85-93` and `playbooks/i
|
|
|
370
370
|
|
|
371
371
|
### 7.3 No-PR Tasks
|
|
372
372
|
|
|
373
|
-
`explore`, `ask`, `test`, `docs`, `plan-to-prd`, and the read-only legs of `meeting-*` simply omit `prs[]`. They still write completion.json with `status` + `summary`. This makes "I had nothing to push" an explicit signal instead of inferred from "no PR URL found in stdout" (which today triggers the auto-retry-then-
|
|
373
|
+
`explore`, `ask`, `test`, `docs`, `plan-to-prd`, and the read-only legs of `meeting-*` simply omit `prs[]`. They still write completion.json with `status` + `summary`. This makes "I had nothing to push" an explicit signal instead of inferred from "no PR URL found in stdout" (which today triggers the auto-retry-then-failed chain at `lifecycle.js:1943-1984`).
|
|
374
374
|
|
|
375
375
|
## 8. Validation & Testing
|
|
376
376
|
|
package/engine/cleanup.js
CHANGED
|
@@ -507,48 +507,39 @@ function runCleanup(config, verbose = false) {
|
|
|
507
507
|
} catch (e) { log('warn', 'reconcile failed-with-PR: ' + e.message); }
|
|
508
508
|
}
|
|
509
509
|
|
|
510
|
-
// 6b. Migrate legacy work-item statuses to canonical
|
|
511
|
-
// in-pr, implemented, complete → done
|
|
510
|
+
// 6b. Migrate legacy work-item statuses to canonical replacements
|
|
511
|
+
// in-pr, implemented, complete → done; needs-human-review → failed
|
|
512
512
|
const LEGACY_DONE_ALIASES = new Set(['in-pr', 'implemented', 'complete']);
|
|
513
|
-
|
|
513
|
+
const LEGACY_NEEDS_REVIEW_STATUS = 'needs-human-review';
|
|
514
|
+
const LEGACY_NEEDS_REVIEW_FAIL_REASON = 'Manual intervention required (migrated from needs-human-review)';
|
|
515
|
+
function _migrateLegacyItem(item) {
|
|
516
|
+
if (LEGACY_DONE_ALIASES.has(item.status)) {
|
|
517
|
+
item.status = shared.WI_STATUS.DONE;
|
|
518
|
+
delete item._retryCount;
|
|
519
|
+
delete item._pendingReason;
|
|
520
|
+
if (!item.completedAt) item.completedAt = shared.ts();
|
|
521
|
+
return true;
|
|
522
|
+
}
|
|
523
|
+
if (item.status === LEGACY_NEEDS_REVIEW_STATUS) {
|
|
524
|
+
item.status = shared.WI_STATUS.FAILED;
|
|
525
|
+
if (!item.failReason) item.failReason = LEGACY_NEEDS_REVIEW_FAIL_REASON;
|
|
526
|
+
if (!item.failedAt) item.failedAt = shared.ts();
|
|
527
|
+
delete item.completedAt;
|
|
528
|
+
return true;
|
|
529
|
+
}
|
|
530
|
+
return false;
|
|
531
|
+
}
|
|
532
|
+
function _migrateLegacyItemsAt(wiPath, label) {
|
|
514
533
|
try {
|
|
515
|
-
const wiPath = projectWorkItemsPath(project);
|
|
516
534
|
let migrated = 0;
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
delete item._pendingReason;
|
|
523
|
-
if (!item.completedAt) item.completedAt = shared.ts();
|
|
524
|
-
migrated++;
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
});
|
|
528
|
-
if (migrated > 0) {
|
|
529
|
-
log('info', `Migrated ${migrated} legacy status(es) → done in ${project.name} work items`);
|
|
530
|
-
}
|
|
531
|
-
} catch (e) { log('warn', 'migrate legacy statuses: ' + e.message); }
|
|
535
|
+
mutateWorkItems(wiPath, items => {
|
|
536
|
+
for (const item of items) if (_migrateLegacyItem(item)) migrated++;
|
|
537
|
+
});
|
|
538
|
+
if (migrated > 0) log('info', `Migrated ${migrated} legacy status(es) in ${label}`);
|
|
539
|
+
} catch (e) { log('warn', `migrate legacy statuses (${label}): ${e.message}`); }
|
|
532
540
|
}
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
const centralPath = path.join(MINIONS_DIR, 'work-items.json');
|
|
536
|
-
let migrated = 0;
|
|
537
|
-
mutateWorkItems(centralPath, items => {
|
|
538
|
-
for (const item of items) {
|
|
539
|
-
if (LEGACY_DONE_ALIASES.has(item.status)) {
|
|
540
|
-
item.status = shared.WI_STATUS.DONE;
|
|
541
|
-
delete item._retryCount;
|
|
542
|
-
delete item._pendingReason;
|
|
543
|
-
if (!item.completedAt) item.completedAt = shared.ts();
|
|
544
|
-
migrated++;
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
});
|
|
548
|
-
if (migrated > 0) {
|
|
549
|
-
log('info', `Migrated ${migrated} legacy status(es) → done in central work items`);
|
|
550
|
-
}
|
|
551
|
-
} catch (e) { log('warn', 'migrate central legacy statuses: ' + e.message); }
|
|
541
|
+
for (const project of projects) _migrateLegacyItemsAt(projectWorkItemsPath(project), `${project.name} work items`);
|
|
542
|
+
_migrateLegacyItemsAt(path.join(MINIONS_DIR, 'work-items.json'), 'central work items');
|
|
552
543
|
|
|
553
544
|
// 6c. Strip stale retry metadata from completed work items
|
|
554
545
|
cleaned.doneRetryCounts = 0;
|
|
@@ -591,18 +582,23 @@ function runCleanup(config, verbose = false) {
|
|
|
591
582
|
const prdFiles = prdDirEntries.filter(f => f.endsWith('.json'));
|
|
592
583
|
for (const pf of prdFiles) {
|
|
593
584
|
const prdPath = path.join(PRD_DIR, pf);
|
|
594
|
-
const prd = safeJson(prdPath);
|
|
595
|
-
if (!prd?.missing_features) continue;
|
|
596
585
|
let migrated = 0;
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
586
|
+
shared.withFileLock(`${prdPath}.lock`, () => {
|
|
587
|
+
const prd = safeJson(prdPath);
|
|
588
|
+
if (!prd?.missing_features) return;
|
|
589
|
+
for (const feat of prd.missing_features) {
|
|
590
|
+
if (LEGACY_DONE_ALIASES.has(feat.status)) {
|
|
591
|
+
feat.status = shared.WI_STATUS.DONE;
|
|
592
|
+
migrated++;
|
|
593
|
+
} else if (feat.status === LEGACY_NEEDS_REVIEW_STATUS) {
|
|
594
|
+
feat.status = shared.WI_STATUS.FAILED;
|
|
595
|
+
migrated++;
|
|
596
|
+
}
|
|
601
597
|
}
|
|
602
|
-
|
|
598
|
+
if (migrated > 0) safeWrite(prdPath, prd);
|
|
599
|
+
});
|
|
603
600
|
if (migrated > 0) {
|
|
604
|
-
|
|
605
|
-
log('info', `Migrated ${migrated} legacy PRD item status(es) → done in ${pf}`);
|
|
601
|
+
log('info', `Migrated ${migrated} legacy PRD item status(es) in ${pf}`);
|
|
606
602
|
}
|
|
607
603
|
}
|
|
608
604
|
} catch (e) { log('warn', 'migrate PRD legacy statuses: ' + e.message); }
|
package/engine/lifecycle.js
CHANGED
|
@@ -637,14 +637,21 @@ function syncPrdItemStatus(itemId, status, sourcePlan) {
|
|
|
637
637
|
const files = sourcePlan ? [sourcePlan] : require('fs').readdirSync(prdDir).filter(f => f.endsWith('.json'));
|
|
638
638
|
for (const pf of files) {
|
|
639
639
|
const fpath = path.join(prdDir, pf);
|
|
640
|
+
// Lock-free peek: most PRDs won't contain the ID, so skip the lock cost.
|
|
640
641
|
const plan = safeJson(fpath);
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
642
|
+
const feature = plan?.missing_features?.find(f => f.id === itemId);
|
|
643
|
+
if (!feature || feature.status === status) continue;
|
|
644
|
+
let updated = false;
|
|
645
|
+
shared.withFileLock(`${fpath}.lock`, () => {
|
|
646
|
+
const fresh = safeJson(fpath);
|
|
647
|
+
const f = fresh?.missing_features?.find(x => x.id === itemId);
|
|
648
|
+
if (f && f.status !== status) {
|
|
649
|
+
f.status = status;
|
|
650
|
+
safeWrite(fpath, fresh);
|
|
651
|
+
updated = true;
|
|
652
|
+
}
|
|
653
|
+
});
|
|
654
|
+
if (updated) return;
|
|
648
655
|
}
|
|
649
656
|
} catch (err) { log('warn', `PRD status sync: ${err.message}`); }
|
|
650
657
|
}
|
|
@@ -947,18 +954,19 @@ function isPrAttachmentRequired(type, item, meta = {}) {
|
|
|
947
954
|
if (branchStrategy === 'shared-branch' && item.itemType !== 'pr' && !explicit) return false;
|
|
948
955
|
|
|
949
956
|
// Fix/test work items dispatched against an existing PR don't produce a new
|
|
950
|
-
// PR — the agent updates meta.pr in place.
|
|
951
|
-
//
|
|
952
|
-
//
|
|
953
|
-
// explicit-flag fallthrough so a legacy requiresPr:true fix doesn't trigger
|
|
954
|
-
// the contract when there's already a PR attached.
|
|
957
|
+
// PR — the agent updates meta.pr in place. The meta.pr short-circuit beats
|
|
958
|
+
// the explicit-flag fallthrough so a legacy requiresPr:true fix doesn't
|
|
959
|
+
// trigger the contract when there's already a PR attached.
|
|
955
960
|
if ((type === WORK_TYPE.FIX || type === WORK_TYPE.TEST) && meta?.pr) return false;
|
|
956
961
|
|
|
962
|
+
// Standalone test work is usually pure build/run/verify. It should only be
|
|
963
|
+
// PR-required when the caller explicitly marks it as file-changing work.
|
|
964
|
+
if (type === WORK_TYPE.TEST && !explicit) return false;
|
|
965
|
+
|
|
957
966
|
return explicit
|
|
958
967
|
|| type === WORK_TYPE.IMPLEMENT
|
|
959
968
|
|| type === WORK_TYPE.IMPLEMENT_LARGE
|
|
960
|
-
|| type === WORK_TYPE.FIX
|
|
961
|
-
|| type === WORK_TYPE.TEST;
|
|
969
|
+
|| type === WORK_TYPE.FIX;
|
|
962
970
|
}
|
|
963
971
|
|
|
964
972
|
function readOptionalJsonStrict(filePath, label, validate) {
|
|
@@ -1148,18 +1156,19 @@ function _outputContainsPrUrl(output) {
|
|
|
1148
1156
|
function markMissingPrAttachment(meta, agentId, reason, resultSummary, severity) {
|
|
1149
1157
|
const noPrWiPath = resolveWorkItemPath(meta);
|
|
1150
1158
|
const isHard = severity !== 'soft';
|
|
1151
|
-
let
|
|
1159
|
+
let syncFailedToPrd = false;
|
|
1152
1160
|
if (noPrWiPath) {
|
|
1153
1161
|
mutateJsonFileLocked(noPrWiPath, data => {
|
|
1154
1162
|
if (!Array.isArray(data)) return data;
|
|
1155
1163
|
const w = data.find(i => i.id === meta.item.id);
|
|
1156
1164
|
if (!w) return data;
|
|
1157
1165
|
if (isHard) {
|
|
1158
|
-
w.status = WI_STATUS.
|
|
1166
|
+
w.status = WI_STATUS.FAILED;
|
|
1159
1167
|
w._missingPrAttachment = true;
|
|
1160
1168
|
w.failReason = reason;
|
|
1169
|
+
w.failedAt = ts();
|
|
1161
1170
|
w._lastReviewReason = reason;
|
|
1162
|
-
|
|
1171
|
+
syncFailedToPrd = !!meta.item?.sourcePlan;
|
|
1163
1172
|
delete w.completedAt;
|
|
1164
1173
|
delete w._noPr;
|
|
1165
1174
|
delete w._noPrReason;
|
|
@@ -1173,8 +1182,8 @@ function markMissingPrAttachment(meta, agentId, reason, resultSummary, severity)
|
|
|
1173
1182
|
return data;
|
|
1174
1183
|
}, { skipWriteIfUnchanged: true });
|
|
1175
1184
|
}
|
|
1176
|
-
if (isHard &&
|
|
1177
|
-
syncPrdItemStatus(meta.item.id, WI_STATUS.
|
|
1185
|
+
if (isHard && syncFailedToPrd) {
|
|
1186
|
+
syncPrdItemStatus(meta.item.id, WI_STATUS.FAILED, meta.item.sourcePlan);
|
|
1178
1187
|
}
|
|
1179
1188
|
if (isHard) {
|
|
1180
1189
|
shared.writeToInbox('engine', `missing-pr-attachment-${meta.item.id}`,
|
|
@@ -1206,25 +1215,26 @@ function markMissingPrAttachment(meta, agentId, reason, resultSummary, severity)
|
|
|
1206
1215
|
|
|
1207
1216
|
function markPrAttachmentVerificationError(meta, agentId, reason, resultSummary) {
|
|
1208
1217
|
const wiPath = resolveWorkItemPath(meta);
|
|
1209
|
-
let
|
|
1218
|
+
let syncFailedToPrd = false;
|
|
1210
1219
|
if (wiPath) {
|
|
1211
1220
|
mutateJsonFileLocked(wiPath, data => {
|
|
1212
1221
|
if (!Array.isArray(data)) return data;
|
|
1213
1222
|
const w = data.find(i => i.id === meta.item.id);
|
|
1214
1223
|
if (!w) return data;
|
|
1215
|
-
w.status = WI_STATUS.
|
|
1224
|
+
w.status = WI_STATUS.FAILED;
|
|
1216
1225
|
w._prAttachmentStateError = true;
|
|
1217
1226
|
w.failReason = reason;
|
|
1227
|
+
w.failedAt = ts();
|
|
1218
1228
|
w._lastReviewReason = reason;
|
|
1219
|
-
|
|
1229
|
+
syncFailedToPrd = !!meta.item?.sourcePlan;
|
|
1220
1230
|
delete w.completedAt;
|
|
1221
1231
|
delete w._missingPrAttachment;
|
|
1222
1232
|
delete w._unverifiedPrAttachment;
|
|
1223
1233
|
return data;
|
|
1224
1234
|
}, { skipWriteIfUnchanged: true });
|
|
1225
1235
|
}
|
|
1226
|
-
if (
|
|
1227
|
-
syncPrdItemStatus(meta.item.id, WI_STATUS.
|
|
1236
|
+
if (syncFailedToPrd) {
|
|
1237
|
+
syncPrdItemStatus(meta.item.id, WI_STATUS.FAILED, meta.item.sourcePlan);
|
|
1228
1238
|
}
|
|
1229
1239
|
shared.writeToInbox('engine', `pr-attachment-state-error-${meta.item.id}`,
|
|
1230
1240
|
`# PR attachment verification blocked for ${meta.item.id}\n\n` +
|
package/engine/shared.js
CHANGED
|
@@ -1089,7 +1089,7 @@ function runtimeConfigWarnings(config, registeredRuntimes) {
|
|
|
1089
1089
|
|
|
1090
1090
|
const WI_STATUS = {
|
|
1091
1091
|
PENDING: 'pending', DISPATCHED: 'dispatched', DONE: 'done', FAILED: 'failed',
|
|
1092
|
-
PAUSED: 'paused', QUEUED: 'queued',
|
|
1092
|
+
PAUSED: 'paused', QUEUED: 'queued',
|
|
1093
1093
|
DECOMPOSED: 'decomposed', CANCELLED: 'cancelled',
|
|
1094
1094
|
};
|
|
1095
1095
|
// Read-side: accept legacy aliases for backward compat with old data/clients.
|
package/engine.js
CHANGED
|
@@ -100,6 +100,8 @@ const mutateWorkItems = shared.mutateWorkItems;
|
|
|
100
100
|
const mutatePullRequests = shared.mutatePullRequests;
|
|
101
101
|
const withFileLock = shared.withFileLock;
|
|
102
102
|
|
|
103
|
+
const CHECKPOINT_CAP_FAIL_REASON = 'Exceeded 3 checkpoint-resumes; manual intervention required';
|
|
104
|
+
|
|
103
105
|
// ─── Dispatch Management (extracted to engine/dispatch.js) ───────────────────
|
|
104
106
|
|
|
105
107
|
const { mutateDispatch, addToDispatch, isRetryableFailureReason, completeDispatch,
|
|
@@ -2789,10 +2791,16 @@ function discoverFromWorkItems(config, project) {
|
|
|
2789
2791
|
const promptAgentId = deferredAgentResolution ? reservedAgentId : agentId;
|
|
2790
2792
|
const promptResult = renderProjectWorkItemPromptForAgent(item, workType, promptAgentId, config, project, root, branchName);
|
|
2791
2793
|
if (promptResult.needsReview) {
|
|
2792
|
-
log('warn', `Work item ${item.id} exceeded 3 checkpoint-resumes — marking as
|
|
2793
|
-
item.status = WI_STATUS.
|
|
2794
|
+
log('warn', `Work item ${item.id} exceeded 3 checkpoint-resumes — marking as failed for manual intervention`);
|
|
2795
|
+
item.status = WI_STATUS.FAILED;
|
|
2796
|
+
item.failReason = CHECKPOINT_CAP_FAIL_REASON;
|
|
2797
|
+
item.failedAt = ts();
|
|
2794
2798
|
item._checkpointCount = promptResult.checkpointCount;
|
|
2795
2799
|
needsWrite = true;
|
|
2800
|
+
if (item.sourcePlan) {
|
|
2801
|
+
try { syncPrdItemStatus(item.id, WI_STATUS.FAILED, item.sourcePlan); }
|
|
2802
|
+
catch (e) { log('warn', `PRD status sync after checkpoint cap (${item.id}): ${e.message}`); }
|
|
2803
|
+
}
|
|
2796
2804
|
continue;
|
|
2797
2805
|
}
|
|
2798
2806
|
if (promptResult.checkpointCount !== null) {
|
|
@@ -3305,8 +3313,17 @@ function discoverCentralWorkItems(config) {
|
|
|
3305
3313
|
workType,
|
|
3306
3314
|
});
|
|
3307
3315
|
if (cpResult.needsReview) {
|
|
3308
|
-
log('warn', `Work item ${item.id} exceeded 3 checkpoint-resumes — marking as
|
|
3309
|
-
mutations.set(item.id, {
|
|
3316
|
+
log('warn', `Work item ${item.id} exceeded 3 checkpoint-resumes — marking as failed for manual intervention`);
|
|
3317
|
+
mutations.set(item.id, {
|
|
3318
|
+
status: WI_STATUS.FAILED,
|
|
3319
|
+
failReason: CHECKPOINT_CAP_FAIL_REASON,
|
|
3320
|
+
failedAt: ts(),
|
|
3321
|
+
_checkpointCount: cpResult.checkpointCount,
|
|
3322
|
+
});
|
|
3323
|
+
if (item.sourcePlan) {
|
|
3324
|
+
try { syncPrdItemStatus(item.id, WI_STATUS.FAILED, item.sourcePlan); }
|
|
3325
|
+
catch (e) { log('warn', `PRD status sync after checkpoint cap (${item.id}): ${e.message}`); }
|
|
3326
|
+
}
|
|
3310
3327
|
continue;
|
|
3311
3328
|
}
|
|
3312
3329
|
if (cpResult.checkpointCount !== null) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1674",
|
|
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"
|