@yemi33/minions 0.1.2422 → 0.1.2424

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.
@@ -369,6 +369,109 @@
369
369
  .thesis p { margin: 0; color: var(--muted); }
370
370
  .thesis strong { color: var(--text); }
371
371
 
372
+ .evolution-timeline {
373
+ display: grid;
374
+ grid-template-columns: repeat(4, minmax(0, 1fr));
375
+ gap: 16px;
376
+ margin: 0;
377
+ padding: 0;
378
+ list-style: none;
379
+ }
380
+ .evolution-stage {
381
+ position: relative;
382
+ display: flex;
383
+ min-width: 0;
384
+ }
385
+ .evolution-stage:not(:last-child)::after {
386
+ content: "\2192";
387
+ position: absolute;
388
+ top: 25px;
389
+ right: -14px;
390
+ z-index: 2;
391
+ display: grid;
392
+ width: 12px;
393
+ height: 24px;
394
+ place-items: center;
395
+ background: var(--bg);
396
+ color: var(--muted);
397
+ font-size: 0.88rem;
398
+ font-weight: 900;
399
+ }
400
+ .evolution-card {
401
+ position: relative;
402
+ width: 100%;
403
+ min-width: 0;
404
+ padding: 23px;
405
+ overflow-wrap: anywhere;
406
+ border: 1px solid var(--border);
407
+ border-radius: var(--radius-lg);
408
+ background: var(--surface-strong);
409
+ }
410
+ .evolution-card::before {
411
+ content: "";
412
+ position: absolute;
413
+ inset: 0 0 auto;
414
+ height: 3px;
415
+ border-radius: var(--radius-lg) var(--radius-lg) 0 0;
416
+ background: var(--blue);
417
+ }
418
+ .evolution-stage:nth-child(2) .evolution-card::before { background: var(--green); }
419
+ .evolution-stage:nth-child(3) .evolution-card::before { background: var(--purple); }
420
+ .evolution-stage:nth-child(4) .evolution-card::before { background: var(--yellow); }
421
+ .evolution-stage:nth-child(4) .evolution-card {
422
+ border-color: rgba(210, 153, 34, 0.42);
423
+ background: linear-gradient(145deg, rgba(210, 153, 34, 0.08), var(--surface-strong));
424
+ }
425
+ .evolution-meta {
426
+ display: flex;
427
+ align-items: center;
428
+ justify-content: space-between;
429
+ gap: 10px;
430
+ margin-bottom: 20px;
431
+ }
432
+ .evolution-index {
433
+ display: grid;
434
+ width: 34px;
435
+ height: 34px;
436
+ flex: 0 0 auto;
437
+ place-items: center;
438
+ border-radius: var(--radius-md);
439
+ background: rgba(88, 166, 255, 0.12);
440
+ color: var(--blue);
441
+ font-size: 0.76rem;
442
+ font-weight: 950;
443
+ }
444
+ .evolution-date {
445
+ color: var(--muted);
446
+ font-size: 0.72rem;
447
+ font-weight: 850;
448
+ letter-spacing: 0.08em;
449
+ text-align: right;
450
+ text-transform: uppercase;
451
+ }
452
+ .evolution-card h3 { margin-bottom: 13px; font-size: 1.12rem; }
453
+ .evolution-card > p { color: var(--muted); font-size: 0.86rem; }
454
+ .evolution-problem {
455
+ padding-bottom: 14px;
456
+ border-bottom: 1px solid var(--border);
457
+ }
458
+ .evolution-card strong { color: var(--text); }
459
+ .evolution-current {
460
+ margin: 18px 0 0;
461
+ padding: 13px 14px;
462
+ border-radius: var(--radius-md);
463
+ background: rgba(88, 166, 255, 0.07);
464
+ }
465
+ .evolution-stage:nth-child(2) .evolution-current { background: rgba(63, 185, 80, 0.07); }
466
+ .evolution-stage:nth-child(3) .evolution-current { background: rgba(188, 140, 255, 0.08); }
467
+ .evolution-stage:nth-child(4) .evolution-current { background: rgba(210, 153, 34, 0.09); }
468
+ .evolution-source {
469
+ margin: 18px 0 0;
470
+ color: var(--muted);
471
+ font-size: 0.78rem;
472
+ text-align: right;
473
+ }
474
+
372
475
  .journey-shell {
373
476
  --journey-progress: 0%;
374
477
  position: relative;
@@ -854,6 +957,8 @@
854
957
  .journey-panel { grid-template-columns: 1fr; }
855
958
  .map-shell, .prompt-layout { grid-template-columns: 1fr; }
856
959
  .map-details { min-height: 0; }
960
+ .evolution-timeline { grid-template-columns: repeat(2, minmax(0, 1fr)); }
961
+ .evolution-stage:not(:last-child)::after { display: none; }
857
962
  .controls-grid { grid-template-columns: 1fr; }
858
963
  }
859
964
 
@@ -894,6 +999,8 @@
894
999
  .map-arrow-4 { transform: rotate(90deg); }
895
1000
  .map-down { display: none; }
896
1001
  .map-down-2, .map-down-3 { transform: none; }
1002
+ .evolution-timeline { grid-template-columns: 1fr; }
1003
+ .evolution-card { padding: 21px; }
897
1004
  .mode-grid, .security-card, .bounds-grid, .surface-strip, .trouble-grid { grid-template-columns: 1fr; }
898
1005
  .mode-note, .limitation { grid-column: auto; }
899
1006
  .control-card dl { grid-template-columns: 1fr; gap: 2px; }
@@ -920,6 +1027,7 @@
920
1027
  <span>Minions Memory</span>
921
1028
  </a>
922
1029
  <div class="nav-links">
1030
+ <a href="#memory-evolution">Evolution</a>
923
1031
  <a href="#memory-journey">Journey</a>
924
1032
  <a href="#architecture-map">System map</a>
925
1033
  <a href="#prompt-assembly">Prompt</a>
@@ -987,6 +1095,69 @@
987
1095
  </div>
988
1096
  </section>
989
1097
 
1098
+ <section class="section" id="memory-evolution" aria-labelledby="evolution-heading">
1099
+ <div class="section-heading">
1100
+ <p class="eyebrow">Evolution, verified in repository history</p>
1101
+ <h2 id="evolution-heading">How memory grew with the problems it had to solve.</h2>
1102
+ <p>Each layer stayed useful after the next one arrived. The current system keeps readable files, adds focused retrieval, and falls back deliberately instead of treating older paths as obsolete.</p>
1103
+ </div>
1104
+
1105
+ <ol class="evolution-timeline" aria-label="Memory system evolution">
1106
+ <li class="evolution-stage" data-memory-era="shared">
1107
+ <article class="evolution-card" aria-labelledby="evolution-shared-title">
1108
+ <div class="evolution-meta">
1109
+ <span class="evolution-index" aria-hidden="true">01</span>
1110
+ <time class="evolution-date" datetime="2026-03">March 2026</time>
1111
+ </div>
1112
+ <h3 id="evolution-shared-title">Shared team memory</h3>
1113
+ <p class="evolution-problem"><strong>Problem solved:</strong> useful findings were scattered across individual task outputs.</p>
1114
+ <p>Inbox consolidation turned agent and human notes into a deduplicated, readable team digest in <span class="code-chip">notes.md</span>, while preserving source notes in the archive.</p>
1115
+ <p class="evolution-current"><strong>Current role:</strong> consolidation still maintains the team-readable record; the digest is also the team half of the legacy prompt path.</p>
1116
+ </article>
1117
+ </li>
1118
+
1119
+ <li class="evolution-stage" data-memory-era="personal">
1120
+ <article class="evolution-card" aria-labelledby="evolution-personal-title">
1121
+ <div class="evolution-meta">
1122
+ <span class="evolution-index" aria-hidden="true">02</span>
1123
+ <time class="evolution-date" datetime="2026-05">May 2026</time>
1124
+ </div>
1125
+ <h3 id="evolution-personal-title">Personal routing</h3>
1126
+ <p class="evolution-problem"><strong>Problem solved:</strong> one shared digest gave every specialist the same broad history.</p>
1127
+ <p>Consolidation began routing configured, non-temporary authors' findings into <span class="code-chip">knowledge/agents/&lt;agent&gt;.md</span>, using YAML identity with a filename fallback.</p>
1128
+ <p class="evolution-current"><strong>Current role:</strong> these notebooks remain readable and maintained, but broad prompt injection is now a fallback rather than the default recall strategy.</p>
1129
+ </article>
1130
+ </li>
1131
+
1132
+ <li class="evolution-stage" data-memory-era="retrieval">
1133
+ <article class="evolution-card" aria-labelledby="evolution-retrieval-title">
1134
+ <div class="evolution-meta">
1135
+ <span class="evolution-index" aria-hidden="true">03</span>
1136
+ <time class="evolution-date" datetime="2026-07">July 2026</time>
1137
+ </div>
1138
+ <h3 id="evolution-retrieval-title">Structured recall</h3>
1139
+ <p class="evolution-problem"><strong>Problem solved:</strong> growing team and personal files spent prompt space on history unrelated to the task.</p>
1140
+ <p>Migration 017 added scoped SQL records and FTS5 search. Retrieval now reranks task matches and builds a bounded <strong>Relevant Memory</strong> pack with provenance, then wraps it in an <span class="code-chip">UNTRUSTED-INPUT</span> fence.</p>
1141
+ <p class="evolution-current"><strong>Current default:</strong> retrieval is enabled with shadow mode off and may select up to 8 records within 8,192 bytes; pinned context remains first.</p>
1142
+ </article>
1143
+ </li>
1144
+
1145
+ <li class="evolution-stage" data-memory-era="current">
1146
+ <article class="evolution-card" aria-labelledby="evolution-current-title">
1147
+ <div class="evolution-meta">
1148
+ <span class="evolution-index" aria-hidden="true">04</span>
1149
+ <time class="evolution-date" datetime="2026-07">July 2026 to now</time>
1150
+ </div>
1151
+ <h3 id="evolution-current-title">Outcome capture and fallback</h3>
1152
+ <p class="evolution-problem"><strong>Problem solved:</strong> authored notes did not consistently preserve the normalized result of every task.</p>
1153
+ <p><strong>Opt-in</strong> <span class="code-chip">memoryEpisodicCapture</span> stores compact success, partial, or failure outcomes in SQL instead of intentionally collecting transcripts or chain-of-thought. It remains off by default; without a structured summary, a redacted, truncated stdout fallback can include transcript-like text.</p>
1154
+ <p class="evolution-current"><strong>Legacy fallback:</strong> when retrieval is off, shadowed, empty, or errors, prompt assembly uses bounded <span class="code-chip">notes.md</span> plus the assigned agent's notebook. Episode capture does not replace either file.</p>
1155
+ </article>
1156
+ </li>
1157
+ </ol>
1158
+ <p class="evolution-source">Milestone months follow the commits that introduced each capability; behavior labels describe the current implementation.</p>
1159
+ </section>
1160
+
990
1161
  <section class="section" id="memory-journey" aria-labelledby="journey-heading">
991
1162
  <div class="section-heading">
992
1163
  <p class="eyebrow">End-to-end data flow</p>
@@ -18,15 +18,15 @@ The default `worktree` mode spawns each dispatch inside its own git worktree und
18
18
 
19
19
  For these repos, the operator usually already has one canonical checkout that builds correctly. Live-checkout mode lets the agent dispatch into that checkout instead of cloning a side-by-side copy.
20
20
 
21
- ## Contract (six guarantees)
21
+ ## Contract (seven guarantees)
22
22
 
23
- Live mode is opinionated about what the engine will and will not do to the operator's tree. The contract has six guarantees; the engine enforces them by recovering when explicitly configured, otherwise leaving blocked work pending or failing closed.
23
+ Live mode is opinionated about what the engine will and will not do to the operator's tree. The contract has seven guarantees; the engine enforces them by recovering when explicitly configured, otherwise leaving blocked work pending or failing closed.
24
24
 
25
25
  ### 1. Per-project mutating-concurrency cap of 1
26
26
 
27
27
  Only one worktree-requiring dispatch (fix / build-fix-complex / implement / review / test / verify / decompose / setup / docs) runs per live-mode project at a time. Read-only types (meeting / ask / explore / plan / plan-to-prd) are excluded from the cap because they never write to disk.
28
28
 
29
- Implementation: `engine.js` builds a `liveProjectsInUse` set from the active dispatch list at every allocation pass and per-tick re-annotation; pending items whose project is already in the set are skipped with a `skipReason` and re-tried on the next tick. (`engine.js:7198`, `engine.js:7460`.)
29
+ Implementation: `engine.js` builds a `liveProjectsInUse` set from the active dispatch list at every allocation pass and per-tick re-annotation; pending items whose project is already in the set are skipped with a `skipReason` and re-tried on the next tick.
30
30
 
31
31
  ### 2. Dirty-tree policy
32
32
 
@@ -88,7 +88,7 @@ After the clean-tree check, `prepareLiveCheckout`:
88
88
  - Succeeds → branch exists locally → plain `git checkout <branchName>` (no `--force`, no `-B`, no pull, no reset). The operator owns the checkout state; if they have local commits or a stale tip relative to `origin/<branchName>`, those are preserved.
89
89
  - Fails → `git checkout -b <branchName>` (new branch from the **current HEAD**, not `origin/<mainRef>`).
90
90
 
91
- **No `git fetch`, branch from HEAD (issue #226).** Live mode does **not** run a pre-checkout `git fetch origin <mainRef>` and does **not** seed new branches from `origin/<mainRef>`. The operator's checked-out HEAD is the baseline in live mode (the agent works in-place in the existing checkout), so a fetch only adds latency and failure surface. Concretely, on a Scalar/GVFS-managed ADO repo (blobless partial clone), both `git fetch origin <mainRef>` and `git checkout -b <branch> origin/<mainRef>` trigger on-demand blob fetching through a GVFS cache server (`*.gvfscache.dev.azure.com`) that receives no auth in headless mode (`credential.helper` is disabled), so they fail every time. Branching from HEAD avoids the remote-ref dependency entirely. `mainRef` is still accepted and validated for caller-contract stability but is no longer used to seed the branch in live mode. **Worktree (isolated) mode is unaffected** — that path still branches from `origin/<mainRef>` for a clean baseline.
91
+ **No implicit `git fetch`; branch from HEAD (issue #226).** By default, live mode does **not** run a pre-checkout `git fetch origin <mainRef>` and does **not** seed new branches from `origin/<mainRef>`; the only fetch exception is the explicit, safe [`liveCheckoutAutoFreshen`](#6b-opt-in-auto-freshen-livecheckoutautofreshen-w-mrdlcud2000e630e) opt-in. The operator's checked-out HEAD is otherwise the baseline. On a Scalar/GVFS-managed ADO repo, both fetch and `git checkout -b <branch> origin/<mainRef>` can trigger unauthenticated on-demand blob fetching, so branching from HEAD avoids that dependency. `mainRef` remains validated but does not seed the branch. **Worktree mode is unaffected** — it still branches from `origin/<mainRef>`.
92
92
 
93
93
  PR-source live dispatches (`meta.branch` set) and shared-branch live dispatches (`meta.branchStrategy === 'shared-branch'`) flow through the same code path with no special case. If the operator already has a PR branch checked out at a different SHA than `origin/<branch>`, the engine will not fast-forward it — that is the intended contract (see Open Q5 in the source PRD). Resolve manually with `git pull --ff-only` or commit the local divergence first.
94
94
 
@@ -121,7 +121,7 @@ Like the blob-fetch case this is **structural, not transient** — it does *not*
121
121
 
122
122
  Live mode shares one checkout per project. There is no pool to recycle, no quarantine directory, no per-WI subdirectory under the project root. The mutating-concurrency cap (Guarantee 1) is the only isolation mechanism: agents take turns in the same directory.
123
123
 
124
- Pool short-circuits live in `engine.js:1368` and `engine/cleanup.js`; both gate on `worktreePath` truthiness and `!liveMode`, so the borrow / return / orphan-GC paths execute zero git commands when the project is live.
124
+ Pool short-circuits in `engine.js` and `engine/cleanup.js` gate on `worktreePath` truthiness and `!liveMode`, so borrow / return / orphan-GC execute zero git commands for live projects.
125
125
 
126
126
  ### 5. Refuse on mid-operation / detached HEAD (engine never aborts the operator's in-progress op)
127
127
 
@@ -225,7 +225,7 @@ Live-mode agents run **in-place** in the operator's checkout, so when a dispatch
225
225
  - **Fallback notify (only when a safe switch is impossible).** If git declines the plain checkout — most likely because the agent left uncommitted changes a checkout would overwrite — the refusal is **honored**: the tree is left exactly as the agent left it and a deduped `live-checkout-branch-<dispatchId>` inbox alert tells the operator how to switch back manually (`git -C <localPath> checkout <originalRef>`). The engine never forces the switch. An **unexpected** restore error (git missing, repo corruption, a GVFS blob fetch on the switch-back) now also writes this alert, so a non-refusal failure never silently strands the tree.
226
226
  - **Terminal-failure alert.** When the dispatch ends in a non-success terminal state, a deduped `live-checkout-failed-<dispatchId>` inbox alert is written so the operator knows a live-mode run failed inside their own checkout (where any partial work is visible). This is independent of the restore and fires even when the restore itself succeeds.
227
227
 
228
- The core invariant holds end-to-end through restore: **the engine only ever switches branches it never `git reset`s, `git clean`s, or `git stash`es the operator's tree, and never passes `--force`.** The one addition — the self-healing auto-commit only ever runs `git add`/`git commit` on the engine's *own* agent branch, never on operator refs, and never discards.
228
+ **Restore itself** only switches branches and may auto-commit WIP on the engine's agent branch; it never resets, cleans, stashes, or passes `--force`. The configured pre-spawn auto-stash/auto-reset policies are the explicit exceptions described under Guarantee 2.
229
229
 
230
230
  ## Create-PR chip safety (CC-initiated, separate from the dispatch path)
231
231
 
@@ -290,6 +290,10 @@ Hybrid mode routes only selected canonical work-item types on the live checkout;
290
290
 
291
291
  `type` may be a string or array, but every entry must be a live-capable canonical `WORK_TYPE`. `build-and-test` is a playbook alias and is normalized to `test` only for backward compatibility. `autoDispatch` must be boolean and requires `test` in the configured types; the generated WI has type `test`, carries structured `targetPr` / `pr_id` / URL-reference fields, targets the source PR branch, and renders the `build-and-test` playbook. Successful direct PR-backed `fix` and `build-fix-complex` dispatches, including human-feedback fixes, use the dispatch ID as their dedup key and do not add a nonexistent WI dependency.
292
292
 
293
+ #### Automatic validation deduplication
294
+
295
+ `autoDispatchLiveValidationWi` keeps at most one non-terminal validation for a source WI and for a canonical PR revision across the central and project SQL scopes. Generated WIs persist `meta.liveValidationPrId` and, when known, `meta.liveValidationHeadSha`; another completion for the same PR and head is suppressed, while a new head may create a fresh validation. Fix completions prefer verified remote-head evidence and otherwise use the tracked PR head. Legacy validation WIs without a stored head conservatively block duplicates until terminal. The comparison and insert run in one work-item-store transaction.
296
+
293
297
  ### Enabling auto-stash on dirty (config.json)
294
298
 
295
299
  ```jsonc
@@ -375,6 +379,7 @@ Live-checkout mode is deliberately small. These are NOT supported and will not b
375
379
  | `engine.js` — `onAgentClose` live-mode restore wiring | Calls `restoreLiveCheckoutAtDispatchEnd` on every terminal result (P-d9e6b2c4). |
376
380
  | `engine/cli.js` — orphan / reattach restore | Fires the dispatch-end restore (via `maybeRestoreLiveCheckoutFromRecord`) from the persisted record on the engine-restart completion path (P-d9e6b2c4; unified PL-live-checkout-reliability-hardening). |
377
381
  | `engine/timeout.js` — `completeFromOutput` + orphan-sweep restore | Fires `maybeRestoreLiveCheckoutFromRecord` when a re-attached live dispatch is reaped post-restart — closes the gap where these paths had no restore wiring (PL-live-checkout-reliability-hardening). |
382
+ | `engine/lifecycle.js` — `autoDispatchLiveValidationWi` | Creates PR-targeted validation WIs and transactionally deduplicates them by source WI plus canonical PR identity/head across central and project scopes. |
378
383
  | `engine.js` — dispatcher `liveProjectsInUse` set | Per-project mutating-concurrency cap (P-a3f9b205). |
379
384
  | `engine.js` — worktree-pool / orphan-GC short-circuits | `worktreePath===null` no-ops in live mode (P-a3f9b206). |
380
385
  | `engine/cleanup.js` — `runPeriodicWorktreeSweep` live filter | Excludes live-checkout projects from the registry-derived periodic worktree GC so the operator's primary checkout never enters the GC decision surface (PL-live-checkout-reliability-hardening). |
@@ -9,10 +9,10 @@ All dispatch decisions happen inside `engine.js#discoverFromPrs` once per discov
9
9
  | # | Cause | Trigger (PR state) | Per-cause gate flag | Hard-stop kill-switch | Other gates |
10
10
  |---|------------------------|---------------------------------------------------|----------------------------|-----------------------|-----------------------------------------------------------|
11
11
  | 1 | Initial minion review | `reviewStatus === 'pending'` | `autoReviewPrs` | `autoFixPaused`* | `evalLoop`; provider polling enabled |
12
- | 2 | Human-feedback fix | `humanFeedback.pendingFix` (or coalesced) | `autoFixHumanComments` | `autoFixPaused` | provider polling enabled; `!awaitingReReview` |
12
+ | 2 | Human-feedback fix | `humanFeedback.pendingFix || hasCoalescedFeedback` | `autoFixHumanComments` | `autoFixPaused` | provider polling enabled; `!fixDispatched` |
13
13
  | 3 | Minion re-review | `reviewStatus === 'waiting'` after fix push | `autoReReviewPrs` | `autoFixPaused`* | `evalLoop`; provider polling enabled |
14
- | 4 | Minion review-feedback fix | `reviewStatus === 'changes-requested'` | `autoFixReviewFeedback` | `autoFixPaused` | `evalLoop`; provider polling enabled; `!awaitingReReview` |
15
- | 5 | Build-failure fix | `buildStatus === 'failing'` + grace expired | `autoFixBuilds` | `autoFixPaused` | provider polling enabled; `_buildFixPushedAt` grace |
14
+ | 4 | Minion review-feedback fix | `reviewStatus === 'changes-requested'` | `autoFixReviewFeedback` | `autoFixPaused` | `evalLoop`; polling; `!awaitingReReview`; `!fixDispatched` |
15
+ | 5 | Build-failure fix | `buildStatus === 'failing'` + grace expired | `autoFixBuilds` | `autoFixPaused` | polling; `_buildFixPushedAt` grace; `!fixDispatched` |
16
16
  | 6 | Merge-conflict fix | `_mergeConflict === true` + `status === 'active'` | `autoFixConflicts` | `autoFixPaused` | provider polling enabled; `!fixDispatched` in same pass |
17
17
 
18
18
  \* `autoFixPaused` is the auto-**fix** kill-switch. It forces sites 2, 4, 5, and 6 off, but is **not** wired to suppress review dispatch (sites 1 and 3) — review verdicts stay fresh during a fix-storm incident. To halt review dispatch, clear `autoReviewPrs` / `autoReReviewPrs` directly, or set `pollingPaused: true` (which forces every per-PR auto-dispatch gate inert by zeroing `pollEnabled`).
@@ -29,19 +29,19 @@ A successful review dispatch that posts no `VERDICT:` comment and is not a recog
29
29
  `discoverFromPrs()` evaluates PR review/fix triggers in a fixed order during each discovery pass:
30
30
 
31
31
  1. Initial minion review (`reviewStatus === 'pending'`)
32
- 2. Human feedback (`humanFeedback.pendingFix` or coalesced feedback)
32
+ 2. Human feedback (`humanFeedback.pendingFix` or `hasCoalescedFeedback`)
33
33
  3. Minion re-review (`reviewStatus === 'waiting'` after a fix)
34
34
  4. Minion review feedback (`changes-requested`)
35
35
  5. Build failure (`buildStatus === 'failing'`)
36
36
  6. Merge conflict (`_mergeConflict`)
37
37
 
38
- When multiple problems coexist, earlier triggers get the first chance to enqueue work. The local `fixDispatched` flag is declared before the initial review trigger and set after first-review, human-feedback, review-feedback, and build-failure dispatches. Conflict fixes run last and explicitly require `!fixDispatched`, so any earlier successful review/fix dispatch suppresses the conflict fix for that PR in the same discovery pass. Build fixes are evaluated after human and minion review feedback, but the build-fix condition itself is not gated by `!fixDispatched`.
38
+ When multiple problems coexist, earlier triggers get the first chance to enqueue work. The local `fixDispatched` flag is declared before the initial review trigger and set after first-review, human-feedback, review-feedback, and build-failure dispatches. Each later trigger checks `!fixDispatched`, so those sites suppress all subsequent review/fix work for that PR in the same discovery pass. Re-review is the exception: it checks `!fixDispatched` before enqueueing but does not set the flag afterward. Consequently, a re-review may enqueue alongside a later build-failure fix or, when no build fix is queued, a merge-conflict fix in the same pass.
39
39
 
40
40
  The engine does not cap review→fix cycles or build-fix attempts. Each trigger evaluates its own gates on every discovery pass; loops stop only when the underlying condition clears (reviewer approves, build passes, conflict resolves, human feedback handled). Operators who need to halt automation on a runaway PR have three escalating tools: (1) clear the relevant per-cause flag (`evalLoop`, `autoFixBuilds`, `autoFixConflicts`, `autoFixHumanComments`, `autoFixReviewFeedback`, `autoReviewPrs`, `autoReReviewPrs`); (2) flip the `autoFixPaused` master kill-switch to halt every auto-**fix** dispatch (sites B, C, D, and human-comment fix) at once while leaving review verdicts and polling fresh — Dashboard → Settings → Auto-fix & Review Loop or `POST /api/engine/auto-fix/pause`; (3) flip `pollingPaused` to halt PR polling entirely, which also forces every per-PR auto-dispatch gate inert. Per-PR per-cause noop pauses (`_noOpFixes[cause].paused`) are an orthogonal recovery surface — see §4E and `POST /api/pull-requests/clear-paused-cause`. See [docs/pr-auto-fix-dispatch.md](pr-auto-fix-dispatch.md) for the full site × gate × kill-switch reference table.
41
41
 
42
42
  ### A. Human comments (`humanFeedback.pendingFix`)
43
43
 
44
- - Gate: `pendingFix || coalescedFeedback` + not already dispatched/on cooldown
44
+ - Gate: `pendingFix || hasCoalescedFeedback` + not already dispatched/on cooldown
45
45
  - Agent comments filtered out via `_isNonActionableComment()` (`engine/github.js`, `engine/ado.js`) — composes preview/bot body checks and the structural Minions-author check from `engine/comment-classifier.js`. The structural check matches the `<!-- minions:agent=<id> kind=<kind> -->` HTML marker on the body's first line (`hasMinionsMarker`); on GitHub it must combine with `viewerDidAuthor === true` (anti-spoof), on ADO with a `config.engine.minionsAdoIdentities` author match. Marker presence alone never classifies a comment as agent-authored on either platform.
46
46
  - Coalesces multiple comments arriving during cooldown into single fix
47
47
  - Routes to author
@@ -91,8 +91,8 @@ A "branch unchanged" no-op report for a `BUILD_FAILURE`-cause fix used to be con
91
91
  ## 5. Fix completes
92
92
 
93
93
  - `updatePrAfterFix()` (lifecycle.js) sets `reviewStatus = 'waiting'` + `fixedAt = ts()`
94
- - Clears `humanFeedback.pendingFix`
95
- - `awaitingReReview` gate (`waiting` + `fixedAt`) blocks all fix dispatch until reviewer acts
94
+ - Clears `humanFeedback.pendingFix` only when it still identifies the completed dispatch's comment; newer feedback is preserved
95
+ - `awaitingReReview` (`waiting` + `fixedAt`) suppresses stale review-feedback fixes; fresh human feedback and independent build/conflict causes retain their own gates
96
96
 
97
97
  ## 6. Re-review cycle
98
98
 
@@ -115,7 +115,7 @@ A "branch unchanged" no-op report for a `BUILD_FAILURE`-cause fix used to be con
115
115
  | Scenario | Guard |
116
116
  |---|---|
117
117
  | Simultaneous review + fix | `activePrIds` — skip PR if any dispatch in-flight |
118
- | Duplicate fix (human/review/build + conflict) | `fixDispatched` flag conflict triggers skip after earlier fix dispatches in the same PR pass |
118
+ | Same-pass review/fix fan-out | `fixDispatched` — initial review and successful fix sites suppress later work; re-review checks but does not set the flag, so it can co-dispatch with one later build/conflict fix |
119
119
  | Branch write conflict | `isBranchActive()` mutex |
120
120
  | Fix while awaiting re-review | `awaitingReReview` (waiting + fixedAt) |
121
121
  | Build fix before CI runs | `_buildFixPushedAt` grace period (10min) |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.2422",
3
+ "version": "0.1.2424",
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"