@yemi33/minions 0.1.370 → 0.1.371

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,8 +1,9 @@
1
1
  # Changelog
2
2
 
3
- ## 0.1.370 (2026-04-06)
3
+ ## 0.1.371 (2026-04-06)
4
4
 
5
5
  ### Features
6
+ - Fix review re-dispatch infinite loop (#226)
6
7
  - Add subagent guidance and health check preamble to playbooks (#225)
7
8
  - Fix 2 failing source-pattern test assertions (#224)
8
9
 
package/engine/github.js CHANGED
@@ -141,6 +141,13 @@ async function pollPrStatus(config) {
141
141
  else if (prData.state === 'closed') newStatus = PR_STATUS.ABANDONED;
142
142
  else if (prData.state === 'open') newStatus = PR_STATUS.ACTIVE;
143
143
 
144
+ // Track head SHA changes to detect new pushes (used for review re-dispatch gating)
145
+ if (prData.head?.sha && pr.headSha !== prData.head.sha) {
146
+ pr.headSha = prData.head.sha;
147
+ pr.lastPushedAt = new Date().toISOString();
148
+ updated = true;
149
+ }
150
+
144
151
  if (pr.status !== newStatus) {
145
152
  log('info', `PR ${pr.id} status: ${pr.status} → ${newStatus}`);
146
153
  pr.status = newStatus;
@@ -191,6 +198,9 @@ async function pollPrStatus(config) {
191
198
  if (states.some(s => s === 'CHANGES_REQUESTED')) newReviewStatus = 'changes-requested';
192
199
  else if (states.some(s => s === 'APPROVED')) newReviewStatus = 'approved';
193
200
  else if (states.length > 0) newReviewStatus = 'pending';
201
+ // If all reviews were COMMENTED (filtered out), states is empty but reviews exist.
202
+ // Set to 'waiting' instead of leaving as 'pending' to prevent infinite review re-dispatch.
203
+ else if (states.length === 0 && reviews.length > 0 && newReviewStatus === 'pending') newReviewStatus = 'waiting';
194
204
 
195
205
  if (pr.reviewStatus !== newReviewStatus) {
196
206
  log('info', `PR ${pr.id} reviewStatus: ${pr.reviewStatus} → ${newReviewStatus}`);
@@ -727,6 +727,9 @@ function updatePrAfterReview(agentId, pr, project) {
727
727
 
728
728
  // Set reviewStatus to 'waiting' (single source of truth — synced from ADO/GitHub votes on next poll)
729
729
  target.reviewStatus = 'waiting';
730
+ // Track when this PR was last reviewed — used by engine.js to prevent re-dispatch
731
+ // until new commits are pushed (lastPushedAt > lastReviewedAt)
732
+ target.lastReviewedAt = ts();
730
733
  target.minionsReview = {
731
734
  reviewer: reviewerName,
732
735
  reviewedAt: ts(),
package/engine.js CHANGED
@@ -1268,9 +1268,11 @@ function discoverFromPrs(config, project) {
1268
1268
  // minionsReview tracks metadata (reviewer, note) but not the authoritative status
1269
1269
  const reviewStatus = pr.reviewStatus || 'pending';
1270
1270
 
1271
- // PRs needing review: pending or waiting (review dispatched but no verdict yet)
1271
+ // PRs needing review: pending review status and not already reviewed without new commits
1272
1272
  const autoReview = config.engine?.autoReview !== false;
1273
- const needsReview = autoReview && reviewStatus === 'pending';
1273
+ // Skip re-dispatch if already reviewed and no new commits pushed since last review
1274
+ const alreadyReviewed = pr.lastReviewedAt && (!pr.lastPushedAt || pr.lastPushedAt <= pr.lastReviewedAt);
1275
+ const needsReview = autoReview && reviewStatus === 'pending' && !alreadyReviewed;
1274
1276
  if (needsReview) {
1275
1277
  const key = `review-${project?.name || 'default'}-${pr.id}`;
1276
1278
  if (isAlreadyDispatched(key) || isOnCooldown(key, cooldownMs)) continue;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.370",
3
+ "version": "0.1.371",
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"