@yemi33/minions 0.1.2350 → 0.1.2351

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.
@@ -389,6 +389,45 @@ function _defaultWriteInboxNote(slug, content) {
389
389
  return require('./dispatch').writeInboxAlert(slug, content);
390
390
  }
391
391
 
392
+ // Best-effort, side-effect-light resolution of `mainRef` as a restore target
393
+ // when a captured originalRef is found to be contaminated (see
394
+ // ENGINE_DISPATCH_BRANCH_PATTERN above). Mirrors the STALE-BASE GUARD's
395
+ // local-main / auto-base-repair fallback (Step 4d) but never switches HEAD
396
+ // itself — it only materializes a local `<mainRef>` branch (a plain ref op,
397
+ // no checkout, no fetch, issue #226 preserved) when auto-base-repair is
398
+ // enabled and no local ref already exists. Returns `{ ref, type }` on success
399
+ // or `{ ref: null, type: null }` when mainRef cannot be resolved without a
400
+ // fetch and auto-base-repair is disabled or unavailable.
401
+ async function _resolveMainRefForRestore({ git, baseOpts, mainRef, autoBaseRepair, _resolveAutoBaseRepair, localPath, logFn }) {
402
+ try {
403
+ await git(['rev-parse', '--verify', '--quiet', `refs/heads/${mainRef}`], baseOpts);
404
+ return { ref: mainRef, type: 'branch' };
405
+ } catch { /* fall through to auto-repair */ }
406
+
407
+ let wantBaseRepair = false;
408
+ if (typeof autoBaseRepair === 'boolean') {
409
+ wantBaseRepair = autoBaseRepair;
410
+ } else {
411
+ const resolver = (typeof _resolveAutoBaseRepair === 'function') ? _resolveAutoBaseRepair : _defaultResolveAutoBaseRepair;
412
+ try { wantBaseRepair = !!resolver(localPath); } catch { wantBaseRepair = false; }
413
+ }
414
+ if (!wantBaseRepair) return { ref: null, type: null };
415
+
416
+ try {
417
+ await git(['rev-parse', '--verify', '--quiet', `refs/remotes/origin/${mainRef}`], baseOpts);
418
+ } catch {
419
+ return { ref: null, type: null };
420
+ }
421
+ try {
422
+ await git(['branch', '--no-track', mainRef, `refs/remotes/origin/${mainRef}`], baseOpts);
423
+ logFn('info',
424
+ `live-checkout: materialized local '${mainRef}' from 'origin/${mainRef}' (no fetch, issue #226 preserved) to correct a contaminated originalRef.`);
425
+ return { ref: mainRef, type: 'branch' };
426
+ } catch {
427
+ return { ref: null, type: null };
428
+ }
429
+ }
430
+
392
431
  async function prepareLiveCheckout(opts = {}) {
393
432
  const {
394
433
  localPath,
@@ -842,6 +881,32 @@ async function prepareLiveCheckout(opts = {}) {
842
881
  // Plain checkout — NO --force, NO -B, NO pull. Operator owns local commits.
843
882
  const failed = await runCheckout(['checkout', branchName], { creating: false });
844
883
  if (failed) return failed;
884
+ // ── originalRef contamination invariant (W-mrb6an8300058fd8). ──────────
885
+ // originalRef was captured at Step 3 BEFORE this checkout, from whatever
886
+ // HEAD happened to be. If it itself looks like a prior dispatch's own
887
+ // engine-managed branch (`_looksLikeAgentBranch` — the same `work/<id>`
888
+ // check issue #608's dirty-tree self-heal uses above) — and this isn't an
889
+ // explicit PR-continuation (allowNonMainBase) — it is never a sane restore
890
+ // target: prefer mainRef instead, while still surfacing the anomaly via
891
+ // logFn rather than silently swallowing it.
892
+ if (!allowNonMainBase && originalRefType === 'branch' && originalRef && originalRef !== mainRef &&
893
+ _looksLikeAgentBranch(originalRef)) {
894
+ logFn(
895
+ `[live-checkout] originalRef anomaly: HEAD was on engine-managed branch '${originalRef}' before checking out '${branchName}' — ` +
896
+ `this looks like leftover contamination from an earlier dispatch, not a sane restore target. Attempting to correct to '${mainRef}'.`,
897
+ 'warn'
898
+ );
899
+ const corrected = await _resolveMainRefForRestore({ git, baseOpts, mainRef, autoBaseRepair, _resolveAutoBaseRepair, localPath, logFn });
900
+ if (corrected.ref) {
901
+ originalRef = corrected.ref;
902
+ originalRefType = corrected.type;
903
+ } else {
904
+ logFn(
905
+ `[live-checkout] originalRef anomaly: could not resolve '${mainRef}' as a corrected restore target (no local ref, auto-base-repair disabled, or 'origin/${mainRef}' missing) — leaving originalRef as the contaminated '${originalRef}'; a human should verify the operator checkout manually.`,
906
+ 'warn'
907
+ );
908
+ }
909
+ }
845
910
  return { ok: true, branch: branchName, created: false, originalRef, originalRefType };
846
911
  }
847
912
 
@@ -983,6 +1048,21 @@ async function prepareLiveCheckout(opts = {}) {
983
1048
  }
984
1049
  logFn('info',
985
1050
  `live-checkout: HEAD was on '${headBranch}' (not base '${mainRef}') — switched to local '${mainRef}' before forking '${branchName}' (no fetch, issue #226 preserved)`);
1051
+ // ── originalRef correction (W-mrb6an8300058fd8). ─────────────────────
1052
+ // originalRef/originalRefType were captured at Step 3 from the STALE
1053
+ // headBranch, BEFORE this self-heal ran. The tree has now been switched
1054
+ // to the CORRECTED mainRef, so the value returned to the caller (and
1055
+ // persisted on the dispatch record — engine.js spawnAgent ~line
1056
+ // 3106-3122) must reflect mainRef, not the stale branch. Otherwise
1057
+ // restoreLiveCheckoutAtDispatchEnd would put the operator checkout BACK
1058
+ // onto the stale branch at dispatch end, propagating the contamination
1059
+ // into every subsequent live-mode dispatch that captures HEAD (the
1060
+ // exact recurrence observed on minions-opg 2026-07-07: HEAD landed on
1061
+ // 'main' via this self-heal, but the persisted originalRef still said
1062
+ // 'work/sched-daily-remove-merged-prs-…', so the dispatch-end restore
1063
+ // switched the operator checkout back onto that stale branch).
1064
+ originalRef = mainRef;
1065
+ originalRefType = 'branch';
986
1066
  }
987
1067
  }
988
1068
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.2350",
3
+ "version": "0.1.2351",
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"