@yemi33/minions 0.1.2027 → 0.1.2028
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/engine.js +73 -4
- package/package.json +1 -1
package/engine.js
CHANGED
|
@@ -588,7 +588,23 @@ function resolveDependencyBranches(depIds, sourcePlan, project, config) {
|
|
|
588
588
|
*
|
|
589
589
|
* @returns {Promise<{skipped: boolean, reason?: string}>}
|
|
590
590
|
*/
|
|
591
|
-
async function syncReusedWorktree(rootDir, worktreePath, branchName, gitOpts = {}) {
|
|
591
|
+
async function syncReusedWorktree(rootDir, worktreePath, branchName, gitOpts = {}, opts = {}) {
|
|
592
|
+
// W-mph6n4p00006ce38: Freshen origin/<mainRef> and fast-forward the
|
|
593
|
+
// worktree to it BEFORE the branch sync. Without this, reused worktrees
|
|
594
|
+
// inherit whatever stale local master the engine clone happens to be on,
|
|
595
|
+
// so downstream dep-merges (and zero-dep agent work) get layered onto a
|
|
596
|
+
// stale base. Shared-branch / useExistingBranch dispatches MUST NOT
|
|
597
|
+
// auto-pick-up master mid-flight, so the carve-out is explicit.
|
|
598
|
+
const { mainRef, isSharedBranch } = opts;
|
|
599
|
+
if (mainRef && !isSharedBranch) {
|
|
600
|
+
try { await shared.shellSafeGit(['fetch', 'origin', mainRef], { ...gitOpts, cwd: rootDir }); }
|
|
601
|
+
catch (e) { log('warn', `git: failed to fetch origin/${mainRef} during reuse-sync: ${e.message}`); }
|
|
602
|
+
try { await shared.shellSafeGit(['merge', `origin/${mainRef}`, '--no-edit', '--no-ff'], { ...gitOpts, cwd: worktreePath }); }
|
|
603
|
+
catch (e) {
|
|
604
|
+
log('warn', `git: failed to merge origin/${mainRef} into ${branchName} during reuse-sync: ${e.message}`);
|
|
605
|
+
try { await shared.shellSafeGit(['merge', '--abort'], { ...gitOpts, cwd: worktreePath }); } catch (_) { /* no merge in progress */ }
|
|
606
|
+
}
|
|
607
|
+
}
|
|
592
608
|
// ls-remote --exit-code returns 2 when no matching refs are found on the
|
|
593
609
|
// remote. The probe only lists refs (no object transfer), so it's cheap
|
|
594
610
|
// even on slow links.
|
|
@@ -1063,7 +1079,12 @@ async function spawnAgent(dispatchItem, config) {
|
|
|
1063
1079
|
// (orphan/timeout retry before first push) would otherwise emit a
|
|
1064
1080
|
// "couldn't find remote ref" warn pair on every reuse.
|
|
1065
1081
|
_phaseT.reuseSyncStart = Date.now();
|
|
1066
|
-
|
|
1082
|
+
// W-mph6n4p00006ce38: hand syncReusedWorktree the main ref + shared-branch
|
|
1083
|
+
// flag so it can freshen origin/<mainRef> into the worktree (skipped on
|
|
1084
|
+
// shared-branch dispatches — see syncReusedWorktree's opts contract).
|
|
1085
|
+
const _reuseMainRef = sanitizeBranch(shared.resolveMainBranch(rootDir, project.mainBranch));
|
|
1086
|
+
const _reuseIsShared = meta?.branchStrategy === 'shared-branch' || meta?.useExistingBranch;
|
|
1087
|
+
await syncReusedWorktree(rootDir, existingWt, branchName, _gitOpts, { mainRef: _reuseMainRef, isSharedBranch: _reuseIsShared });
|
|
1067
1088
|
_phaseT.reuseSyncEnd = Date.now();
|
|
1068
1089
|
} else {
|
|
1069
1090
|
_phaseT.createWorktreeStart = Date.now();
|
|
@@ -1167,8 +1188,24 @@ async function spawnAgent(dispatchItem, config) {
|
|
|
1167
1188
|
} else {
|
|
1168
1189
|
log('info', `Creating worktree: ${worktreePath} on branch ${branchName}`);
|
|
1169
1190
|
const mainRef = sanitizeBranch(shared.resolveMainBranch(rootDir, project.mainBranch));
|
|
1191
|
+
// W-mph6n4p00006ce38: mirror the pool-borrow path (~line 1110-1114)
|
|
1192
|
+
// — fetch fresh origin/<mainRef> and start the new branch off it,
|
|
1193
|
+
// not the local ref. Without this, fresh-create dispatches inherit
|
|
1194
|
+
// whatever stale local master the engine clone happens to be on
|
|
1195
|
+
// (most painful: long-lived engine processes between restarts).
|
|
1196
|
+
// Non-fatal: if the fetch fails (network blip, transient auth),
|
|
1197
|
+
// fall back to local mainRef so the dispatch still progresses;
|
|
1198
|
+
// the dep-merge phase's own fetch + the on-failure
|
|
1199
|
+
// `git reset --hard origin/<mainRef>` recovery remain as safety nets.
|
|
1200
|
+
let _freshCreateBase = mainRef;
|
|
1170
1201
|
try {
|
|
1171
|
-
await
|
|
1202
|
+
await shared.shellSafeGit(['fetch', 'origin', mainRef], { ..._gitOpts, cwd: rootDir, timeout: 30000 });
|
|
1203
|
+
_freshCreateBase = `origin/${mainRef}`;
|
|
1204
|
+
} catch (mainFetchErr) {
|
|
1205
|
+
log('warn', `Failed to fetch origin/${mainRef} before fresh-create worktree for ${branchName}: ${mainFetchErr.message} — falling back to local ${mainRef}`);
|
|
1206
|
+
}
|
|
1207
|
+
try {
|
|
1208
|
+
await runWorktreeAdd(rootDir, worktreePath, ['-b', branchName, _freshCreateBase], _worktreeGitOpts, worktreeCreateRetries);
|
|
1172
1209
|
} catch (e1) {
|
|
1173
1210
|
const branchExists = e1.message?.includes('already exists');
|
|
1174
1211
|
log('warn', `Worktree -b failed for ${branchName}: ${e1.message?.split('\n')[0]}`);
|
|
@@ -1180,7 +1217,7 @@ async function spawnAgent(dispatchItem, config) {
|
|
|
1180
1217
|
// Clean up partial worktree directory from failed attempt
|
|
1181
1218
|
try { if (fs.existsSync(worktreePath)) fs.rmSync(worktreePath, { recursive: true, force: true }); } catch { /* optional */ }
|
|
1182
1219
|
try {
|
|
1183
|
-
await runWorktreeAdd(rootDir, worktreePath, ['-b', branchName,
|
|
1220
|
+
await runWorktreeAdd(rootDir, worktreePath, ['-b', branchName, _freshCreateBase], _worktreeGitOpts, 0);
|
|
1184
1221
|
} catch (e1b) {
|
|
1185
1222
|
log('error', `Worktree -b retry also failed for ${branchName}: ${e1b.message?.split('\n')[0]}`);
|
|
1186
1223
|
throw e1b;
|
|
@@ -1303,6 +1340,38 @@ async function spawnAgent(dispatchItem, config) {
|
|
|
1303
1340
|
// of FAILURE_CLASS.MERGE_CONFLICT (which retries 3x against the
|
|
1304
1341
|
// same broken auth path).
|
|
1305
1342
|
let _depAuthFailed = false;
|
|
1343
|
+
// W-mph6n4p00006ce38: Refresh origin/<mainRef> + freshen the worktree
|
|
1344
|
+
// BEFORE the parallel dep fetches and preflight sim. Without this,
|
|
1345
|
+
// preflight + real dep-merges run against whatever stale local master
|
|
1346
|
+
// the engine clone happens to be on, producing false-positive
|
|
1347
|
+
// conflicts whenever master drifted ahead of the engine's last fetch.
|
|
1348
|
+
// The pool-borrow + fresh-create paths already start at fresh
|
|
1349
|
+
// origin/<mainRef>, but the reuse path can outrun its sync; and even
|
|
1350
|
+
// for the start-fresh paths, master can advance between worktree
|
|
1351
|
+
// creation and dep-merge (long-lived dispatches). Shared-branch
|
|
1352
|
+
// dispatches MUST NOT auto-pick-up master mid-flight — the carve-out
|
|
1353
|
+
// is explicit. Non-fatal: failures log + continue with the stale base,
|
|
1354
|
+
// and the existing on-failure `git reset --hard origin/<mainRef>`
|
|
1355
|
+
// recovery (~line 1452) remains the safety net.
|
|
1356
|
+
const _depMainRef = sanitizeBranch(shared.resolveMainBranch(rootDir, project.mainBranch));
|
|
1357
|
+
const _depIsSharedBranch = meta?.branchStrategy === 'shared-branch' || meta?.useExistingBranch;
|
|
1358
|
+
if (!_failedRefCache.has(_depMainRef)) {
|
|
1359
|
+
try {
|
|
1360
|
+
await adoGitAuth.runAdoGit(project, ['fetch', 'origin', _depMainRef], { ..._gitOpts, cwd: rootDir });
|
|
1361
|
+
} catch (mainFetchErr) {
|
|
1362
|
+
log('warn', `Failed to fetch origin/${_depMainRef} before dep merge: ${mainFetchErr.message} — proceeding with stale base`);
|
|
1363
|
+
if (adoGitAuth.isAdoAuthFailure(mainFetchErr)) _depAuthFailed = true;
|
|
1364
|
+
_failedRefCache.add(_depMainRef);
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
if (!_depIsSharedBranch) {
|
|
1368
|
+
try {
|
|
1369
|
+
await shared.shellSafeGit(['merge', `origin/${_depMainRef}`, '--no-edit', '--no-ff'], { ..._gitOpts, cwd: worktreePath });
|
|
1370
|
+
} catch (mainMergeErr) {
|
|
1371
|
+
log('warn', `Failed to merge origin/${_depMainRef} into ${branchName} before dep merge: ${mainMergeErr.message} — proceeding without main refresh`);
|
|
1372
|
+
try { await shared.shellSafeGit(['merge', '--abort'], { ..._gitOpts, cwd: worktreePath }); } catch (_) { /* no merge in progress */ }
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1306
1375
|
// Fetch all dependency branches in parallel (git fetches are independent)
|
|
1307
1376
|
const fetchable = depBranches.filter(d => !_failedRefCache.has(d.branch));
|
|
1308
1377
|
const unfetchable = depBranches.filter(d => _failedRefCache.has(d.branch));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2028",
|
|
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"
|