@yemi33/minions 0.1.2366 → 0.1.2368
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/docs/README.md +2 -0
- package/docs/auto-discovery.md +9 -6
- package/engine/lifecycle.js +12 -0
- package/engine.js +43 -7
- package/package.json +1 -1
package/docs/README.md
CHANGED
|
@@ -7,6 +7,7 @@ A navigable index of every Markdown file under `docs/`. Entries are grouped by a
|
|
|
7
7
|
Hands-on stories and distribution guides for people running or evaluating Minions.
|
|
8
8
|
|
|
9
9
|
- [blog-first-successful-dispatch.md](blog-first-successful-dispatch.md) — Narrative walkthrough of the first end-to-end agent dispatch and the seven failed spawn attempts that preceded it.
|
|
10
|
+
- [architecture-review-2026-07-09.md](architecture-review-2026-07-09.md) — Architecture review covering engine orchestration, SQL/JSON state boundaries, runtime/process recovery, worktree lifecycle, pipelines/schedules, and prompt assembly, verified against `origin/main` at `b1b83dd2`.
|
|
10
11
|
- [documentation-audit-2026-07-09.md](documentation-audit-2026-07-09.md) — Verified user-facing documentation audit covering runtime, CLI, storage, routing, and broken-link corrections.
|
|
11
12
|
- [distribution.md](distribution.md) — How Minions is published (this repo: `@opg-microsoft/minions` to GitHub Packages; paired peer `@yemi33/minions` to npm) and the bidirectional sync contract — automated opg → yemi33 backport workflow + manual yemi33 → opg sync PRs.
|
|
12
13
|
- [onboarding.md](onboarding.md) — Condensed first-30-minutes walkthrough for a new operator.
|
|
@@ -46,6 +47,7 @@ Architecture, design proposals, and lifecycle references for people working on t
|
|
|
46
47
|
- [harness-transparency.md](harness-transparency.md) — The `harnessUsed` self-report contract: capture (agent reports the skills / MCPs / commands / docs it used) → ground (engine cross-checks against `_harnessPropagated` and annotates `grounded:true\|false`, never dropping) → surface (PR comment, final agent note, work-item modal).
|
|
47
48
|
- [kb-dedup-duplicate-pair-investigation.md](kb-dedup-duplicate-pair-investigation.md) — Investigation-only pass diffing suspected duplicate KB filename groups (content-hash dedup for agent-authored reviews/build-reports) to confirm true full-body duplication before any fix is proposed.
|
|
48
49
|
- [kb-pr3223-cascade-archiving.md](kb-pr3223-cascade-archiving.md) — Findings from PR #3223: cascade-archiving PRDs when their linked markdown plan is archived, including the per-concern try/catch pattern and `source_plan` basename-matching invariant in `handlePlansArchive` (`dashboard.js`).
|
|
50
|
+
- [kb-pr696-merge-conflict-docs.md](kb-pr696-merge-conflict-docs.md) — Findings from PR #696: resolving a merge conflict in `docs/kb-sweep.md` with stale line-number citations to `engine/kb-sweep.js`.
|
|
49
51
|
- [kb-sweep.md](kb-sweep.md) — Knowledge-base consolidation sweep (hash dedup → LLM batch dedup/reclassify → per-entry compress) and the detached runner that keeps it alive across `minions restart`.
|
|
50
52
|
- [keep-processes.md](keep-processes.md) — `meta.keep_processes` sidecar contract: when to use it vs managed-spawn, sidecar schema, caps, and the [`engine/keep-process-sweep.js`](../engine/keep-process-sweep.js) lifecycle.
|
|
51
53
|
- [live-checkout-mode.md](live-checkout-mode.md) — Per-project opt-in `checkoutMode: 'live'`: skips `git worktree add` and dispatches in-place inside `project.localPath` for `repo`-managed trees, submodule-heavy repos, deep Windows paths, and native build state. Includes the refuse-on-dirty contract and the per-project mutating-concurrency cap of 1.
|
package/docs/auto-discovery.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Auto-Discovery & Execution Pipeline
|
|
2
2
|
|
|
3
|
-
> Last verified: 2026-
|
|
3
|
+
> Last verified: 2026-07-10 against `engine.js` `tickInner()` and `routing.md`.
|
|
4
4
|
|
|
5
5
|
How the minions engine finds work and dispatches agents automatically.
|
|
6
6
|
|
|
@@ -28,16 +28,19 @@ tick()
|
|
|
28
28
|
checkPlanCompletion() Mark plans completed when all features done/in-pr
|
|
29
29
|
2.7 pollPrHumanComments() Poll PR threads for human comments (wall-clock cadence from prPollCommentsEvery × tickInterval, default ≈ 12min)
|
|
30
30
|
reconcilePrs() (ADO+GH) Reconciliation sweep (runs regardless of poll flags)
|
|
31
|
+
reconcileSharedBranchPrs() Backfill the PR store for shared-branch plans whose aggregate remote PR is untracked/under-linked (runs unconditionally, like the other recovery sweeps)
|
|
31
32
|
2.9 stallRecovery() Auto-retry failed items blocking pending deps (every 120 ticks)
|
|
32
33
|
3a. pruneStalePrDispatches() Clear pending PR dispatches whose underlying PRs no longer warrant action
|
|
33
|
-
3.
|
|
34
|
-
4.
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
34
|
+
3. dispatch existing pending Spawn agents for items already durable in dispatch.pending (up to maxConcurrent) — runs BEFORE discovery so a slow pre-dispatch validator can't starve already-queued work
|
|
35
|
+
4. discoverWork() Scan ALL linked projects for new tasks
|
|
36
|
+
5. updateSnapshot() Write identity/now.md
|
|
37
|
+
5a. memoryBaseline() Sample RSS/heap/GC every memoryBaselineEveryTicks ticks (default 6 ≈ 60s)
|
|
38
|
+
5b. heapSnapshotRequest() Serve a pending guard-token heap-snapshot request, if any
|
|
38
39
|
```
|
|
39
40
|
|
|
40
41
|
> When `control.state === 'stopping'`, only steps 1–1c run; discovery and dispatch are skipped while live agents drain.
|
|
42
|
+
>
|
|
43
|
+
> Dispatch of already-pending work runs before `discoverWork()`, not after (`engine.js` comment: "Process pending dispatches before discovery. discoverWork() runs pre-dispatch LLM validation for newly found work; a slow validator must not starve runnable entries that are already durable in dispatch.pending.").
|
|
41
44
|
|
|
42
45
|
## Work Discovery
|
|
43
46
|
|
package/engine/lifecycle.js
CHANGED
|
@@ -2593,6 +2593,18 @@ async function updatePrAfterReview(agentId, pr, project, config, resultSummary,
|
|
|
2593
2593
|
}
|
|
2594
2594
|
}
|
|
2595
2595
|
target.lastReviewedAt = ts();
|
|
2596
|
+
// W-mrej8p740006b0bf — SHA-based review freshness. Persist the exact head
|
|
2597
|
+
// SHA the review verdict actually evaluated instead of leaning on
|
|
2598
|
+
// lastReviewedAt/lastPushedAt ordering, which flips "already reviewed"
|
|
2599
|
+
// true purely because the verdict was *recorded* after a mid-review push
|
|
2600
|
+
// landed — even though the verdict evaluated the prior head. Prefer the
|
|
2601
|
+
// SHA captured at dispatch time (meta.reviewHeadSha, stamped by
|
|
2602
|
+
// engine.js's review/re-review dispatch builders); fall back to the
|
|
2603
|
+
// current live head for older dispatches that predate that capture (best
|
|
2604
|
+
// effort — matches the previous timestamp-only behavior for those).
|
|
2605
|
+
const dispatchReviewedHeadSha = String(dispatchItem?.meta?.reviewHeadSha || '').trim();
|
|
2606
|
+
const liveHeadShaAtCompletion = getPrFixBaselineHead(target);
|
|
2607
|
+
target.reviewedHeadSha = dispatchReviewedHeadSha || liveHeadShaAtCompletion || target.reviewedHeadSha || '';
|
|
2596
2608
|
// A verdict landed cleanly this time — clear any stale missing-verdict flag.
|
|
2597
2609
|
delete target._reviewVerdictMissing;
|
|
2598
2610
|
delete target._reviewVerdictMissingCount;
|
package/engine.js
CHANGED
|
@@ -7378,6 +7378,42 @@ function getPrCauseBase(pr) {
|
|
|
7378
7378
|
|| '';
|
|
7379
7379
|
}
|
|
7380
7380
|
|
|
7381
|
+
// W-mrej8p740006b0bf — SHA-based review freshness. A review is only
|
|
7382
|
+
// considered to cover the PR's CURRENT head when the recorded
|
|
7383
|
+
// `reviewedHeadSha` (the exact SHA the completed review verdict evaluated —
|
|
7384
|
+
// stamped by lifecycle.js#updatePrAfterReview from the dispatch's captured
|
|
7385
|
+
// `meta.reviewHeadSha`) matches the live head. This replaces the previous
|
|
7386
|
+
// `pr.lastPushedAt <= pr.lastReviewedAt` timestamp-ordering check, which
|
|
7387
|
+
// flips true purely because the verdict was *persisted* after a mid-review
|
|
7388
|
+
// push landed — even though the verdict evaluated the prior head (root cause
|
|
7389
|
+
// of github:opg-microsoft/minions#821: a commit pushed while a review was in
|
|
7390
|
+
// flight silently suppressed automatic re-review). Falls back to the legacy
|
|
7391
|
+
// timestamp comparison for PR records that predate `reviewedHeadSha`
|
|
7392
|
+
// capture, so existing open PRs don't all spuriously need re-review the
|
|
7393
|
+
// moment this ships.
|
|
7394
|
+
function isPrReviewCurrent(pr) {
|
|
7395
|
+
if (pr?.reviewedHeadSha) {
|
|
7396
|
+
const currentHead = getPrCauseHead(pr);
|
|
7397
|
+
return !!currentHead && pr.reviewedHeadSha === currentHead;
|
|
7398
|
+
}
|
|
7399
|
+
return !!(pr?.lastReviewedAt && (!pr?.lastPushedAt || pr.lastPushedAt <= pr.lastReviewedAt));
|
|
7400
|
+
}
|
|
7401
|
+
|
|
7402
|
+
// Same SHA-freshness signal applied to the WAITING → re-review gate. A fix
|
|
7403
|
+
// is "reviewed-stale" when the tracked `reviewedHeadSha` no longer matches
|
|
7404
|
+
// the live head — checked in addition to (not instead of) the existing
|
|
7405
|
+
// `fixedAt` vs `lastReviewedAt` timestamp comparison, so a mid-review push
|
|
7406
|
+
// during a re-review dispatch is caught the same way as the first-review
|
|
7407
|
+
// race above.
|
|
7408
|
+
function isPrFixedAfterReview(pr) {
|
|
7409
|
+
const timestampSaysFixed = !!(pr?.minionsReview?.fixedAt &&
|
|
7410
|
+
(!pr?.lastReviewedAt || pr.minionsReview.fixedAt > pr.lastReviewedAt));
|
|
7411
|
+
if (timestampSaysFixed) return true;
|
|
7412
|
+
if (!pr?.reviewedHeadSha) return false;
|
|
7413
|
+
const currentHead = getPrCauseHead(pr);
|
|
7414
|
+
return !!currentHead && pr.reviewedHeadSha !== currentHead;
|
|
7415
|
+
}
|
|
7416
|
+
|
|
7381
7417
|
function getPrAutomationCauseKey(kind, pr) {
|
|
7382
7418
|
if (kind === 'review-feedback') {
|
|
7383
7419
|
const reviewRef = pr?.minionsReview?.dispatchId
|
|
@@ -7648,7 +7684,7 @@ async function discoverFromPrs(config, project) {
|
|
|
7648
7684
|
// PRs needing review: evalLoop gates the entire review+fix cycle; pollEnabled ensures reviewStatus is fresh
|
|
7649
7685
|
const reviewEnabled = evalLoopEnabled && pollEnabled && autoReviewPrs;
|
|
7650
7686
|
const reReviewEnabled = evalLoopEnabled && pollEnabled && autoReReviewPrs;
|
|
7651
|
-
const alreadyReviewed =
|
|
7687
|
+
const alreadyReviewed = isPrReviewCurrent(pr);
|
|
7652
7688
|
const needsReview = reviewEnabled && reviewStatus === REVIEW_STATUS.PENDING && !alreadyReviewed;
|
|
7653
7689
|
if (needsReview) {
|
|
7654
7690
|
const key = getPrReviewCooldownKey('review', project, pr, prDisplayId);
|
|
@@ -7694,7 +7730,7 @@ async function discoverFromPrs(config, project) {
|
|
|
7694
7730
|
pr_author: pr.agent || '', pr_url: pr.url || '',
|
|
7695
7731
|
}, `Review ${pr.id}: ${pr.title}`, {
|
|
7696
7732
|
dispatchKey: key, source: 'pr', pr, branch: prBranch, project: projMeta,
|
|
7697
|
-
deferReviewerResolution: true,
|
|
7733
|
+
deferReviewerResolution: true, reviewHeadSha: getPrCauseHead(pr),
|
|
7698
7734
|
});
|
|
7699
7735
|
if (deferred) {
|
|
7700
7736
|
deferred._pendingReason = 'no_non_author_reviewer';
|
|
@@ -7707,7 +7743,7 @@ async function discoverFromPrs(config, project) {
|
|
|
7707
7743
|
const item = buildPrDispatch(agentId, config, project, pr, 'review', {
|
|
7708
7744
|
pr_id: pr.id, pr_number: prNumber, pr_title: pr.title || '', pr_branch: prBranch,
|
|
7709
7745
|
pr_author: pr.agent || '', pr_url: pr.url || '',
|
|
7710
|
-
}, `Review ${pr.id}: ${pr.title}`, { dispatchKey: key, cooldownKey: key, source: 'pr', pr, branch: prBranch, project: projMeta });
|
|
7746
|
+
}, `Review ${pr.id}: ${pr.title}`, { dispatchKey: key, cooldownKey: key, source: 'pr', pr, branch: prBranch, project: projMeta, reviewHeadSha: getPrCauseHead(pr) });
|
|
7711
7747
|
if (item) { newWork.push(item); fixDispatched = true; }
|
|
7712
7748
|
}
|
|
7713
7749
|
|
|
@@ -7861,8 +7897,7 @@ async function discoverFromPrs(config, project) {
|
|
|
7861
7897
|
|
|
7862
7898
|
// Re-review after fix: trigger when a fix was pushed after the last minions review,
|
|
7863
7899
|
// or when no minions review has completed yet (e.g. human-feedback-only fix path).
|
|
7864
|
-
const fixedAfterReview =
|
|
7865
|
-
(!pr.lastReviewedAt || pr.minionsReview.fixedAt > pr.lastReviewedAt));
|
|
7900
|
+
const fixedAfterReview = isPrFixedAfterReview(pr);
|
|
7866
7901
|
const needsReReview = reReviewEnabled && reviewStatus === REVIEW_STATUS.WAITING &&
|
|
7867
7902
|
fixedAfterReview && !fixDispatched;
|
|
7868
7903
|
if (needsReReview) {
|
|
@@ -7909,7 +7944,7 @@ async function discoverFromPrs(config, project) {
|
|
|
7909
7944
|
pr_author: pr.agent || '', pr_url: pr.url || '',
|
|
7910
7945
|
}, `Review ${pr.id}: ${pr.title}`, {
|
|
7911
7946
|
dispatchKey: key, cooldownKey: key, source: 'pr', pr, branch: prBranch, project: projMeta,
|
|
7912
|
-
deferReviewerResolution: true,
|
|
7947
|
+
deferReviewerResolution: true, reviewHeadSha: getPrCauseHead(pr),
|
|
7913
7948
|
});
|
|
7914
7949
|
if (deferred) {
|
|
7915
7950
|
deferred._pendingReason = 'no_non_author_reviewer';
|
|
@@ -7922,7 +7957,7 @@ async function discoverFromPrs(config, project) {
|
|
|
7922
7957
|
const item = buildPrDispatch(agentId, config, project, pr, 'review', {
|
|
7923
7958
|
pr_id: pr.id, pr_number: prNumber, pr_title: pr.title || '', pr_branch: prBranch,
|
|
7924
7959
|
pr_author: pr.agent || '', pr_url: pr.url || '',
|
|
7925
|
-
}, `Review ${pr.id}: ${pr.title}`, { dispatchKey: key, cooldownKey: key, source: 'pr', pr, branch: prBranch, project: projMeta });
|
|
7960
|
+
}, `Review ${pr.id}: ${pr.title}`, { dispatchKey: key, cooldownKey: key, source: 'pr', pr, branch: prBranch, project: projMeta, reviewHeadSha: getPrCauseHead(pr) });
|
|
7926
7961
|
if (item) { newWork.push(item); }
|
|
7927
7962
|
}
|
|
7928
7963
|
|
|
@@ -11716,6 +11751,7 @@ module.exports = {
|
|
|
11716
11751
|
_isOutputTruncated, AGENT_OUTPUT_CAP_BYTES, // P-8e4c2a17: exported for testing (OUTPUT_TRUNCATED detection)
|
|
11717
11752
|
promoteCheckpointSteeringForClose, // exported for testing
|
|
11718
11753
|
normalizePrBranch, resolvePrBranch, prCausePart, getPrCauseHead, getPrCauseBase, getPrAutomationCauseKey, getPrAutomationDispatchKey, // exported for testing
|
|
11754
|
+
isPrReviewCurrent, isPrFixedAfterReview, // exported for testing (W-mrej8p740006b0bf — SHA-based review freshness)
|
|
11719
11755
|
ensurePrBranchForDispatch, isWithinLinkGraceWindow, PR_LINK_GRACE_WINDOW_MS, // exported for testing (W-mphm0kt0000cebc3)
|
|
11720
11756
|
|
|
11721
11757
|
// Playbooks
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2368",
|
|
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"
|