@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 CHANGED
@@ -1,9 +1,13 @@
1
1
  # Changelog
2
2
 
3
- ## 0.1.1639 (2026-04-30)
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="pr-branch">' + escapeHtml(pr.branch || '') + '</span>' + pendingReasonHtml + '</td>' +
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>' +
@@ -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
  }
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "runtime": "copilot",
3
3
  "models": null,
4
- "cachedAt": "2026-04-30T16:35:01.979Z"
4
+ "cachedAt": "2026-04-30T17:09:32.809Z"
5
5
  }
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 getPrDispatchBranch(pr) {
1986
- return typeof pr?.branch === 'string' ? pr.branch.trim() : '';
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 mutatePrPendingReason(project, pr, mutator) {
1990
- const prPath = projectPrPath(project);
1991
- let changed = false;
1992
- mutatePullRequests(prPath, prs => {
1993
- const target = shared.findPrRecord(prs, pr, project);
1994
- if (!target) return prs;
1995
- changed = mutator(target) === true;
1996
- return prs;
1997
- });
1998
- return changed;
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
- function markPrMissingBranch(project, pr, action) {
2002
- if (pr._pendingReason === PR_PENDING_MISSING_BRANCH) return false;
2003
- const changed = mutatePrPendingReason(project, pr, target => {
2004
- if (target._pendingReason === PR_PENDING_MISSING_BRANCH) return false;
2005
- target._pendingReason = PR_PENDING_MISSING_BRANCH;
2006
- return true;
2007
- });
2008
- if (changed) {
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 clearPrMissingBranch(project, pr) {
2016
- if (pr._pendingReason !== PR_PENDING_MISSING_BRANCH) return false;
2017
- const changed = mutatePrPendingReason(project, pr, target => {
2018
- if (target._pendingReason !== PR_PENDING_MISSING_BRANCH) return false;
2019
- delete target._pendingReason;
2020
- return true;
2021
- });
2022
- if (changed) delete pr._pendingReason;
2023
- return changed;
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
- markPrMissingBranch(project, pr, action);
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
- if (getPrDispatchBranch(pr)) clearPrMissingBranch(project, pr);
2119
+ const prBranchForMutex = resolvePrBranch(pr);
2077
2120
  // Branch mutex: skip if PR branch is locked by any active dispatch (cross-type collision)
2078
- if (pr.branch && isBranchActive(pr.branch)) {
2079
- log('info', `Branch mutex: skipping PR ${pr.id} dispatch — branch ${pr.branch} locked by another agent`);
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: 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: pr.branch, project: projMeta });
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: 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: pr.branch, project: projMeta });
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: 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: pr.branch, project: projMeta });
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: 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: pr.branch, project: projMeta });
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: 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: pr.branch, project: projMeta });
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: 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: pr.branch, project: projMeta });
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.1639",
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"