@yemi33/minions 0.1.1729 → 0.1.1730
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 +6 -3
- package/engine/copilot-models.json +1 -1
- package/engine/lifecycle.js +168 -10
- package/engine/shared.js +62 -0
- package/engine.js +14 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 0.1.
|
|
3
|
+
## 0.1.1730 (2026-05-05)
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
- detect no-op PR fixes (#2077)
|
|
7
|
+
|
|
8
|
+
## 0.1.1728 (2026-05-05)
|
|
4
9
|
|
|
5
10
|
### Features
|
|
6
|
-
- suppress comment-only fix loops (#2076)
|
|
7
|
-
- Allow explicit doc chat dispatch requests (#2072)
|
|
8
11
|
- redact repository URLs in filed issues (#2069)
|
|
9
12
|
|
|
10
13
|
## 0.1.1727 (2026-05-05)
|
package/engine/lifecycle.js
CHANGED
|
@@ -1466,16 +1466,153 @@ function fixCompletionChangedBranch(structuredCompletion) {
|
|
|
1466
1466
|
return true;
|
|
1467
1467
|
}
|
|
1468
1468
|
|
|
1469
|
-
function
|
|
1469
|
+
function normalizePrFixBranchName(branch) {
|
|
1470
|
+
return String(branch || '').trim().replace(/^refs\/heads\//, '');
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
function getPrFixBaselineHead(pr) {
|
|
1474
|
+
return String(pr?.headSha || pr?._adoSourceCommit || pr?._adoHeadCommit || '').trim();
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
function findPrFixWorktree(meta, project, config) {
|
|
1478
|
+
const branch = normalizePrFixBranchName(meta?.branch || meta?.pr?.branch);
|
|
1479
|
+
const rootDir = project?.localPath ? path.resolve(project.localPath) : null;
|
|
1480
|
+
if (!branch || !rootDir) return null;
|
|
1481
|
+
const worktreeRoot = path.resolve(rootDir, config?.engine?.worktreeRoot || ENGINE_DEFAULTS.worktreeRoot);
|
|
1482
|
+
try {
|
|
1483
|
+
if (!fs.existsSync(worktreeRoot)) return null;
|
|
1484
|
+
for (const dir of fs.readdirSync(worktreeRoot)) {
|
|
1485
|
+
const wtPath = path.join(worktreeRoot, dir);
|
|
1486
|
+
if (!fs.statSync(wtPath).isDirectory()) continue;
|
|
1487
|
+
if (worktreeMatchesBranch(dir.toLowerCase(), branch, getWorktreeBranch(wtPath))) return wtPath;
|
|
1488
|
+
}
|
|
1489
|
+
} catch (err) {
|
|
1490
|
+
log('warn', `PR fix no-op worktree lookup for ${meta?.pr?.id || branch}: ${err.message}`);
|
|
1491
|
+
}
|
|
1492
|
+
return null;
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
async function gitRevParse(cwd, ref) {
|
|
1496
|
+
try {
|
|
1497
|
+
const out = await runFileCapture('git', ['rev-parse', '--verify', `${ref}^{commit}`], { cwd, timeout: 10000 });
|
|
1498
|
+
return String(out || '').trim();
|
|
1499
|
+
} catch {
|
|
1500
|
+
return '';
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
async function detectPrFixBranchChange(meta, config) {
|
|
1505
|
+
const branch = normalizePrFixBranchName(meta?.branch || meta?.pr?.branch);
|
|
1506
|
+
const project = resolvePrFallbackProject(meta, config) || meta?.project || null;
|
|
1507
|
+
const rootDir = project?.localPath ? path.resolve(project.localPath) : null;
|
|
1508
|
+
const beforeHead = getPrFixBaselineHead(meta?.pr);
|
|
1509
|
+
if (!meta?.pr?.id || !branch || !rootDir || !beforeHead) {
|
|
1510
|
+
return { changed: null, reason: 'missing PR branch baseline' };
|
|
1511
|
+
}
|
|
1512
|
+
|
|
1513
|
+
const worktreePath = findPrFixWorktree(meta, project, config);
|
|
1514
|
+
if (worktreePath) {
|
|
1515
|
+
try {
|
|
1516
|
+
const dirty = await runFileCapture('git', ['status', '--porcelain'], { cwd: worktreePath, timeout: 10000 });
|
|
1517
|
+
if (String(dirty || '').trim()) {
|
|
1518
|
+
return { changed: true, beforeHead, afterHead: beforeHead, evidence: 'worktree-diff' };
|
|
1519
|
+
}
|
|
1520
|
+
} catch (err) {
|
|
1521
|
+
log('warn', `PR fix no-op dirty check for ${meta.pr.id}: ${err.message}`);
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
let fetched = false;
|
|
1526
|
+
try {
|
|
1527
|
+
await runFileCapture('git', ['fetch', 'origin', branch], { cwd: rootDir, timeout: 30000 });
|
|
1528
|
+
fetched = true;
|
|
1529
|
+
} catch (err) {
|
|
1530
|
+
log('warn', `PR fix no-op fetch for ${meta.pr.id} (${branch}) failed: ${err.message}`);
|
|
1531
|
+
}
|
|
1470
1532
|
|
|
1533
|
+
const remoteHead = await gitRevParse(rootDir, `refs/remotes/origin/${branch}`);
|
|
1534
|
+
if (remoteHead && remoteHead !== beforeHead) {
|
|
1535
|
+
return { changed: true, beforeHead, afterHead: remoteHead, evidence: 'remote-head' };
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1538
|
+
if (worktreePath) {
|
|
1539
|
+
const localHead = await gitRevParse(worktreePath, 'HEAD');
|
|
1540
|
+
if (localHead && localHead !== beforeHead) {
|
|
1541
|
+
return { changed: true, beforeHead, afterHead: localHead, evidence: 'local-head' };
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
if (fetched && remoteHead && remoteHead === beforeHead) {
|
|
1546
|
+
return { changed: false, beforeHead, afterHead: remoteHead, evidence: 'remote-head' };
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1549
|
+
return { changed: null, beforeHead, afterHead: remoteHead || '', reason: 'unable to prove branch head after fix' };
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1552
|
+
function recordPrNoOpFixAttempt(target, cause, source, dispatchItem, branchChange, config) {
|
|
1553
|
+
const evidenceFingerprint = shared.prFixEvidenceFingerprint(target, cause);
|
|
1554
|
+
const prior = shared.getPrNoOpFixRecord(target, cause);
|
|
1555
|
+
const sameEvidence = prior?.evidenceFingerprint === evidenceFingerprint;
|
|
1556
|
+
const count = sameEvidence ? (Number(prior.count) || 0) + 1 : 1;
|
|
1557
|
+
const pauseAfter = Number(config?.engine?.prNoOpFixPauseAttempts) || ENGINE_DEFAULTS.prNoOpFixPauseAttempts;
|
|
1558
|
+
const paused = count >= pauseAfter;
|
|
1559
|
+
const now = ts();
|
|
1560
|
+
target._noOpFixes = target._noOpFixes && typeof target._noOpFixes === 'object' ? target._noOpFixes : {};
|
|
1561
|
+
target._noOpFixes[cause] = {
|
|
1562
|
+
count,
|
|
1563
|
+
paused,
|
|
1564
|
+
evidenceFingerprint,
|
|
1565
|
+
firstAt: sameEvidence ? (prior.firstAt || now) : now,
|
|
1566
|
+
lastAt: now,
|
|
1567
|
+
dispatchId: dispatchItem?.id || null,
|
|
1568
|
+
dispatchKey: dispatchItem?.meta?.dispatchKey || null,
|
|
1569
|
+
source: source || null,
|
|
1570
|
+
beforeHead: branchChange?.beforeHead || '',
|
|
1571
|
+
afterHead: branchChange?.afterHead || '',
|
|
1572
|
+
reason: branchChange?.reason || 'fix completed without changing the PR branch',
|
|
1573
|
+
};
|
|
1574
|
+
target._lastNoOpFix = {
|
|
1575
|
+
cause,
|
|
1576
|
+
at: now,
|
|
1577
|
+
paused,
|
|
1578
|
+
dispatchId: dispatchItem?.id || null,
|
|
1579
|
+
beforeHead: branchChange?.beforeHead || '',
|
|
1580
|
+
afterHead: branchChange?.afterHead || '',
|
|
1581
|
+
};
|
|
1582
|
+
|
|
1583
|
+
if (cause === shared.PR_FIX_CAUSE.HUMAN_FEEDBACK && target.humanFeedback) {
|
|
1584
|
+
target.humanFeedback.pendingFix = !paused;
|
|
1585
|
+
if (paused) target.humanFeedback.noOpPaused = true;
|
|
1586
|
+
else delete target.humanFeedback.noOpPaused;
|
|
1587
|
+
}
|
|
1588
|
+
if (cause === shared.PR_FIX_CAUSE.BUILD_FAILURE) delete target._buildFixPushedAt;
|
|
1589
|
+
if (cause === shared.PR_FIX_CAUSE.MERGE_CONFLICT) delete target._conflictFixedAt;
|
|
1590
|
+
return target._noOpFixes[cause];
|
|
1591
|
+
}
|
|
1592
|
+
|
|
1593
|
+
function clearPrNoOpFixAttempt(target, cause) {
|
|
1594
|
+
if (!target?._noOpFixes || !target._noOpFixes[cause]) return;
|
|
1595
|
+
delete target._noOpFixes[cause];
|
|
1596
|
+
if (Object.keys(target._noOpFixes).length === 0) delete target._noOpFixes;
|
|
1597
|
+
if (target._lastNoOpFix?.cause === cause) delete target._lastNoOpFix;
|
|
1598
|
+
if (target.humanFeedback) delete target.humanFeedback.noOpPaused;
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1601
|
+
function updatePrAfterFix(pr, project, source, options = {}, legacyDispatchId = '') {
|
|
1471
1602
|
if (!pr?.id) return;
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
const
|
|
1476
|
-
const automationCauseKey = options.automationCauseKey || '';
|
|
1477
|
-
const fixDispatchId = options.dispatchId || dispatchId || '';
|
|
1603
|
+
if (!options || typeof options !== 'object' || Array.isArray(options)) {
|
|
1604
|
+
options = { automationCauseKey: options, dispatchId: legacyDispatchId };
|
|
1605
|
+
}
|
|
1606
|
+
const explicitlyChangedBranch = options.branchChanged !== false;
|
|
1478
1607
|
const prPath = project ? shared.projectPrPath(project) : path.join(path.resolve(MINIONS_DIR, '..'), '.minions', 'pull-requests.json');
|
|
1608
|
+
const automationCauseKey = options.automationCauseKey || options.dispatchItem?.meta?.automationCauseKey || '';
|
|
1609
|
+
const fixDispatchId = options.dispatchItem?.id || options.dispatchId || legacyDispatchId || '';
|
|
1610
|
+
const cause = shared.getPrFixAutomationCause({
|
|
1611
|
+
dispatchKey: options.dispatchItem?.meta?.dispatchKey,
|
|
1612
|
+
source,
|
|
1613
|
+
task: options.dispatchItem?.task,
|
|
1614
|
+
});
|
|
1615
|
+
let result = null;
|
|
1479
1616
|
shared.mutateJsonFileLocked(prPath, (prs) => {
|
|
1480
1617
|
if (!Array.isArray(prs)) return prs;
|
|
1481
1618
|
const target = shared.findPrRecord(prs, pr, project);
|
|
@@ -1485,10 +1622,17 @@ function updatePrAfterFix(pr, project, source, opts = {}, dispatchId = '') {
|
|
|
1485
1622
|
delete next.fixedAt;
|
|
1486
1623
|
target.minionsReview = next;
|
|
1487
1624
|
};
|
|
1625
|
+
if (explicitlyChangedBranch && options.branchChange?.changed === false) {
|
|
1626
|
+
const record = recordPrNoOpFixAttempt(target, cause, source, options.dispatchItem, options.branchChange, options.config);
|
|
1627
|
+
result = { noOp: true, cause, paused: !!record.paused, count: record.count };
|
|
1628
|
+
log('warn', `Updated ${pr.id} → recorded no-op ${cause} fix attempt ${record.count}${record.paused ? ' (paused)' : ''}; PR branch was unchanged`);
|
|
1629
|
+
return prs;
|
|
1630
|
+
}
|
|
1631
|
+
clearPrNoOpFixAttempt(target, cause);
|
|
1488
1632
|
if (source === 'pr-human-feedback') {
|
|
1489
1633
|
const clearPendingFix = shouldClearHumanFeedbackPendingFix(target, pr, automationCauseKey);
|
|
1490
1634
|
if (target.humanFeedback && clearPendingFix) target.humanFeedback.pendingFix = false;
|
|
1491
|
-
if (
|
|
1635
|
+
if (explicitlyChangedBranch) {
|
|
1492
1636
|
// Never downgrade from approved — fix was dispatched but PR is already approved
|
|
1493
1637
|
if (target.reviewStatus !== 'approved') target.reviewStatus = 'waiting';
|
|
1494
1638
|
target.minionsReview = { ...target.minionsReview, note: 'Fixed human feedback, awaiting re-review', fixedAt: ts() };
|
|
@@ -1507,7 +1651,7 @@ function updatePrAfterFix(pr, project, source, opts = {}, dispatchId = '') {
|
|
|
1507
1651
|
}
|
|
1508
1652
|
} else {
|
|
1509
1653
|
if (target.reviewStatus !== 'approved') target.reviewStatus = 'waiting';
|
|
1510
|
-
if (
|
|
1654
|
+
if (explicitlyChangedBranch) {
|
|
1511
1655
|
target.minionsReview = { ...target.minionsReview, note: 'Fixed, awaiting re-review', fixedAt: ts() };
|
|
1512
1656
|
log('info', `Updated ${pr.id} → reviewStatus: waiting (fix pushed)`);
|
|
1513
1657
|
} else {
|
|
@@ -1523,8 +1667,10 @@ function updatePrAfterFix(pr, project, source, opts = {}, dispatchId = '') {
|
|
|
1523
1667
|
handledAt: ts(),
|
|
1524
1668
|
});
|
|
1525
1669
|
}
|
|
1670
|
+
result = { noOp: false, cause };
|
|
1526
1671
|
return prs;
|
|
1527
1672
|
}, { defaultValue: [] });
|
|
1673
|
+
return result;
|
|
1528
1674
|
}
|
|
1529
1675
|
|
|
1530
1676
|
// ─── Post-Merge Rebase ──────────────────────────────────────────────────────
|
|
@@ -2754,6 +2900,16 @@ async function runPostCompletionHooks(dispatchItem, agentId, code, stdout, confi
|
|
|
2754
2900
|
|
|
2755
2901
|
// Archive is manual — user archives plans from the dashboard when ready
|
|
2756
2902
|
|
|
2903
|
+
let prFixBranchChange = null;
|
|
2904
|
+
if (type === WORK_TYPE.FIX && effectiveSuccess && meta?.pr?.id) {
|
|
2905
|
+
try {
|
|
2906
|
+
prFixBranchChange = await detectPrFixBranchChange(meta, config);
|
|
2907
|
+
} catch (err) {
|
|
2908
|
+
log('warn', `PR fix no-op detection for ${meta.pr.id}: ${err.message}`);
|
|
2909
|
+
prFixBranchChange = { changed: null, reason: err.message };
|
|
2910
|
+
}
|
|
2911
|
+
}
|
|
2912
|
+
|
|
2757
2913
|
// Scheduled task back-reference: update schedule-runs.json and write linked inbox note
|
|
2758
2914
|
if (meta?.item?._scheduleId) {
|
|
2759
2915
|
try {
|
|
@@ -2839,7 +2995,9 @@ async function runPostCompletionHooks(dispatchItem, agentId, code, stdout, confi
|
|
|
2839
2995
|
updatePrAfterFix(meta?.pr, meta?.project, meta?.source, {
|
|
2840
2996
|
branchChanged: fixCompletionChangedBranch(structuredCompletion),
|
|
2841
2997
|
automationCauseKey: meta?.automationCauseKey,
|
|
2842
|
-
|
|
2998
|
+
dispatchItem,
|
|
2999
|
+
branchChange: prFixBranchChange,
|
|
3000
|
+
config,
|
|
2843
3001
|
});
|
|
2844
3002
|
// (#984) Sync PRD status for PR-linked features: fix work items have a different ID
|
|
2845
3003
|
// than the original PRD feature, so syncPrdItemStatus(fixWiId, ...) finds nothing.
|
package/engine/shared.js
CHANGED
|
@@ -834,6 +834,7 @@ const ENGINE_DEFAULTS = {
|
|
|
834
834
|
autoReReviewPrs: true, // auto-dispatch review agents after a PR fix is pushed
|
|
835
835
|
autoFixReviewFeedback: true, // auto-dispatch fix agents for minions review changes-requested verdicts
|
|
836
836
|
autoFixHumanComments: true, // auto-dispatch fix agents for actionable human PR comments
|
|
837
|
+
prNoOpFixPauseAttempts: 2, // pause one PR automation cause after repeated no-op fixes for unchanged evidence
|
|
837
838
|
completionReportRetentionDays: 90, // retain completion report sidecars beyond capped dispatch history
|
|
838
839
|
completionReportMaxFiles: 5000, // hard cap for completion report sidecars during cleanup
|
|
839
840
|
meetingRoundTimeout: 900000, // 15min per meeting round before auto-advance
|
|
@@ -2492,6 +2493,62 @@ const _WIN_RESERVED_NAMES = new Set([
|
|
|
2492
2493
|
'LPT1', 'LPT2', 'LPT3', 'LPT4', 'LPT5', 'LPT6', 'LPT7', 'LPT8', 'LPT9',
|
|
2493
2494
|
]);
|
|
2494
2495
|
|
|
2496
|
+
const PR_FIX_CAUSE = {
|
|
2497
|
+
HUMAN_FEEDBACK: 'human-feedback',
|
|
2498
|
+
REVIEW_FEEDBACK: 'review-feedback',
|
|
2499
|
+
BUILD_FAILURE: 'build-failure',
|
|
2500
|
+
MERGE_CONFLICT: 'merge-conflict',
|
|
2501
|
+
UNKNOWN: 'pr-fix',
|
|
2502
|
+
};
|
|
2503
|
+
|
|
2504
|
+
function getPrFixAutomationCause({ dispatchKey = '', source = '', task = '' } = {}) {
|
|
2505
|
+
const key = String(dispatchKey || '').toLowerCase();
|
|
2506
|
+
const src = String(source || '').toLowerCase();
|
|
2507
|
+
const title = String(task || '').toLowerCase();
|
|
2508
|
+
if (src === 'pr-human-feedback' || key.startsWith('human-fix-') || title.includes('human feedback')) return PR_FIX_CAUSE.HUMAN_FEEDBACK;
|
|
2509
|
+
if (key.startsWith('build-fix-') || title.includes('build failure')) return PR_FIX_CAUSE.BUILD_FAILURE;
|
|
2510
|
+
if (key.startsWith('conflict-fix-') || title.includes('merge conflict')) return PR_FIX_CAUSE.MERGE_CONFLICT;
|
|
2511
|
+
if (key.startsWith('fix-') || src === 'pr') return PR_FIX_CAUSE.REVIEW_FEEDBACK;
|
|
2512
|
+
return PR_FIX_CAUSE.UNKNOWN;
|
|
2513
|
+
}
|
|
2514
|
+
|
|
2515
|
+
function prFixEvidenceFingerprint(pr, cause = PR_FIX_CAUSE.UNKNOWN) {
|
|
2516
|
+
const review = pr?.minionsReview || {};
|
|
2517
|
+
const feedback = pr?.humanFeedback || {};
|
|
2518
|
+
const evidence = { cause };
|
|
2519
|
+
if (cause === PR_FIX_CAUSE.HUMAN_FEEDBACK) {
|
|
2520
|
+
evidence.lastProcessedCommentDate = feedback.lastProcessedCommentDate || '';
|
|
2521
|
+
evidence.feedbackContent = feedback.feedbackContent || '';
|
|
2522
|
+
} else if (cause === PR_FIX_CAUSE.BUILD_FAILURE) {
|
|
2523
|
+
evidence.buildStatus = pr?.buildStatus || '';
|
|
2524
|
+
evidence.buildFailReason = pr?.buildFailReason || '';
|
|
2525
|
+
evidence.buildErrorLog = pr?.buildErrorLog || '';
|
|
2526
|
+
evidence.buildStatusDetail = pr?._buildStatusDetail || '';
|
|
2527
|
+
} else if (cause === PR_FIX_CAUSE.MERGE_CONFLICT) {
|
|
2528
|
+
evidence.mergeConflict = !!pr?._mergeConflict;
|
|
2529
|
+
evidence.mergeStatus = pr?.mergeStatus || '';
|
|
2530
|
+
evidence.mergeConflictDetail = pr?._mergeConflictDetail || '';
|
|
2531
|
+
} else {
|
|
2532
|
+
evidence.reviewStatus = pr?.reviewStatus || '';
|
|
2533
|
+
evidence.lastReviewedAt = pr?.lastReviewedAt || '';
|
|
2534
|
+
evidence.reviewedAt = review.reviewedAt || '';
|
|
2535
|
+
evidence.reviewNote = review.note || pr?.reviewNote || '';
|
|
2536
|
+
}
|
|
2537
|
+
return crypto.createHash('sha1').update(JSON.stringify(evidence)).digest('hex').slice(0, 16);
|
|
2538
|
+
}
|
|
2539
|
+
|
|
2540
|
+
function getPrNoOpFixRecord(pr, cause) {
|
|
2541
|
+
if (!pr || !cause || !pr._noOpFixes || typeof pr._noOpFixes !== 'object') return null;
|
|
2542
|
+
const record = pr._noOpFixes[cause];
|
|
2543
|
+
return record && typeof record === 'object' ? record : null;
|
|
2544
|
+
}
|
|
2545
|
+
|
|
2546
|
+
function isPrNoOpFixCausePaused(pr, cause) {
|
|
2547
|
+
const record = getPrNoOpFixRecord(pr, cause);
|
|
2548
|
+
if (!record?.paused) return false;
|
|
2549
|
+
return record.evidenceFingerprint === prFixEvidenceFingerprint(pr, cause);
|
|
2550
|
+
}
|
|
2551
|
+
|
|
2495
2552
|
/**
|
|
2496
2553
|
* Recursively purge Windows reserved-name pseudo-files (NUL, CON, PRN, AUX, etc.)
|
|
2497
2554
|
* using the \\?\ extended path prefix that bypasses reserved-name interpretation.
|
|
@@ -2787,6 +2844,11 @@ module.exports = {
|
|
|
2787
2844
|
validateProjectName,
|
|
2788
2845
|
validateProjectPath,
|
|
2789
2846
|
validatePid,
|
|
2847
|
+
PR_FIX_CAUSE,
|
|
2848
|
+
getPrFixAutomationCause,
|
|
2849
|
+
prFixEvidenceFingerprint,
|
|
2850
|
+
getPrNoOpFixRecord,
|
|
2851
|
+
isPrNoOpFixCausePaused,
|
|
2790
2852
|
parseSkillFrontmatter,
|
|
2791
2853
|
sleepMs,
|
|
2792
2854
|
killGracefully,
|
package/engine.js
CHANGED
|
@@ -2214,6 +2214,12 @@ function clearPendingHumanFeedbackFlag(projectMeta, prId) {
|
|
|
2214
2214
|
} catch (e) { log('warn', 'clear pending human feedback flag: ' + e.message); }
|
|
2215
2215
|
}
|
|
2216
2216
|
|
|
2217
|
+
function isPrNoOpFixCauseSuppressed(pr, cause) {
|
|
2218
|
+
if (!shared.isPrNoOpFixCausePaused(pr, cause)) return false;
|
|
2219
|
+
log('warn', `PR ${pr.id}: suppressing ${cause} automation after repeated no-op fix attempts; waiting for human resume or new evidence`);
|
|
2220
|
+
return true;
|
|
2221
|
+
}
|
|
2222
|
+
|
|
2217
2223
|
const PR_PENDING_MISSING_BRANCH = shared.PR_PENDING_REASON.MISSING_BRANCH;
|
|
2218
2224
|
|
|
2219
2225
|
function normalizePrBranch(value) {
|
|
@@ -2539,7 +2545,8 @@ async function discoverFromPrs(config, project) {
|
|
|
2539
2545
|
const humanCauseKey = getPrAutomationCauseKey('human-comment', pr);
|
|
2540
2546
|
const humanFixKey = getPrAutomationDispatchKey(humanFixBaseKey, humanCauseKey);
|
|
2541
2547
|
const hasCoalescedFeedback = (dispatchCooldowns.get(humanFixKey)?.pendingContexts || []).length > 0;
|
|
2542
|
-
if (pollEnabled && autoFixHumanComments && (pr.humanFeedback?.pendingFix || hasCoalescedFeedback) && !fixDispatched
|
|
2548
|
+
if (pollEnabled && autoFixHumanComments && (pr.humanFeedback?.pendingFix || hasCoalescedFeedback) && !fixDispatched
|
|
2549
|
+
&& !isPrNoOpFixCauseSuppressed(pr, shared.PR_FIX_CAUSE.HUMAN_FEEDBACK)) {
|
|
2543
2550
|
const key = humanFixKey;
|
|
2544
2551
|
if (isPrAutomationCauseHandledOrPending(project, pr, humanCauseKey)) continue;
|
|
2545
2552
|
let staleCoalesced = [];
|
|
@@ -2636,7 +2643,8 @@ async function discoverFromPrs(config, project) {
|
|
|
2636
2643
|
|
|
2637
2644
|
// PRs with changes requested → route back to author for fix.
|
|
2638
2645
|
// Gate on evalLoopEnabled and provider polling — the review→fix cycle depends on fresh vote state.
|
|
2639
|
-
if (evalLoopEnabled && pollEnabled && autoFixReviewFeedback && reviewStatus === 'changes-requested' && !awaitingReReview && !fixDispatched
|
|
2646
|
+
if (evalLoopEnabled && pollEnabled && autoFixReviewFeedback && reviewStatus === 'changes-requested' && !awaitingReReview && !fixDispatched
|
|
2647
|
+
&& !isPrNoOpFixCauseSuppressed(pr, shared.PR_FIX_CAUSE.REVIEW_FEEDBACK)) {
|
|
2640
2648
|
const reviewCauseKey = getPrAutomationCauseKey('review-feedback', pr);
|
|
2641
2649
|
const key = getPrAutomationDispatchKey(`fix-${project?.name || 'default'}-${prDisplayId}`, reviewCauseKey);
|
|
2642
2650
|
if (isPrAutomationCauseHandledOrPending(project, pr, reviewCauseKey)) continue;
|
|
@@ -2663,7 +2671,8 @@ async function discoverFromPrs(config, project) {
|
|
|
2663
2671
|
if (Date.now() - new Date(pr._buildFixPushedAt).getTime() < gracePeriodMs) continue;
|
|
2664
2672
|
}
|
|
2665
2673
|
const autoFixBuilds = config.engine?.autoFixBuilds ?? ENGINE_DEFAULTS.autoFixBuilds;
|
|
2666
|
-
if (pollEnabled && autoFixBuilds && pr.status === PR_STATUS.ACTIVE && pr.buildStatus === 'failing'
|
|
2674
|
+
if (pollEnabled && autoFixBuilds && pr.status === PR_STATUS.ACTIVE && pr.buildStatus === 'failing'
|
|
2675
|
+
&& !isPrNoOpFixCauseSuppressed(pr, shared.PR_FIX_CAUSE.BUILD_FAILURE)) {
|
|
2667
2676
|
const buildCauseKey = getPrAutomationCauseKey('build', pr);
|
|
2668
2677
|
const key = getPrAutomationDispatchKey(`build-fix-${project?.name || 'default'}-${prDisplayId}`, buildCauseKey);
|
|
2669
2678
|
if (isPrAutomationCauseHandledOrPending(project, pr, buildCauseKey)) continue;
|
|
@@ -2757,7 +2766,8 @@ async function discoverFromPrs(config, project) {
|
|
|
2757
2766
|
|
|
2758
2767
|
// PRs with merge conflicts — dispatch fix to resolve (gated by provider polling + autoFixConflicts)
|
|
2759
2768
|
const autoFixConflicts = config.engine?.autoFixConflicts ?? ENGINE_DEFAULTS.autoFixConflicts;
|
|
2760
|
-
if (pollEnabled && autoFixConflicts && pr.status === PR_STATUS.ACTIVE && pr._mergeConflict && !fixDispatched
|
|
2769
|
+
if (pollEnabled && autoFixConflicts && pr.status === PR_STATUS.ACTIVE && pr._mergeConflict && !fixDispatched
|
|
2770
|
+
&& !isPrNoOpFixCauseSuppressed(pr, shared.PR_FIX_CAUSE.MERGE_CONFLICT)) {
|
|
2761
2771
|
const conflictCauseKey = getPrAutomationCauseKey('merge-conflict', pr);
|
|
2762
2772
|
const key = getPrAutomationDispatchKey(`conflict-fix-${project?.name || 'default'}-${prDisplayId}`, conflictCauseKey);
|
|
2763
2773
|
// Suppress re-dispatch for 10 min after last attempt — ADO/GitHub recomputes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1730",
|
|
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"
|