@yemi33/minions 0.1.2029 → 0.1.2030

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.
@@ -7,7 +7,7 @@ const fs = require('fs');
7
7
  const path = require('path');
8
8
  const shared = require('./shared');
9
9
  const queries = require('./queries');
10
- const { setCooldownFailure } = require('./cooldown');
10
+ const { setCooldown, setCooldownFailure } = require('./cooldown');
11
11
 
12
12
  const { safeJson, mutateJsonFileLocked, mutateWorkItems,
13
13
  mutatePullRequests, getProjects, projectWorkItemsPath, projectPrPath, log, ts, dateStamp,
@@ -170,7 +170,27 @@ function addToDispatch(item) {
170
170
  added = true;
171
171
  return dispatch;
172
172
  });
173
- if (added) log('info', `Queued dispatch: ${item.id} (${item.type} → ${item.agent})`);
173
+ if (added) {
174
+ log('info', `Queued dispatch: ${item.id} (${item.type} → ${item.agent})`);
175
+ // W-mph8xt88000ke0fc — Stamp the dispatch cooldown ONLY when the item
176
+ // actually survives the queue-time dedup gauntlet. Discover-side blocks
177
+ // (discoverFromPrs build-failure / review-feedback / merge-conflict,
178
+ // discoverFromWorkItems, discoverCentralWorkItems) used to call
179
+ // setCooldown(key) inline the instant a candidate item was built,
180
+ // BEFORE addToDispatch ran the prDedupeKey / workItem-id / dispatchKey
181
+ // dedup checks. The losing candidate left an orphan cooldown that
182
+ // blocked re-dispatch of that cause for `cooldownMinutes || 30` minutes
183
+ // even though no agent ever ran (live repro:
184
+ // ado:office/iss/constellation#5227109 on 2026-05-22 — build-fix and
185
+ // human-fix landed within 38 ms; only the human-fix ran but the orphan
186
+ // build-fix cooldown blocked the next 24 min of retries).
187
+ //
188
+ // Callers opt in by setting `item.meta.cooldownKey`. When absent (e.g.
189
+ // lifecycle.js auto-redispatches that intentionally bypass the cooldown
190
+ // ladder), no stamp fires — same as the pre-fix behavior for those
191
+ // paths.
192
+ if (item.meta?.cooldownKey) setCooldown(item.meta.cooldownKey);
193
+ }
174
194
  return item.id;
175
195
  }
176
196
 
package/engine.js CHANGED
@@ -4315,7 +4315,7 @@ async function discoverFromPrs(config, project) {
4315
4315
  const item = buildPrDispatch(agentId, config, project, pr, 'review', {
4316
4316
  pr_id: pr.id, pr_number: prNumber, pr_title: pr.title || '', pr_branch: prBranch,
4317
4317
  pr_author: pr.agent || '', pr_url: pr.url || '',
4318
- }, `Review ${pr.id}: ${pr.title}`, { dispatchKey: key, source: 'pr', pr, branch: prBranch, project: projMeta });
4318
+ }, `Review ${pr.id}: ${pr.title}`, { dispatchKey: key, cooldownKey: key, source: 'pr', pr, branch: prBranch, project: projMeta });
4319
4319
  if (item) { newWork.push(item); fixDispatched = true; }
4320
4320
  }
4321
4321
 
@@ -4424,7 +4424,7 @@ async function discoverFromPrs(config, project) {
4424
4424
  pr_id: pr.id, pr_number: prNumber, pr_title: pr.title || '', pr_branch: prBranch,
4425
4425
  reviewer: 'Human Reviewer',
4426
4426
  review_note: reviewNote,
4427
- }, `Fix ${pr.id}: ${pr.title || ''} — human feedback`, { dispatchKey: key, automationCauseKey: humanCauseKey, source: 'pr-human-feedback', pr, branch: prBranch, project: projMeta });
4427
+ }, `Fix ${pr.id}: ${pr.title || ''} — human feedback`, { dispatchKey: key, cooldownKey: key, automationCauseKey: humanCauseKey, source: 'pr-human-feedback', pr, branch: prBranch, project: projMeta });
4428
4428
  if (item) { newWork.push(item); fixDispatched = true; }
4429
4429
  } // end if (!skipHumanFeedback) — cause-local guard for #2632
4430
4430
  }
@@ -4478,7 +4478,7 @@ async function discoverFromPrs(config, project) {
4478
4478
  pr_id: pr.id, pr_number: prNumber, pr_title: pr.title || '', pr_branch: prBranch,
4479
4479
  pr_author: pr.agent || '', pr_url: pr.url || '',
4480
4480
  }, `Review ${pr.id}: ${pr.title}`, {
4481
- dispatchKey: key, source: 'pr', pr, branch: prBranch, project: projMeta,
4481
+ dispatchKey: key, cooldownKey: key, source: 'pr', pr, branch: prBranch, project: projMeta,
4482
4482
  deferReviewerResolution: true,
4483
4483
  });
4484
4484
  if (deferred) {
@@ -4492,7 +4492,7 @@ async function discoverFromPrs(config, project) {
4492
4492
  const item = buildPrDispatch(agentId, config, project, pr, 'review', {
4493
4493
  pr_id: pr.id, pr_number: prNumber, pr_title: pr.title || '', pr_branch: prBranch,
4494
4494
  pr_author: pr.agent || '', pr_url: pr.url || '',
4495
- }, `Review ${pr.id}: ${pr.title}`, { dispatchKey: key, source: 'pr', pr, branch: prBranch, project: projMeta });
4495
+ }, `Review ${pr.id}: ${pr.title}`, { dispatchKey: key, cooldownKey: key, source: 'pr', pr, branch: prBranch, project: projMeta });
4496
4496
  if (item) { newWork.push(item); }
4497
4497
  }
4498
4498
 
@@ -4513,7 +4513,7 @@ async function discoverFromPrs(config, project) {
4513
4513
  pr_id: pr.id, pr_branch: prBranch,
4514
4514
  review_note: pr.minionsReview?.note || pr.reviewNote || 'See PR thread comments',
4515
4515
  }, `Fix ${pr.id}: ${pr.title || ''} — review feedback`, {
4516
- dispatchKey: key, automationCauseKey: reviewCauseKey, source: 'pr', pr, branch: prBranch, project: projMeta,
4516
+ dispatchKey: key, cooldownKey: key, automationCauseKey: reviewCauseKey, source: 'pr', pr, branch: prBranch, project: projMeta,
4517
4517
  // W-mpg58wv3 — closure-loop binding. Carries the originating minion review
4518
4518
  // WI id (and any ADO thread ids it cited) onto the fix WI so the
4519
4519
  // post-completion path in lifecycle.js can auto-dispatch a re-review
@@ -4523,7 +4523,7 @@ async function discoverFromPrs(config, project) {
4523
4523
  addresses_threads: Array.isArray(pr.minionsReview?.threads) ? pr.minionsReview.threads.slice() : [],
4524
4524
  });
4525
4525
  if (item) {
4526
- newWork.push(item); setCooldown(key); fixDispatched = true;
4526
+ newWork.push(item); fixDispatched = true;
4527
4527
  }
4528
4528
  }
4529
4529
 
@@ -4635,9 +4635,9 @@ async function discoverFromPrs(config, project) {
4635
4635
  const item = buildPrDispatch(agentId, config, project, pr, 'fix', {
4636
4636
  pr_id: pr.id, pr_branch: prBranch,
4637
4637
  review_note: reviewNote,
4638
- }, `Fix build failure on ${pr.id}: ${pr.title || ''}`, { dispatchKey: key, automationCauseKey: buildCauseKey, source: 'pr', pr, branch: prBranch, project: projMeta });
4638
+ }, `Fix build failure on ${pr.id}: ${pr.title || ''}`, { dispatchKey: key, cooldownKey: key, automationCauseKey: buildCauseKey, source: 'pr', pr, branch: prBranch, project: projMeta });
4639
4639
  if (item) {
4640
- newWork.push(item); setCooldown(key); fixDispatched = true;
4640
+ newWork.push(item); fixDispatched = true;
4641
4641
  try {
4642
4642
  const prPath = projectPrPath(project);
4643
4643
  mutatePullRequests(prPath, prs => {
@@ -4707,10 +4707,9 @@ async function discoverFromPrs(config, project) {
4707
4707
  const item = buildPrDispatch(agentId, config, project, pr, 'fix', {
4708
4708
  pr_id: pr.id, pr_branch: prBranch,
4709
4709
  review_note: `This PR has merge conflicts with the target branch. Inspect the live PR and repository history, choose the safest merge/rebase/update strategy, resolve all conflicts, validate the result, and push the branch.`,
4710
- }, `Fix merge conflicts on ${pr.id}: ${pr.title || ''}`, { dispatchKey: key, automationCauseKey: conflictCauseKey, source: 'pr', pr, branch: prBranch, project: projMeta });
4710
+ }, `Fix merge conflicts on ${pr.id}: ${pr.title || ''}`, { dispatchKey: key, cooldownKey: key, automationCauseKey: conflictCauseKey, source: 'pr', pr, branch: prBranch, project: projMeta });
4711
4711
  if (item) {
4712
4712
  newWork.push(item);
4713
- setCooldown(key);
4714
4713
  // Record dispatch timestamp so re-dispatch is suppressed during ADO lag window
4715
4714
  try {
4716
4715
  mutatePullRequests(projectPrPath(project), prs => {
@@ -5123,10 +5122,9 @@ function discoverFromWorkItems(config, project) {
5123
5122
  agentRole: config.agents[agentId]?.role || tempAgents.get(agentId)?.role || 'Agent',
5124
5123
  task: `[${project?.name || 'project'}] ${item.title || item.description?.slice(0, 80) || item.id}`,
5125
5124
  prompt,
5126
- meta: { dispatchKey: key, source: 'work-item', branch: branchName, branchStrategy: item.branchStrategy || 'parallel', useExistingBranch: !!(isPrTargeted || (item.branchStrategy === 'shared-branch' && item.featureBranch)), item: promptItem, project: { name: project?.name, localPath: project?.localPath }, deferAgentResolution: deferredAgentResolution, ...(linkedPr ? { pr: linkedPr } : {}) }
5125
+ meta: { dispatchKey: key, cooldownKey: key, source: 'work-item', branch: branchName, branchStrategy: item.branchStrategy || 'parallel', useExistingBranch: !!(isPrTargeted || (item.branchStrategy === 'shared-branch' && item.featureBranch)), item: promptItem, project: { name: project?.name, localPath: project?.localPath }, deferAgentResolution: deferredAgentResolution, ...(linkedPr ? { pr: linkedPr } : {}) }
5127
5126
  });
5128
5127
 
5129
- setCooldown(key);
5130
5128
  } catch (err) { log('warn', `discoverFromWorkItems: skipping ${item.id}: ${err.message}`); }
5131
5129
  }
5132
5130
 
@@ -5567,7 +5565,7 @@ function discoverCentralWorkItems(config) {
5567
5565
  task: `[fan-out] ${item.title} → ${agent.name}${assignedProject ? ' → ' + assignedProject.name : ''}`,
5568
5566
  prompt,
5569
5567
  meta: {
5570
- dispatchKey: fanKey, source: 'central-work-item-fanout', item, parentKey: key,
5568
+ dispatchKey: fanKey, cooldownKey: key, source: 'central-work-item-fanout', item, parentKey: key,
5571
5569
  branch: fanBranch,
5572
5570
  deadline: item.timeout ? Date.now() + item.timeout : Date.now() + (config.engine?.fanOutTimeout || config.engine?.agentTimeout || ENGINE_DEFAULTS.agentTimeout)
5573
5571
  }
@@ -5579,7 +5577,6 @@ function discoverCentralWorkItems(config) {
5579
5577
  scope: 'fan-out',
5580
5578
  fanOutAgents: idleAgents.map(a => a.id),
5581
5579
  });
5582
- setCooldown(key);
5583
5580
  log('info', `Fan-out: ${item.id} queued for ${idleAgents.length} agents: ${idleAgents.map(a => a.name).join(', ')}`);
5584
5581
 
5585
5582
  } else {
@@ -5793,10 +5790,8 @@ function discoverCentralWorkItems(config) {
5793
5790
  agentRole,
5794
5791
  task: item.title || item.description?.slice(0, 80) || item.id,
5795
5792
  prompt,
5796
- meta: { dispatchKey: key, source: 'central-work-item', item: { ...item, ...mutations.get(item.id) }, planFileName: item.planFile || mutations.get(item.id)?._planFileName || null, branch: centralBranch, ...(targetProject ? { project: { name: targetProject.name, localPath: targetProject.localPath } } : {}) }
5793
+ meta: { dispatchKey: key, cooldownKey: key, source: 'central-work-item', item: { ...item, ...mutations.get(item.id) }, planFileName: item.planFile || mutations.get(item.id)?._planFileName || null, branch: centralBranch, ...(targetProject ? { project: { name: targetProject.name, localPath: targetProject.localPath } } : {}) }
5797
5794
  });
5798
-
5799
- setCooldown(key);
5800
5795
  }
5801
5796
  } catch (err) { log('warn', `discoverCentralWorkItems: skipping ${item.id}: ${err.message}`); }
5802
5797
  }
@@ -6004,7 +5999,10 @@ async function discoverWork(config) {
6004
5999
 
6005
6000
  for (const item of allWork) {
6006
6001
  await addToDispatchWithValidation(item, { config });
6007
- if (item.meta?.dispatchKey) setCooldown(item.meta.dispatchKey);
6002
+ // W-mph8xt88000ke0fc — Cooldowns are stamped by addToDispatch ONLY on
6003
+ // successful append (post-dedup). Stamping unconditionally here used to
6004
+ // leave orphan cooldowns for items collapsed by prDedupeKey / workItem-id
6005
+ // / dispatchKey dedup or routed to the pre-dispatch review queue.
6008
6006
  if (item.meta?.source === 'pr-human-feedback') {
6009
6007
  clearPendingHumanFeedbackFlag(item.meta.project, item.meta.pr?.id);
6010
6008
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.2029",
3
+ "version": "0.1.2030",
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"