@yemi33/minions 0.1.2317 → 0.1.2318

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.
@@ -853,7 +853,19 @@ function completeDispatch(id, result = DISPATCH_RESULT.SUCCESS, reason = '', res
853
853
  completedWorkItemFailure = isCompletedWorkItemForFailure(liveWi);
854
854
  } catch (e) { log('warn', 'read live work item before retry: ' + e.message); }
855
855
  }
856
- if (result === DISPATCH_RESULT.ERROR && item.meta?.dispatchKey && retryableFailure && !completedWorkItemFailure) {
856
+ // #634 cooldown backoff bookkeeping is independent of the retry decision.
857
+ // `retryableFailure` (== `agentRetryable`) governs whether the engine will
858
+ // ever re-dispatch this work; `setCooldownFailure` governs how aggressively
859
+ // it backs off between attempts. Previously this was gated on
860
+ // `retryableFailure`, so a caller marking a failure `agentRetryable: false`
861
+ // (structural/non-retryable classes like LIVE_CHECKOUT_WORKTREE_CONFLICT,
862
+ // WORKSPACE_MANIFEST_REPO, INVALID_WORKDIR, etc.) skipped cooldown
863
+ // bookkeeping entirely — leaving only the flat 15-minute `isAlreadyDispatched`
864
+ // dedup window as the sole re-dispatch gate, so the identical doomed
865
+ // dispatch key looped forever every ~15 minutes with zero backoff. Always
866
+ // record the failure here so `isOnCooldown`'s exponential backoff applies
867
+ // regardless of whether the failure is retryable.
868
+ if (result === DISPATCH_RESULT.ERROR && item.meta?.dispatchKey && !completedWorkItemFailure) {
857
869
  setCooldownFailure(item.meta.dispatchKey);
858
870
  }
859
871
 
package/engine/github.js CHANGED
@@ -690,6 +690,15 @@ async function forEachActiveGhPr(config, callback) {
690
690
  for (const { before, after } of updatedCentralRecords) {
691
691
  const idx = currentPrs.findIndex(p => p.id === after.id);
692
692
  if (idx >= 0) {
693
+ // W-mr3gr4wl0004b8e3: never downgrade reviewStatus from 'approved' — it's a
694
+ // permanent terminal state. Mirrors the project-local write-back guard above
695
+ // (github.js:561-563) and ado.js's project/central write-backs (ado.js:1211-1213,
696
+ // :1360-1362). applyPrFieldDelta only merges fields changed during a given poll
697
+ // cycle, so this guard is the sole race protection against a stale/out-of-order
698
+ // GitHub poll response downgrading an already-approved PR.
699
+ if (currentPrs[idx].reviewStatus === REVIEW_STATUS.APPROVED && after.reviewStatus !== REVIEW_STATUS.APPROVED) {
700
+ after.reviewStatus = REVIEW_STATUS.APPROVED;
701
+ }
693
702
  // W-mp5trwh60008386d: same merged-status guard as project-local PRs.
694
703
  if (currentPrs[idx].status === PR_STATUS.MERGED && after.status !== PR_STATUS.MERGED) {
695
704
  after.status = PR_STATUS.MERGED;
package/engine.js CHANGED
@@ -7185,7 +7185,19 @@ async function discoverFromPrs(config, project) {
7185
7185
  coalesceCurrentHumanFeedback();
7186
7186
  continue;
7187
7187
  }
7188
- if (blockedByCooldown && !alreadyDispatched) {
7188
+ // #634 only clear the cooldown when it holds no real failure history
7189
+ // (i.e. it was created purely by `setCooldownWithContext` to park
7190
+ // coalesced feedback while `fixThrottled`, and `failures` is still 0).
7191
+ // A cooldown with `failures > 0` came from `setCooldownFailure` after an
7192
+ // actual dispatch error — including non-retryable structural failures,
7193
+ // which now always record a failure (see dispatch.js#completeDispatch).
7194
+ // Clearing it just because the 15-min `isAlreadyDispatched` window
7195
+ // closed would erase the exponential backoff and re-dispatch the same
7196
+ // doomed key every ~15 minutes, defeating the whole point of the
7197
+ // backoff. Only genuinely orphaned (never-attempted) cooldowns are
7198
+ // cleared as stale.
7199
+ const isGenuineFailureCooldown = (dispatchCooldowns.get(key)?.failures || 0) > 0;
7200
+ if (blockedByCooldown && !alreadyDispatched && !isGenuineFailureCooldown) {
7189
7201
  staleCoalesced = drainCoalescedContexts(key);
7190
7202
  clearCooldown(key);
7191
7203
  log('info', `Cleared stale cooldown for ${key} — no matching dispatch history`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.2317",
3
+ "version": "0.1.2318",
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"