@yemi33/minions 0.1.2349 → 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.
- package/engine/kb-sweep.js +14 -2
- package/engine/live-checkout.js +80 -0
- package/package.json +1 -1
package/engine/kb-sweep.js
CHANGED
|
@@ -658,8 +658,14 @@ ${body}`;
|
|
|
658
658
|
return { processed, bytesBefore, bytesAfter };
|
|
659
659
|
}
|
|
660
660
|
|
|
661
|
-
function _applyLlmPlan(plan, manifest, opts = {}) {
|
|
661
|
+
function _applyLlmPlan(plan, manifest, changedIdx, opts = {}) {
|
|
662
662
|
let removed = 0, merged = 0, reclassified = 0;
|
|
663
|
+
// changedIdx is the set of manifest indices actually content-analyzed by
|
|
664
|
+
// the LLM this batch (see _llmBatchSweep's scannedIdx). Duplicate pairs
|
|
665
|
+
// referencing only unanalyzed indices (LLM hallucination / stale index
|
|
666
|
+
// mis-reference) must never be archived — a pair is only trustworthy if
|
|
667
|
+
// at least one side was actually analyzed this batch (W-mrb447th00062813).
|
|
668
|
+
const changedSet = new Set(changedIdx || []);
|
|
663
669
|
for (const r of (plan.remove || [])) {
|
|
664
670
|
const entry = manifest[r.index];
|
|
665
671
|
if (!entry) continue;
|
|
@@ -667,6 +673,11 @@ function _applyLlmPlan(plan, manifest, opts = {}) {
|
|
|
667
673
|
if (_archiveKbFile(path.join(KB_DIR, entry.category, entry.file), `stale: ${r.reason || ''}`)) removed++;
|
|
668
674
|
}
|
|
669
675
|
for (const d of (plan.duplicates || [])) {
|
|
676
|
+
const pairIdx = [d.keep, ...(d.remove || [])];
|
|
677
|
+
if (!pairIdx.some((idx) => changedSet.has(idx))) {
|
|
678
|
+
log('warn', `[kb-sweep] duplicate pair (keep=${d.keep}, remove=${JSON.stringify(d.remove)}) references no analyzed (changedIdx) entry — skipping archive to avoid acting on a hallucinated/stale LLM reference`);
|
|
679
|
+
continue;
|
|
680
|
+
}
|
|
670
681
|
for (const idx of (d.remove || [])) {
|
|
671
682
|
const entry = manifest[idx];
|
|
672
683
|
if (!entry) continue;
|
|
@@ -954,7 +965,7 @@ async function _runKbSweepImpl(opts = {}) {
|
|
|
954
965
|
// Only runs against survivors, but we need indices that match the LIST sent to the LLM
|
|
955
966
|
const llmManifest = afterConsolidate;
|
|
956
967
|
const { plan, scannedIdx: llmScannedIdx } = await _llmBatchSweep(llmManifest, callLLM, trackEngineUsage, opts);
|
|
957
|
-
const llmActions = _applyLlmPlan(plan, llmManifest, opts);
|
|
968
|
+
const llmActions = _applyLlmPlan(plan, llmManifest, llmScannedIdx, opts);
|
|
958
969
|
summary.llmDuplicatesArchived = llmActions.merged;
|
|
959
970
|
summary.staleRemoved = llmActions.removed;
|
|
960
971
|
summary.reclassified = llmActions.reclassified;
|
|
@@ -1170,6 +1181,7 @@ module.exports = {
|
|
|
1170
1181
|
_llmBatchSweep,
|
|
1171
1182
|
_classifyLlmScanFreshness,
|
|
1172
1183
|
_markLlmScanned,
|
|
1184
|
+
_applyLlmPlan,
|
|
1173
1185
|
LLM_SCAN_FLAG_KEY,
|
|
1174
1186
|
CONSOLIDATION_DIGEST_PREFIX,
|
|
1175
1187
|
COMPRESS_THRESHOLD_BYTES,
|
package/engine/live-checkout.js
CHANGED
|
@@ -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.
|
|
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"
|