@yemi33/minions 0.1.450 → 0.1.452

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 CHANGED
@@ -1,11 +1,13 @@
1
1
  # Changelog
2
2
 
3
- ## 0.1.450 (2026-04-07)
3
+ ## 0.1.452 (2026-04-07)
4
4
 
5
5
  ### Features
6
6
  - notification dot on Notes & KB sidebar when sweep completes/fails
7
7
 
8
8
  ### Fixes
9
+ - remove duplicate retry logic causing dispatch double-queueing
10
+ - plan archive/unarchive now optimistic — immediate UI feedback
9
11
  - meeting conclude timeout synthesizes conclusion instead of null
10
12
  - track ADO head commit for push detection — unblocks re-review cycle
11
13
  - block all fix dispatches while awaiting re-review after a fix
@@ -550,18 +550,18 @@ async function planDelete(file) {
550
550
 
551
551
  async function planArchive(file, btn) {
552
552
  _stopPlanPoll();
553
- if (btn) { btn.dataset.origText = btn.textContent; btn.textContent = 'Archiving...'; btn.style.pointerEvents = 'none'; btn.style.opacity = '0.6'; }
554
- function resetBtn() { if (btn) { btn.textContent = btn.dataset.origText || 'Archive'; btn.style.pointerEvents = ''; btn.style.opacity = ''; } }
553
+ markDeleted('plan:' + file);
554
+ try { closeModal(); } catch { /* may not be open */ }
555
+ showToast('cmd-toast', 'Plan archived', true);
555
556
  try {
556
557
  const res = await fetch('/api/plans/archive', {
557
558
  method: 'POST', headers: { 'Content-Type': 'application/json' },
558
559
  body: JSON.stringify({ file })
559
560
  });
560
561
  const ct = res.headers.get('content-type') || '';
561
- if (!ct.includes('json')) { resetBtn(); alert('Archive failed — dashboard may need a restart'); return; }
562
+ if (!ct.includes('json')) { refresh(); return; }
562
563
  const d = await res.json().catch(() => ({}));
563
564
  if (res.ok && d.ok) {
564
- try { closeModal(); } catch { /* may not be open */ }
565
565
  var msg = 'Archived';
566
566
  if (d.archivedSource) msg += ' PRD + source plan (' + d.archivedSource + ')';
567
567
  if (d.cancelledItems) msg += ', cancelled ' + d.cancelledItems + ' pending item(s)';
@@ -721,25 +721,16 @@ async function triggerVerify(file, btn) {
721
721
  }
722
722
 
723
723
  async function planUnarchive(file, btn) {
724
- if (btn) { btn.dataset.origText = btn.textContent; btn.textContent = 'Restoring...'; btn.style.pointerEvents = 'none'; btn.style.opacity = '0.6'; }
725
- function resetBtn() { if (btn) { btn.textContent = btn.dataset.origText || 'Unarchive'; btn.style.pointerEvents = ''; btn.style.opacity = ''; } }
724
+ try { closeModal(); } catch {}
725
+ showToast('cmd-toast', 'Restored from archive', true);
726
726
  try {
727
727
  const res = await fetch('/api/plans/unarchive', {
728
728
  method: 'POST', headers: { 'Content-Type': 'application/json' },
729
729
  body: JSON.stringify({ file })
730
730
  });
731
- const ct = res.headers.get('content-type') || '';
732
- if (!ct.includes('json')) { resetBtn(); alert('Unarchive failed dashboard may need a restart'); return; }
733
- const d = await res.json().catch(() => ({}));
734
- if (res.ok && d.ok) {
735
- showToast('cmd-toast', 'Restored from archive', true);
736
- refreshPlans();
737
- refresh();
738
- } else {
739
- resetBtn();
740
- alert('Unarchive failed: ' + (d.error || 'unknown'));
741
- }
742
- } catch (e) { resetBtn(); alert('Error: ' + e.message); }
731
+ if (res.ok) { refreshPlans(); refresh(); }
732
+ else { const d = await res.json().catch(() => ({})); alert('Unarchive failed: ' + (d.error || 'unknown')); refresh(); }
733
+ } catch (e) { alert('Error: ' + e.message); refresh(); }
743
734
  }
744
735
 
745
736
  window.MinionsPlans = { openCreatePlanModal, refreshPlans, derivePlanStatus, renderPlans, openArchivedPlansModal, planExecute, planSubmitRevise, planShowRevise, planHideRevise, planView, planApprove, planArchive, planUnarchive, planDelete, planPause, planReject, planDiscuss, planOpenInDocChat, planRegeneratePRD, openVerifyGuide, triggerVerify };
@@ -1189,52 +1189,18 @@ function runPostCompletionHooks(dispatchItem, agentId, code, stdout, config) {
1189
1189
  meta._agentId = agentId;
1190
1190
  updateWorkItemStatus(meta, WI_STATUS.DONE, '');
1191
1191
  }
1192
- if (!effectiveSuccess && meta?.item?.id) {
1193
- // Auto-retry: read fresh _retryCount from file (not stale dispatch-time snapshot)
1194
- let retries = (meta.item._retryCount || 0);
1192
+ // Failure retry is handled by completeDispatch in dispatch.js — not duplicated here.
1193
+ // Only clear _decomposing flag on failure so decompose items don't get permanently stuck.
1194
+ if (!effectiveSuccess && meta?.item?.id && type === WORK_TYPE.DECOMPOSE) {
1195
1195
  const wiPath = resolveWorkItemPath(meta);
1196
- try {
1197
- if (wiPath) {
1198
- const items = safeJson(wiPath) || [];
1199
- const wi = items.find(i => i.id === meta.item.id);
1200
- if (wi) retries = (wi._retryCount || 0); // Use fresh value from file
1201
- }
1202
- } catch { /* optional */ }
1203
-
1204
- if (retries < ENGINE_DEFAULTS.maxRetries) {
1205
- log('info', `Agent failed for ${meta.item.id} — auto-retry ${retries + 1}/${ENGINE_DEFAULTS.maxRetries}`);
1206
- updateWorkItemStatus(meta, WI_STATUS.PENDING, '');
1196
+ if (wiPath) {
1207
1197
  try {
1208
- if (wiPath) {
1209
- mutateJsonFileLocked(wiPath, data => {
1210
- if (!Array.isArray(data)) return data;
1211
- const wi = data.find(i => i.id === meta.item.id);
1212
- if (!wi) return data;
1213
- // Don't revert if already completed by another code path
1214
- if (wi.status === WI_STATUS.DONE || wi.completedAt) {
1215
- log('info', `Skip retry for ${meta.item.id} — already completed`);
1216
- return data;
1217
- }
1218
- wi._retryCount = retries + 1; wi.status = WI_STATUS.PENDING; delete wi.dispatched_at; delete wi.dispatched_to; delete wi.failReason;
1219
- if (type === WORK_TYPE.DECOMPOSE) delete wi._decomposing;
1220
- return data;
1221
- });
1222
- }
1223
- } catch (err) { log('warn', `Retry update: ${err.message}`); }
1224
- } else {
1225
- updateWorkItemStatus(meta, WI_STATUS.FAILED, `Agent failed (${ENGINE_DEFAULTS.maxRetries} retries exhausted)`);
1226
- }
1227
- // Clear _decomposing flag on failure so item doesn't get permanently stuck
1228
- if (type === WORK_TYPE.DECOMPOSE) {
1229
- try {
1230
- if (wiPath) {
1231
- mutateJsonFileLocked(wiPath, data => {
1232
- if (!Array.isArray(data)) return data;
1233
- const wi = data.find(i => i.id === meta.item.id);
1234
- if (wi) delete wi._decomposing;
1235
- return data;
1236
- });
1237
- }
1198
+ mutateJsonFileLocked(wiPath, data => {
1199
+ if (!Array.isArray(data)) return data;
1200
+ const wi = data.find(i => i.id === meta.item.id);
1201
+ if (wi) delete wi._decomposing;
1202
+ return data;
1203
+ });
1238
1204
  } catch (err) { log('warn', `Decompose cleanup: ${err.message}`); }
1239
1205
  }
1240
1206
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.450",
3
+ "version": "0.1.452",
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"