@yemi33/minions 0.1.1639 → 0.1.1640
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 -2
- package/dashboard/js/render-prs.js +4 -1
- package/dashboard/styles.css +1 -0
- package/dashboard.js +1 -0
- package/engine/ado.js +9 -6
- package/engine/copilot-models.json +1 -1
- package/engine/github.js +10 -0
- package/engine.js +108 -59
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 0.1.
|
|
3
|
+
## 0.1.1640 (2026-04-30)
|
|
4
|
+
|
|
5
|
+
### Fixes
|
|
6
|
+
- surface unresolved pr branches
|
|
7
|
+
|
|
8
|
+
## 0.1.1638 (2026-04-30)
|
|
4
9
|
|
|
5
10
|
### Fixes
|
|
6
|
-
- auto-link agent-created PRs to work items (#1904)
|
|
7
11
|
- Playbook 'fix' / 'review' gates items forever when pr_branch is unresolved (closes #1899) (#1901)
|
|
8
12
|
|
|
9
13
|
## 0.1.1637 (2026-04-30)
|
|
@@ -18,6 +18,9 @@ function prRow(pr) {
|
|
|
18
18
|
const buildLabel = pr.buildFixEscalated ? 'escalated (' + (pr.buildFixAttempts || '?') + ' fixes)' : (pr.buildStatus || 'none') + (pr._buildStatusStale ? ' (stale)' : '');
|
|
19
19
|
const statusClass = pr.status === 'merged' ? 'merged' : pr.status === 'abandoned' ? 'rejected' : pr.status === 'active' ? 'active' : 'draft';
|
|
20
20
|
const statusLabel = pr.status || 'active';
|
|
21
|
+
const branchError = pr._branchResolutionError?.reason || '';
|
|
22
|
+
const branchLabel = pr.branch || (branchError ? 'missing branch' : '—');
|
|
23
|
+
const branchClass = 'pr-branch' + (branchError ? ' branch-missing' : '');
|
|
21
24
|
const url = pr.url || '#';
|
|
22
25
|
const prId = pr.id || '—';
|
|
23
26
|
const pendingReason = pr._pendingReason ? String(pr._pendingReason) : '';
|
|
@@ -28,7 +31,7 @@ function prRow(pr) {
|
|
|
28
31
|
'<td><span class="pr-id">' + escapeHtml(String(prId)) + '</span></td>' +
|
|
29
32
|
'<td><a class="pr-title" href="' + escapeHtml(safeUrl(url)) + '" target="_blank" rel="noopener">' + escapeHtml(pr.title || 'Untitled') + '</a>' + (pr.description ? '<div class="pr-desc">' + escapeHtml(pr.description.length > 120 ? pr.description.slice(0, 120) + '...' : pr.description) + '</div>' : '') + '</td>' +
|
|
30
33
|
'<td><span class="pr-agent">' + escapeHtml(pr.agent || '—') + '</span></td>' +
|
|
31
|
-
'<td><span class="
|
|
34
|
+
'<td><span class="' + branchClass + '"' + (branchError ? ' title="' + escapeHtml(branchError) + '"' : '') + '>' + escapeHtml(branchLabel) + '</span>' + pendingReasonHtml + '</td>' +
|
|
32
35
|
'<td><span class="pr-badge ' + reviewClass + '"' + (reviewTitle ? ' title="' + escapeHtml(reviewTitle) + '"' : '') + '>' + escapeHtml(reviewLabel) + '</span></td>' +
|
|
33
36
|
'<td>' + (sq.reviewer && sq.status !== 'waiting' ? '<span class="pr-agent" title="' + escapeHtml(sq.note || '') + '">' + escapeHtml(sq.reviewer) + '</span>' : sq.reviewer && sq.status === 'waiting' ? '<span class="pr-agent" style="color:var(--muted)" title="Vote pending confirmation">' + escapeHtml(sq.reviewer) + '…</span>' : pr.reviewedBy && pr.reviewedBy.length ? '<span class="pr-agent">' + escapeHtml(pr.reviewedBy.join(', ')) + '</span>' : '<span style="color:var(--muted);font-size:11px">—</span>') + '</td>' +
|
|
34
37
|
'<td><span class="pr-badge ' + buildClass + '">' + escapeHtml(buildLabel) + '</span></td>' +
|
package/dashboard/styles.css
CHANGED
|
@@ -262,6 +262,7 @@
|
|
|
262
262
|
.pr-id { color: var(--muted); font-family: Consolas, monospace; font-size: var(--text-base); }
|
|
263
263
|
.pr-agent { font-size: var(--text-base); color: var(--text); }
|
|
264
264
|
.pr-branch { font-family: Consolas, monospace; font-size: var(--text-sm); color: var(--muted); background: var(--bg); padding: var(--space-1) var(--space-3); border-radius: 3px; border: 1px solid var(--border); }
|
|
265
|
+
.pr-branch.branch-missing { color: var(--red); border-color: var(--red); background: rgba(248,81,73,0.12); }
|
|
265
266
|
.pr-badge { font-size: var(--text-sm); font-weight: 600; padding: var(--space-1) var(--space-4); border-radius: var(--radius-xl); text-transform: uppercase; letter-spacing: 0.5px; white-space: nowrap; }
|
|
266
267
|
.pr-badge.draft { background: rgba(139,148,158,0.15); color: var(--muted); border: 1px solid var(--border); }
|
|
267
268
|
.pr-badge.active { background: rgba(88,166,255,0.15); color: var(--blue); border: 1px solid var(--blue); }
|
package/dashboard.js
CHANGED
|
@@ -5927,6 +5927,7 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
5927
5927
|
if (prData.description) pr.description = prData.description.slice(0, 500);
|
|
5928
5928
|
if (!pr.branch && prData.branch) {
|
|
5929
5929
|
pr.branch = prData.branch;
|
|
5930
|
+
if (pr._branchResolutionError) delete pr._branchResolutionError;
|
|
5930
5931
|
if (pr._pendingReason === 'missing_pr_branch') delete pr._pendingReason;
|
|
5931
5932
|
}
|
|
5932
5933
|
if (pr.agent === 'human' && prData.author) pr.agent = prData.author;
|
package/engine/ado.js
CHANGED
|
@@ -357,6 +357,14 @@ async function pollPrStatus(config) {
|
|
|
357
357
|
|
|
358
358
|
const prData = await adoFetch(`${repoBase}?api-version=7.1`, token);
|
|
359
359
|
|
|
360
|
+
const sourceBranch = stripRefsHeads(prData.sourceRefName);
|
|
361
|
+
if (sourceBranch && pr.branch !== sourceBranch) {
|
|
362
|
+
pr.branch = sourceBranch;
|
|
363
|
+
if (pr._branchResolutionError) delete pr._branchResolutionError;
|
|
364
|
+
if (pr._pendingReason === 'missing_pr_branch') delete pr._pendingReason;
|
|
365
|
+
updated = true;
|
|
366
|
+
}
|
|
367
|
+
|
|
360
368
|
let newStatus = pr.status;
|
|
361
369
|
if (prData.status === 'completed') newStatus = PR_STATUS.MERGED;
|
|
362
370
|
else if (prData.status === 'abandoned') newStatus = PR_STATUS.ABANDONED;
|
|
@@ -405,12 +413,6 @@ async function pollPrStatus(config) {
|
|
|
405
413
|
pr._adoSourceCommit = sourceCommit;
|
|
406
414
|
updated = true;
|
|
407
415
|
}
|
|
408
|
-
const sourceBranch = stripRefsHeads(prData.sourceRefName);
|
|
409
|
-
if (!pr.branch && sourceBranch) {
|
|
410
|
-
pr.branch = sourceBranch;
|
|
411
|
-
if (pr._pendingReason === 'missing_pr_branch') delete pr._pendingReason;
|
|
412
|
-
updated = true;
|
|
413
|
-
}
|
|
414
416
|
|
|
415
417
|
const reviewers = prData.reviewers || [];
|
|
416
418
|
const votes = reviewers.map(r => r.vote).filter(v => v !== undefined);
|
|
@@ -795,6 +797,7 @@ async function reconcilePrs(config) {
|
|
|
795
797
|
}
|
|
796
798
|
if (existing && !existing.branch && branch) {
|
|
797
799
|
existing.branch = branch;
|
|
800
|
+
if (existing._branchResolutionError) delete existing._branchResolutionError;
|
|
798
801
|
if (existing._pendingReason === 'missing_pr_branch') delete existing._pendingReason;
|
|
799
802
|
metadataUpdated++;
|
|
800
803
|
}
|
package/engine/github.js
CHANGED
|
@@ -284,6 +284,7 @@ async function forEachActiveGhPr(config, callback) {
|
|
|
284
284
|
if (pr.agent === 'human' && prData.user?.login) pr.agent = prData.user.login;
|
|
285
285
|
if (!pr.branch && prData.head?.ref) {
|
|
286
286
|
pr.branch = prData.head.ref;
|
|
287
|
+
if (pr._branchResolutionError) delete pr._branchResolutionError;
|
|
287
288
|
if (pr._pendingReason === 'missing_pr_branch') delete pr._pendingReason;
|
|
288
289
|
}
|
|
289
290
|
}
|
|
@@ -327,6 +328,14 @@ async function pollPrStatus(config) {
|
|
|
327
328
|
|
|
328
329
|
let updated = false;
|
|
329
330
|
|
|
331
|
+
const headBranch = prData.head?.ref ? String(prData.head.ref).trim() : '';
|
|
332
|
+
if (headBranch && pr.branch !== headBranch) {
|
|
333
|
+
pr.branch = headBranch;
|
|
334
|
+
if (pr._branchResolutionError) delete pr._branchResolutionError;
|
|
335
|
+
if (pr._pendingReason === 'missing_pr_branch') delete pr._pendingReason;
|
|
336
|
+
updated = true;
|
|
337
|
+
}
|
|
338
|
+
|
|
330
339
|
// Map GitHub PR state to minions status
|
|
331
340
|
let newStatus = pr.status;
|
|
332
341
|
if (prData.merged) newStatus = PR_STATUS.MERGED;
|
|
@@ -703,6 +712,7 @@ async function reconcilePrs(config) {
|
|
|
703
712
|
}
|
|
704
713
|
if (existing && !existing.branch && branch) {
|
|
705
714
|
existing.branch = branch;
|
|
715
|
+
if (existing._branchResolutionError) delete existing._branchResolutionError;
|
|
706
716
|
if (existing._pendingReason === 'missing_pr_branch') delete existing._pendingReason;
|
|
707
717
|
metadataUpdated++;
|
|
708
718
|
}
|
package/engine.js
CHANGED
|
@@ -1982,56 +1982,99 @@ function clearPendingHumanFeedbackFlag(projectMeta, prId) {
|
|
|
1982
1982
|
|
|
1983
1983
|
const PR_PENDING_MISSING_BRANCH = 'missing_pr_branch';
|
|
1984
1984
|
|
|
1985
|
-
function
|
|
1986
|
-
|
|
1985
|
+
function normalizePrBranch(value) {
|
|
1986
|
+
const raw = value == null ? '' : String(value).trim();
|
|
1987
|
+
if (!raw) return '';
|
|
1988
|
+
return raw.replace(/^refs\/heads\//i, '');
|
|
1987
1989
|
}
|
|
1988
1990
|
|
|
1989
|
-
function
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1991
|
+
function resolvePrBranch(pr) {
|
|
1992
|
+
if (!pr || typeof pr !== 'object') return '';
|
|
1993
|
+
const candidates = [
|
|
1994
|
+
pr.branch,
|
|
1995
|
+
pr.pr_branch,
|
|
1996
|
+
pr.prBranch,
|
|
1997
|
+
pr.sourceRefName,
|
|
1998
|
+
pr.sourceBranch,
|
|
1999
|
+
pr.sourceRef,
|
|
2000
|
+
pr.headRefName,
|
|
2001
|
+
pr.head?.ref,
|
|
2002
|
+
];
|
|
2003
|
+
for (const candidate of candidates) {
|
|
2004
|
+
const branch = normalizePrBranch(candidate);
|
|
2005
|
+
if (branch) return branch;
|
|
2006
|
+
}
|
|
2007
|
+
return '';
|
|
1999
2008
|
}
|
|
2000
2009
|
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2010
|
+
// Unified branch-resolution state writer: when branch is found, persist it and
|
|
2011
|
+
// clear BOTH the structured _branchResolutionError (red-badge UI) and the
|
|
2012
|
+
// _pendingReason marker (dashboard pending-reason vocabulary). When branch is
|
|
2013
|
+
// missing, set BOTH fields so the dashboard can surface the gate via either
|
|
2014
|
+
// rendering path.
|
|
2015
|
+
function updatePrBranchResolutionState(project, pr, { branch = '', reason = '' } = {}) {
|
|
2016
|
+
let changed = false;
|
|
2017
|
+
try {
|
|
2018
|
+
mutatePullRequests(projectPrPath(project), prs => {
|
|
2019
|
+
const target = shared.findPrRecord(prs, pr, project);
|
|
2020
|
+
if (!target) return;
|
|
2021
|
+
if (branch) {
|
|
2022
|
+
if (target.branch !== branch) {
|
|
2023
|
+
target.branch = branch;
|
|
2024
|
+
changed = true;
|
|
2025
|
+
}
|
|
2026
|
+
if (target._branchResolutionError) {
|
|
2027
|
+
delete target._branchResolutionError;
|
|
2028
|
+
changed = true;
|
|
2029
|
+
}
|
|
2030
|
+
if (target._pendingReason === PR_PENDING_MISSING_BRANCH) {
|
|
2031
|
+
delete target._pendingReason;
|
|
2032
|
+
changed = true;
|
|
2033
|
+
}
|
|
2034
|
+
return;
|
|
2035
|
+
}
|
|
2036
|
+
if (reason) {
|
|
2037
|
+
const currentReason = target._branchResolutionError?.reason || '';
|
|
2038
|
+
if (currentReason !== reason) {
|
|
2039
|
+
target._branchResolutionError = { reason, at: ts() };
|
|
2040
|
+
changed = true;
|
|
2041
|
+
}
|
|
2042
|
+
if (target._pendingReason !== PR_PENDING_MISSING_BRANCH) {
|
|
2043
|
+
target._pendingReason = PR_PENDING_MISSING_BRANCH;
|
|
2044
|
+
changed = true;
|
|
2045
|
+
}
|
|
2046
|
+
}
|
|
2047
|
+
});
|
|
2048
|
+
} catch (e) {
|
|
2049
|
+
log('warn', `mark PR branch resolution state for ${pr?.id || 'unknown PR'}: ${e.message}`);
|
|
2050
|
+
}
|
|
2051
|
+
if (branch) {
|
|
2052
|
+
pr.branch = branch;
|
|
2053
|
+
if (pr._branchResolutionError) delete pr._branchResolutionError;
|
|
2054
|
+
if (pr._pendingReason === PR_PENDING_MISSING_BRANCH) delete pr._pendingReason;
|
|
2055
|
+
} else if (reason && changed) {
|
|
2056
|
+
pr._branchResolutionError = { reason, at: ts() };
|
|
2009
2057
|
pr._pendingReason = PR_PENDING_MISSING_BRANCH;
|
|
2010
|
-
log('warn', `PR ${pr.id}: cannot dispatch ${action} — missing pr_branch; waiting for PR metadata enrichment`);
|
|
2011
2058
|
}
|
|
2012
2059
|
return changed;
|
|
2013
2060
|
}
|
|
2014
2061
|
|
|
2015
|
-
function
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
if (
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
}
|
|
2025
|
-
|
|
2026
|
-
function canDispatchPrBranch(project, pr, action) {
|
|
2027
|
-
if (getPrDispatchBranch(pr)) {
|
|
2028
|
-
clearPrMissingBranch(project, pr);
|
|
2029
|
-
return true;
|
|
2062
|
+
function ensurePrBranchForDispatch(project, pr, automationType) {
|
|
2063
|
+
const branch = resolvePrBranch(pr);
|
|
2064
|
+
if (branch) {
|
|
2065
|
+
if (pr.branch !== branch || pr._branchResolutionError || pr._pendingReason === PR_PENDING_MISSING_BRANCH) {
|
|
2066
|
+
updatePrBranchResolutionState(project, pr, { branch });
|
|
2067
|
+
}
|
|
2068
|
+
return branch;
|
|
2069
|
+
}
|
|
2070
|
+
const reason = `Cannot dispatch ${automationType} for ${shared.getPrDisplayId(pr)}: missing pr_branch/source branch metadata. Link or refresh the PR so the source branch is known.`;
|
|
2071
|
+
if (updatePrBranchResolutionState(project, pr, { reason })) {
|
|
2072
|
+
log('warn', `PR ${pr.id}: ${reason}`);
|
|
2030
2073
|
}
|
|
2031
|
-
|
|
2032
|
-
return false;
|
|
2074
|
+
return '';
|
|
2033
2075
|
}
|
|
2034
2076
|
|
|
2077
|
+
|
|
2035
2078
|
/**
|
|
2036
2079
|
* Scan pull-requests.json for PRs needing review or fixes
|
|
2037
2080
|
*/
|
|
@@ -2073,10 +2116,10 @@ async function discoverFromPrs(config, project) {
|
|
|
2073
2116
|
const prDisplayId = shared.getPrDisplayId(pr);
|
|
2074
2117
|
const prCanonicalId = shared.getCanonicalPrId(project, pr, pr.url || '');
|
|
2075
2118
|
if (activePrIds.has(prCanonicalId)) continue; // Skip PRs with active dispatch (prevent race)
|
|
2076
|
-
|
|
2119
|
+
const prBranchForMutex = resolvePrBranch(pr);
|
|
2077
2120
|
// Branch mutex: skip if PR branch is locked by any active dispatch (cross-type collision)
|
|
2078
|
-
if (
|
|
2079
|
-
log('info', `Branch mutex: skipping PR ${pr.id} dispatch — branch ${
|
|
2121
|
+
if (prBranchForMutex && isBranchActive(prBranchForMutex)) {
|
|
2122
|
+
log('info', `Branch mutex: skipping PR ${pr.id} dispatch — branch ${prBranchForMutex} locked by another agent`);
|
|
2080
2123
|
continue;
|
|
2081
2124
|
}
|
|
2082
2125
|
// Skip human-authored PRs not linked to any work item — only auto-manage agent PRs
|
|
@@ -2114,7 +2157,6 @@ async function discoverFromPrs(config, project) {
|
|
|
2114
2157
|
if (needsReview) {
|
|
2115
2158
|
const key = `review-${project?.name || 'default'}-${prDisplayId}`;
|
|
2116
2159
|
if (isAlreadyDispatched(key) || isOnCooldown(key, cooldownMs)) continue;
|
|
2117
|
-
if (!canDispatchPrBranch(project, pr, 'review')) continue;
|
|
2118
2160
|
|
|
2119
2161
|
// Pre-dispatch live vote check — cached reviewStatus may be stale (poll lag ~6 min)
|
|
2120
2162
|
try {
|
|
@@ -2139,11 +2181,13 @@ async function discoverFromPrs(config, project) {
|
|
|
2139
2181
|
|
|
2140
2182
|
const agentId = resolveAgent('review', config);
|
|
2141
2183
|
if (!agentId) continue;
|
|
2184
|
+
const prBranch = ensurePrBranchForDispatch(project, pr, 'review');
|
|
2185
|
+
if (!prBranch) continue;
|
|
2142
2186
|
|
|
2143
2187
|
const item = buildPrDispatch(agentId, config, project, pr, 'review', {
|
|
2144
|
-
pr_id: pr.id, pr_number: prNumber, pr_title: pr.title || '', pr_branch:
|
|
2188
|
+
pr_id: pr.id, pr_number: prNumber, pr_title: pr.title || '', pr_branch: prBranch,
|
|
2145
2189
|
pr_author: pr.agent || '', pr_url: pr.url || '',
|
|
2146
|
-
}, `Review ${pr.id}: ${pr.title}`, { dispatchKey: key, source: 'pr', pr, branch:
|
|
2190
|
+
}, `Review ${pr.id}: ${pr.title}`, { dispatchKey: key, source: 'pr', pr, branch: prBranch, project: projMeta });
|
|
2147
2191
|
if (item) { newWork.push(item); }
|
|
2148
2192
|
}
|
|
2149
2193
|
|
|
@@ -2158,7 +2202,6 @@ async function discoverFromPrs(config, project) {
|
|
|
2158
2202
|
// Skip isAlreadyDispatched — fixedAfterReview/lastReviewedAt already dedupe; the 1hr
|
|
2159
2203
|
// completed-dispatch window would block legitimate re-reviews within the hour after a fix
|
|
2160
2204
|
if (isOnCooldown(key, cooldownMs)) continue;
|
|
2161
|
-
if (!canDispatchPrBranch(project, pr, 're-review')) continue;
|
|
2162
2205
|
|
|
2163
2206
|
// Pre-dispatch live vote check — cached 'waiting' may be stale if reviewer already acted
|
|
2164
2207
|
try {
|
|
@@ -2181,11 +2224,13 @@ async function discoverFromPrs(config, project) {
|
|
|
2181
2224
|
|
|
2182
2225
|
const agentId = resolveAgent('review', config);
|
|
2183
2226
|
if (!agentId) continue;
|
|
2227
|
+
const prBranch = ensurePrBranchForDispatch(project, pr, 're-review');
|
|
2228
|
+
if (!prBranch) continue;
|
|
2184
2229
|
|
|
2185
2230
|
const item = buildPrDispatch(agentId, config, project, pr, 'review', {
|
|
2186
|
-
pr_id: pr.id, pr_number: prNumber, pr_title: pr.title || '', pr_branch:
|
|
2231
|
+
pr_id: pr.id, pr_number: prNumber, pr_title: pr.title || '', pr_branch: prBranch,
|
|
2187
2232
|
pr_author: pr.agent || '', pr_url: pr.url || '',
|
|
2188
|
-
}, `Review ${pr.id}: ${pr.title}`, { dispatchKey: key, source: 'pr', pr, branch:
|
|
2233
|
+
}, `Review ${pr.id}: ${pr.title}`, { dispatchKey: key, source: 'pr', pr, branch: prBranch, project: projMeta });
|
|
2189
2234
|
if (item) { newWork.push(item); }
|
|
2190
2235
|
}
|
|
2191
2236
|
|
|
@@ -2195,14 +2240,15 @@ async function discoverFromPrs(config, project) {
|
|
|
2195
2240
|
if (evalLoopEnabled && reviewStatus === 'changes-requested' && !awaitingReReview && !evalEscalated) {
|
|
2196
2241
|
const key = `fix-${project?.name || 'default'}-${prDisplayId}`;
|
|
2197
2242
|
if (isAlreadyDispatched(key) || isOnCooldown(key, cooldownMs)) continue;
|
|
2198
|
-
if (!canDispatchPrBranch(project, pr, 'fix')) continue;
|
|
2199
2243
|
const agentId = resolveAgent('fix', config, { authorAgent: pr.agent });
|
|
2200
2244
|
if (!agentId) continue;
|
|
2245
|
+
const prBranch = ensurePrBranchForDispatch(project, pr, 'fix');
|
|
2246
|
+
if (!prBranch) continue;
|
|
2201
2247
|
|
|
2202
2248
|
const item = buildPrDispatch(agentId, config, project, pr, 'fix', {
|
|
2203
|
-
pr_id: pr.id, pr_branch:
|
|
2249
|
+
pr_id: pr.id, pr_branch: prBranch,
|
|
2204
2250
|
review_note: pr.minionsReview?.note || pr.reviewNote || 'See PR thread comments',
|
|
2205
|
-
}, `Fix ${pr.id}: ${pr.title || ''} — review feedback`, { dispatchKey: key, source: 'pr', pr, branch:
|
|
2251
|
+
}, `Fix ${pr.id}: ${pr.title || ''} — review feedback`, { dispatchKey: key, source: 'pr', pr, branch: prBranch, project: projMeta });
|
|
2206
2252
|
if (item) {
|
|
2207
2253
|
newWork.push(item); setCooldown(key); fixDispatched = true;
|
|
2208
2254
|
// Increment review→fix cycle counter
|
|
@@ -2235,9 +2281,10 @@ async function discoverFromPrs(config, project) {
|
|
|
2235
2281
|
}
|
|
2236
2282
|
continue;
|
|
2237
2283
|
}
|
|
2238
|
-
if (!canDispatchPrBranch(project, pr, 'human-feedback fix')) continue;
|
|
2239
2284
|
const agentId = resolveAgent('fix', config, { authorAgent: pr.agent });
|
|
2240
2285
|
if (!agentId) continue;
|
|
2286
|
+
const prBranch = ensurePrBranchForDispatch(project, pr, 'human-feedback fix');
|
|
2287
|
+
if (!prBranch) continue;
|
|
2241
2288
|
|
|
2242
2289
|
const coalesced = [...staleCoalesced, ...getCoalescedContexts(key)];
|
|
2243
2290
|
let reviewNote = pr.humanFeedback.feedbackContent || 'See PR thread comments';
|
|
@@ -2247,10 +2294,10 @@ async function discoverFromPrs(config, project) {
|
|
|
2247
2294
|
}
|
|
2248
2295
|
|
|
2249
2296
|
const item = buildPrDispatch(agentId, config, project, pr, 'fix', {
|
|
2250
|
-
pr_id: pr.id, pr_number: prNumber, pr_title: pr.title || '', pr_branch:
|
|
2297
|
+
pr_id: pr.id, pr_number: prNumber, pr_title: pr.title || '', pr_branch: prBranch,
|
|
2251
2298
|
reviewer: 'Human Reviewer',
|
|
2252
2299
|
review_note: reviewNote,
|
|
2253
|
-
}, `Fix ${pr.id}: ${pr.title || ''} — human feedback`, { dispatchKey: key, source: 'pr-human-feedback', pr, branch:
|
|
2300
|
+
}, `Fix ${pr.id}: ${pr.title || ''} — human feedback`, { dispatchKey: key, source: 'pr-human-feedback', pr, branch: prBranch, project: projMeta });
|
|
2254
2301
|
if (item) { newWork.push(item); fixDispatched = true; }
|
|
2255
2302
|
}
|
|
2256
2303
|
|
|
@@ -2283,7 +2330,6 @@ async function discoverFromPrs(config, project) {
|
|
|
2283
2330
|
|
|
2284
2331
|
const key = `build-fix-${project?.name || 'default'}-${prDisplayId}`;
|
|
2285
2332
|
if (fixThrottled || isAlreadyDispatched(key) || isOnCooldown(key, cooldownMs)) continue;
|
|
2286
|
-
if (!canDispatchPrBranch(project, pr, 'build fix')) continue;
|
|
2287
2333
|
|
|
2288
2334
|
// Pre-dispatch live build check — cached buildStatus may be stale: ADO can
|
|
2289
2335
|
// recompute the merge commit when master moves and pollPrStatus deliberately
|
|
@@ -2319,6 +2365,8 @@ async function discoverFromPrs(config, project) {
|
|
|
2319
2365
|
|
|
2320
2366
|
const agentId = resolveAgent('fix', config, { authorAgent: pr.agent });
|
|
2321
2367
|
if (!agentId) continue;
|
|
2368
|
+
const prBranch = ensurePrBranchForDispatch(project, pr, 'build-fix');
|
|
2369
|
+
if (!prBranch) continue;
|
|
2322
2370
|
|
|
2323
2371
|
let reviewNote = `Build is failing: ${pr.buildFailReason || 'Check CI pipeline for details'}. Fix the build errors and push.`;
|
|
2324
2372
|
if (pr.buildErrorLog) {
|
|
@@ -2326,9 +2374,9 @@ async function discoverFromPrs(config, project) {
|
|
|
2326
2374
|
}
|
|
2327
2375
|
|
|
2328
2376
|
const item = buildPrDispatch(agentId, config, project, pr, 'fix', {
|
|
2329
|
-
pr_id: pr.id, pr_branch:
|
|
2377
|
+
pr_id: pr.id, pr_branch: prBranch,
|
|
2330
2378
|
review_note: reviewNote,
|
|
2331
|
-
}, `Fix build failure on ${pr.id}: ${pr.title || ''}`, { dispatchKey: key, source: 'pr', pr, branch:
|
|
2379
|
+
}, `Fix build failure on ${pr.id}: ${pr.title || ''}`, { dispatchKey: key, source: 'pr', pr, branch: prBranch, project: projMeta });
|
|
2332
2380
|
if (item) {
|
|
2333
2381
|
newWork.push(item); setCooldown(key); fixDispatched = true;
|
|
2334
2382
|
// Increment build fix attempts counter
|
|
@@ -2368,7 +2416,6 @@ async function discoverFromPrs(config, project) {
|
|
|
2368
2416
|
const conflictFixedAt = pr._conflictFixedAt;
|
|
2369
2417
|
const withinLag = conflictFixedAt && Date.now() - new Date(conflictFixedAt).getTime() < 10 * 60 * 1000;
|
|
2370
2418
|
if (!withinLag && !fixThrottled && !isAlreadyDispatched(key) && !isOnCooldown(key, cooldownMs)) {
|
|
2371
|
-
if (!canDispatchPrBranch(project, pr, 'conflict fix')) continue;
|
|
2372
2419
|
// Pre-dispatch live conflict check — cached `_mergeConflict` may be
|
|
2373
2420
|
// stale: ADO/GitHub recompute mergeStatus asynchronously (1–5 min lag),
|
|
2374
2421
|
// so a successful upstream merge can leave the flag set even after the
|
|
@@ -2395,10 +2442,12 @@ async function discoverFromPrs(config, project) {
|
|
|
2395
2442
|
if (!liveSkip) {
|
|
2396
2443
|
const agentId = resolveAgent('fix', config, { authorAgent: pr.agent });
|
|
2397
2444
|
if (agentId) {
|
|
2445
|
+
const prBranch = ensurePrBranchForDispatch(project, pr, 'conflict-fix');
|
|
2446
|
+
if (!prBranch) continue;
|
|
2398
2447
|
const item = buildPrDispatch(agentId, config, project, pr, 'fix', {
|
|
2399
|
-
pr_id: pr.id, pr_branch:
|
|
2448
|
+
pr_id: pr.id, pr_branch: prBranch,
|
|
2400
2449
|
review_note: `This PR has merge conflicts with the target branch. Resolve the conflicts:\n\n1. Pull latest from main/master\n2. Resolve all conflicts (prefer PR branch changes unless main has critical fixes)\n3. Build and test after resolving\n4. Push the resolved branch`,
|
|
2401
|
-
}, `Fix merge conflicts on ${pr.id}: ${pr.title || ''}`, { dispatchKey: key, source: 'pr', pr, branch:
|
|
2450
|
+
}, `Fix merge conflicts on ${pr.id}: ${pr.title || ''}`, { dispatchKey: key, source: 'pr', pr, branch: prBranch, project: projMeta });
|
|
2402
2451
|
if (item) {
|
|
2403
2452
|
newWork.push(item);
|
|
2404
2453
|
setCooldown(key);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1640",
|
|
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"
|