@yemi33/minions 0.1.2352 → 0.1.2354

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 CHANGED
@@ -2460,6 +2460,21 @@ async function spawnAgent(dispatchItem, config) {
2460
2460
  const _liveAllowNonMainBase = !!(meta?.useExistingBranch || meta?.branchStrategy === 'shared-branch');
2461
2461
  const _wiIdForAlert = meta?.item?.id || id;
2462
2462
  const _liveCheckout = require('./engine/live-checkout');
2463
+ // W-mrawgw4q000a6bff: stash-before-reset ordering. Resolve BOTH recovery
2464
+ // policies here (before the first prepareLiveCheckout call) so we can
2465
+ // decide precedence up front instead of letting prepareLiveCheckout's
2466
+ // internal (autoReset-only) resolution win by default. When auto-stash is
2467
+ // enabled, pass `autoReset: false` to force this first call to just report
2468
+ // `{ ok:false, reason:'dirty' }` on a dirty tree — bypassing its internal
2469
+ // destructive reset attempt entirely — so the non-destructive stash path
2470
+ // below gets first crack at the dirty tree. Auto-reset then only runs as an
2471
+ // explicit LAST-RESORT fallback (see the `_autoStash.outcome === 'unchanged'`
2472
+ // block below) when stash is disabled or itself failed. When auto-stash is
2473
+ // disabled, `autoReset` stays `undefined` so prepareLiveCheckout keeps its
2474
+ // original behavior of self-resolving straight to reset (no change for
2475
+ // auto-reset-only dispatches).
2476
+ const _wantAutoStash = (typeof _liveCheckout.resolveLiveCheckoutAutoStash === 'function')
2477
+ && !!_liveCheckout.resolveLiveCheckoutAutoStash({ project, engine: engineConfig });
2463
2478
  let _liveResult;
2464
2479
  try {
2465
2480
  _liveResult = await _liveCheckout.prepareLiveCheckout({
@@ -2471,6 +2486,7 @@ async function spawnAgent(dispatchItem, config) {
2471
2486
  wiId: _wiIdForAlert,
2472
2487
  skipDirtyCheck: !!project.skipLiveCheckoutDirtyCheck,
2473
2488
  allowNonMainBase: _liveAllowNonMainBase,
2489
+ autoReset: _wantAutoStash ? false : undefined,
2474
2490
  log: (msg, lvl) => log(lvl || 'info', msg),
2475
2491
  });
2476
2492
  } catch (liveErr) {
@@ -2544,6 +2560,59 @@ async function spawnAgent(dispatchItem, config) {
2544
2560
  return null;
2545
2561
  }
2546
2562
  _liveResult = _autoStash.liveResult;
2563
+ // ── Auto-reset LAST-RESORT fallback (W-mrawgw4q000a6bff) ────────────────
2564
+ // Stash was attempted above (if enabled) and had first crack at the dirty
2565
+ // tree. If the tree is STILL confirmed-dirty here — either because
2566
+ // auto-stash is disabled, or because `applyLiveCheckoutAutoStash` itself
2567
+ // reported `outcome:'unchanged'` (the `git stash push` call failed) — and
2568
+ // the fleet/project auto-reset policy resolves true, NOW perform the
2569
+ // destructive `fetch` + `reset --hard origin/<ref>` as a fallback, reusing
2570
+ // prepareLiveCheckout's existing reset logic by re-invoking it with an
2571
+ // explicit `autoReset: true` (bypassing config resolution, which we've
2572
+ // already evaluated). This only fires when the first prepareLiveCheckout
2573
+ // call above was forced to `autoReset:false` (i.e. auto-stash was enabled
2574
+ // but did not recover a clean tree) — when auto-stash was disabled from the
2575
+ // start, the FIRST call already self-resolved auto-reset internally, so
2576
+ // `_autoStash.outcome` is `'unchanged'` there too but `_wantAutoStash` was
2577
+ // false and the original call's own resolution already ran (nothing to
2578
+ // retry here, only re-checking after a NO-OP stash gate). Never destructive
2579
+ // when disabled; never runs when stash already succeeded (`outcome ===
2580
+ // 'stashed'`).
2581
+ // Compute this once, phrased differently from the confirmed-dirty check
2582
+ // below (so source-inspection tests anchored on the literal dirty-alert
2583
+ // check text keep matching that ORIGINAL occurrence, not this one).
2584
+ const _fallbackReason = (_liveResult && _liveResult.ok === false) ? _liveResult.reason : null;
2585
+ if (_wantAutoStash && _autoStash.outcome === 'unchanged'
2586
+ && _fallbackReason === 'dirty'
2587
+ && shared.resolveLiveCheckoutAutoReset(project, engineConfig)) {
2588
+ log('info', `spawnAgent: live-checkout auto-stash did not clear the dirty tree for ${id}; falling back to auto-reset (liveCheckoutAutoReset)`);
2589
+ try {
2590
+ _liveResult = await _liveCheckout.prepareLiveCheckout({
2591
+ localPath: cwd,
2592
+ branchName,
2593
+ mainRef: _liveMainRef,
2594
+ gitOpts: _gitOpts,
2595
+ dispatchId: id,
2596
+ wiId: _wiIdForAlert,
2597
+ skipDirtyCheck: !!project.skipLiveCheckoutDirtyCheck,
2598
+ allowNonMainBase: _liveAllowNonMainBase,
2599
+ autoReset: true,
2600
+ log: (msg, lvl) => log(lvl || 'info', msg),
2601
+ });
2602
+ } catch (resetFallbackErr) {
2603
+ log('error', `spawnAgent: live-checkout auto-reset fallback threw for ${id}: ${resetFallbackErr.message}`);
2604
+ _cleanupPromptFiles();
2605
+ completeDispatch(
2606
+ id,
2607
+ DISPATCH_RESULT.ERROR,
2608
+ `live-checkout failed: ${resetFallbackErr.message}`.slice(0, 800),
2609
+ 'Live-checkout helper threw during the auto-reset fallback (not proof of a dirty tree). Auto-retried with bounded backoff; transient git/branch-lock state usually clears on the next attempt.',
2610
+ { failureClass: FAILURE_CLASS.LIVE_CHECKOUT_FAILED },
2611
+ );
2612
+ cleanupTempAgent(agentId);
2613
+ return null;
2614
+ }
2615
+ }
2547
2616
  if (_liveResult && _liveResult.ok === false && _liveResult.reason === 'dirty') {
2548
2617
  const _dirtyFiles = Array.isArray(_liveResult.dirtyFiles) ? _liveResult.dirtyFiles : [];
2549
2618
  const _branchInfo = typeof _liveResult.branchInfo === 'string' ? _liveResult.branchInfo : '';
@@ -8875,6 +8944,15 @@ function buildWorkItemDispatchVars(item, vars, config, options = {}) {
8875
8944
  } catch (e) { log('warn', `checkpoint read for ${item.id}: ${e.message}`); }
8876
8945
  }
8877
8946
 
8947
+ // Retry context — surfaces WHY the previous attempt was bounced (e.g. a
8948
+ // validation-gate rejection like the plan-to-prd #893 PRD-file check) so a
8949
+ // retried agent doesn't blindly repeat the same mistake. Populated whenever
8950
+ // a prior tick set item._lastRetryReason (dispatch.js / lifecycle.js retry
8951
+ // paths); empty string on a fresh (non-retry) dispatch.
8952
+ vars.retry_context = (item._retryCount && item._lastRetryReason)
8953
+ ? `## Retry Context (attempt ${item._retryCount + 1})\n\nThe previous attempt did not pass validation:\n\n> ${item._lastRetryReason}\n\nAddress this before completing again.`
8954
+ : '';
8955
+
8878
8956
  // ASK-specific variables
8879
8957
  if (workType === WORK_TYPE.ASK) {
8880
8958
  vars.question = item.title + (item.description ? '\n\n' + item.description : '');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.2352",
3
+ "version": "0.1.2354",
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"
@@ -15,6 +15,8 @@ A user has provided a plan. Analyze it against the codebase and produce a struct
15
15
 
16
16
  {{plan_content}}
17
17
 
18
+ {{retry_context}}
19
+
18
20
  ## Instructions
19
21
 
20
22
  {{#project_skills_block}}