@yemi33/minions 0.1.2369 → 0.1.2371

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.
Files changed (79) hide show
  1. package/bin/minions.js +38 -11
  2. package/dashboard/js/memory-search.js +112 -0
  3. package/dashboard/js/render-kb.js +15 -0
  4. package/dashboard/js/render-other.js +2 -30
  5. package/dashboard/js/render-work-items.js +0 -34
  6. package/dashboard/js/settings.js +27 -27
  7. package/dashboard/pages/inbox.html +1 -1
  8. package/dashboard/pages/tools.html +1 -1
  9. package/dashboard.js +148 -258
  10. package/docs/README.md +2 -3
  11. package/docs/architecture.excalidraw +2 -2
  12. package/docs/auto-discovery.md +3 -3
  13. package/docs/completion-reports.md +0 -121
  14. package/docs/copilot-cli-schema.md +9 -17
  15. package/docs/deprecated.json +0 -51
  16. package/docs/design-state-storage.md +4 -4
  17. package/docs/harness-propagation.md +33 -263
  18. package/docs/human-vs-automated.md +1 -1
  19. package/docs/named-agents.md +0 -2
  20. package/docs/plan-lifecycle.md +5 -6
  21. package/docs/qa-runbook-lifecycle.md +10 -25
  22. package/docs/shared-lifecycle-module-map.md +2 -10
  23. package/docs/team-memory.md +18 -4
  24. package/engine/ado-comment.js +5 -11
  25. package/engine/ado.js +10 -5
  26. package/engine/cleanup.js +16 -36
  27. package/engine/cli.js +13 -175
  28. package/engine/comment-format.js +8 -182
  29. package/engine/consolidation.js +72 -1
  30. package/engine/db/index.js +60 -10
  31. package/engine/db/migrations/017-agent-memory.js +208 -0
  32. package/engine/db/migrations/018-sql-only-cutover.js +56 -0
  33. package/engine/dispatch-store.js +0 -114
  34. package/engine/dispatch.js +1 -6
  35. package/engine/features.js +6 -0
  36. package/engine/gh-comment.js +2 -10
  37. package/engine/github.js +8 -6
  38. package/engine/lifecycle.js +141 -173
  39. package/engine/llm.js +9 -11
  40. package/engine/managed-spawn.js +1 -6
  41. package/engine/memory-retrieval.js +165 -0
  42. package/engine/memory-store.js +367 -0
  43. package/engine/metrics-store.js +4 -128
  44. package/engine/pipeline.js +4 -7
  45. package/engine/playbook.js +64 -198
  46. package/engine/prd-store.js +115 -141
  47. package/engine/preflight.js +8 -55
  48. package/engine/projects.js +34 -41
  49. package/engine/pull-requests-store.js +9 -234
  50. package/engine/qa-runs.js +4 -15
  51. package/engine/qa-sessions.js +5 -17
  52. package/engine/queries.js +23 -103
  53. package/engine/routing.js +1 -1
  54. package/engine/runtimes/claude.js +1 -2
  55. package/engine/runtimes/codex.js +0 -2
  56. package/engine/runtimes/copilot.js +1 -4
  57. package/engine/shared-branch-pr-reconcile.js +2 -5
  58. package/engine/shared.js +179 -533
  59. package/engine/small-state-store.js +12 -647
  60. package/engine/spawn-agent.js +5 -96
  61. package/engine/state-operations.js +166 -0
  62. package/engine/supervisor.js +0 -1
  63. package/engine/watch-actions.js +2 -3
  64. package/engine/watches-store.js +2 -128
  65. package/engine/work-items-store.js +3 -254
  66. package/engine.js +102 -334
  67. package/package.json +2 -2
  68. package/playbooks/build-fix-complex.md +0 -4
  69. package/playbooks/fix.md +0 -4
  70. package/playbooks/implement-shared.md +0 -4
  71. package/playbooks/implement.md +0 -4
  72. package/playbooks/plan-to-prd.md +16 -11
  73. package/playbooks/plan.md +0 -4
  74. package/playbooks/review.md +0 -6
  75. package/playbooks/shared-rules.md +0 -65
  76. package/docs/harness-transparency.md +0 -199
  77. package/docs/project-skills.md +0 -193
  78. package/engine/discover-project-skills.js +0 -673
  79. package/engine/playbook-intents.js +0 -76
@@ -2,8 +2,7 @@
2
2
  * engine/ado-comment.js — Azure DevOps mirror of engine/gh-comment.js.
3
3
  *
4
4
  * Posts a minions PR-comment thread to ADO with the same hidden marker, the
5
- * same collapsible "Harnesses used" section, and the same deterministic brand
6
- * link as the GitHub path — because all three are produced by the ONE neutral
5
+ * same deterministic brand link as the GitHub path — because both are produced by the ONE neutral
7
6
  * builder `engine/comment-format.js#buildMinionsCommentBody`. This module owns
8
7
  * only the ADO transport (REST `…/threads` POST) and ADO-specific input
9
8
  * validation (orgBase / project / repositoryId / prNumber).
@@ -13,7 +12,7 @@
13
12
  * unlinked on ADO (the GitHub backstop in buildMinionsCommentBody never ran).
14
13
  * Routing ADO comments through this poster gives ADO the same guarantees as
15
14
  * GitHub: marker present (so pollers classify the comment as agent-authored),
16
- * harness section folded in, brand trailer hyperlinked.
15
+ * brand trailer hyperlinked.
17
16
  *
18
17
  * Auth: a bearer token from `az account get-access-token` (via
19
18
  * engine/ado-token.js#acquireAdoToken), threaded as `Authorization: Bearer`.
@@ -34,7 +33,7 @@
34
33
  * changes) stay `active` so they block/notify the author.
35
34
  */
36
35
 
37
- const { buildMinionsCommentBody, buildSkippedSkillSection } = require('./comment-format');
36
+ const { buildMinionsCommentBody } = require('./comment-format');
38
37
  const { acquireAdoToken } = require('./ado-token');
39
38
 
40
39
  const ADO_API_VERSION = '7.1';
@@ -95,7 +94,7 @@ async function _defaultAcquireToken() {
95
94
  /**
96
95
  * Post a minions PR comment to an Azure DevOps pull request.
97
96
  *
98
- * Mirrors engine/gh-comment.js#postPrComment: builds the marked + harness +
97
+ * Mirrors engine/gh-comment.js#postPrComment: builds the marked +
99
98
  * brand-linked body via the shared builder, then POSTs a new active thread.
100
99
  *
101
100
  * @param {object} args
@@ -107,8 +106,6 @@ async function _defaultAcquireToken() {
107
106
  * @param {string} args.agentId minions agent id (marker)
108
107
  * @param {string} args.kind comment kind (marker)
109
108
  * @param {string} [args.workItemId] originating work-item id (marker)
110
- * @param {object} [args.harnessUsed] grounded harnessUsed record (folded into body)
111
- * @param {object} [args.skillSkipped] skipped-project-skill record `{name, reason}` (folded into body, visible)
112
109
  * @param {number} [args.timeoutMs=30000]
113
110
  * @param {Function} [args.acquireToken] () => Promise<string> — injectable token source
114
111
  * @param {Function} [args.fetchImpl=fetch] injectable fetch
@@ -123,8 +120,6 @@ async function postAdoPrComment({
123
120
  agentId,
124
121
  kind,
125
122
  workItemId,
126
- harnessUsed,
127
- skillSkipped,
128
123
  resolved = false,
129
124
  timeoutMs = 30000,
130
125
  acquireToken = _defaultAcquireToken,
@@ -136,7 +131,7 @@ async function postAdoPrComment({
136
131
  _validatePrNumber(prNumber);
137
132
 
138
133
  // buildMinionsCommentBody validates marker fields and throws on bad input.
139
- const finalBody = buildMinionsCommentBody({ agentId, kind, workItemId, body, harnessUsed, skillSkipped });
134
+ const finalBody = buildMinionsCommentBody({ agentId, kind, workItemId, body });
140
135
 
141
136
  const token = await acquireToken();
142
137
  if (!token || typeof token !== 'string') {
@@ -186,7 +181,6 @@ module.exports = {
186
181
  // Re-export the neutral builder so ADO callers have a single import surface,
187
182
  // mirroring engine/gh-comment.js.
188
183
  buildMinionsCommentBody,
189
- buildSkippedSkillSection,
190
184
  // Internal validators exported for tests.
191
185
  _validateOrgBase,
192
186
  _validateProject,
package/engine/ado.js CHANGED
@@ -409,11 +409,13 @@ function applyAdoPrMetadata(pr, prData) {
409
409
  updated = true;
410
410
  }
411
411
 
412
- // W-mpej044m00076d63: backfill `pr.created` from ADO's real creation
413
- // timestamp. lifecycle.js attach paths omit `created` so this poll is the
414
- // single source of truth for fresh records, and historical records missing
415
- // `created` get backfilled the next time they're polled. Idempotent.
416
- if (!pr.created && prData.creationDate) {
412
+ // W-mpej044m00076d63 / W-mrezh0yb0007f733: keep `pr.created` aligned with
413
+ // ADO's real creationDate. The platform is authoritative for when a PR was
414
+ // opened, so adopt its value whenever the stored one is missing, a legacy
415
+ // date-only stamp, or a link/attach-time `now()` default (the bug where
416
+ // manually-linked PRs showed a "created today" date). `shouldAdoptPlatformCreated`
417
+ // compares instants so equivalent representations don't churn. Idempotent.
418
+ if (shared.shouldAdoptPlatformCreated(pr.created, prData.creationDate)) {
417
419
  pr.created = prData.creationDate;
418
420
  updated = true;
419
421
  }
@@ -2606,6 +2608,9 @@ async function fetchAdoPrMetadata(prNum, adoOrg, adoProj, adoRepo, project) {
2606
2608
  description: pr.description || '',
2607
2609
  branch: stripRefsHeads(pr.sourceRefName),
2608
2610
  author: pr.createdBy?.displayName || '',
2611
+ // W-mrezh0yb0007f733 — surface ADO's real creationDate so manual-link paths
2612
+ // can seed `pr.created` with the true value instead of link-time now().
2613
+ created: pr.creationDate || '',
2609
2614
  };
2610
2615
  }
2611
2616
 
package/engine/cleanup.js CHANGED
@@ -1388,23 +1388,13 @@ async function runCleanup(config, verbose = false) {
1388
1388
  }
1389
1389
  // PRD items (missing_features[].status)
1390
1390
  try {
1391
- let prdDirEntries;
1392
- try {
1393
- prdDirEntries = fs.readdirSync(PRD_DIR);
1394
- } catch (e) {
1395
- log('warn', `migrate PRD statuses: failed to read ${PRD_DIR} — ${e.message}`);
1396
- prdDirEntries = [];
1397
- }
1398
- const prdFiles = prdDirEntries.filter(f => f.endsWith('.json'));
1391
+ const prdStore = require('./prd-store');
1392
+ const prdFiles = prdStore.listPrdRows().filter(row => !row.archived).map(row => row.filename);
1399
1393
  for (const pf of prdFiles) {
1400
1394
  const prdPath = path.join(PRD_DIR, pf);
1395
+ const prd = prdStore.readPrd(prdPath);
1401
1396
  let migrated = 0;
1402
- shared.withFileLock(`${prdPath}.lock`, () => {
1403
- // safeJsonNoRestore: PRDs are terminal artifacts. If the active PRD
1404
- // was archived but a stale .backup sidecar remains, never resurrect
1405
- // it during a routine migration sweep (W-mouptdh1000h9f39).
1406
- const prd = safeJsonNoRestore(prdPath);
1407
- if (!prd?.missing_features) return;
1397
+ if (prd?.missing_features) {
1408
1398
  for (const feat of prd.missing_features) {
1409
1399
  if (LEGACY_DONE_ALIASES.has(feat.status)) {
1410
1400
  feat.status = shared.WI_STATUS.DONE;
@@ -1414,8 +1404,8 @@ async function runCleanup(config, verbose = false) {
1414
1404
  migrated++;
1415
1405
  }
1416
1406
  }
1417
- if (migrated > 0) safeWrite(prdPath, prd);
1418
- });
1407
+ }
1408
+ if (migrated > 0) prdStore.writePrd(prdPath, prd);
1419
1409
  if (migrated > 0) {
1420
1410
  log('info', `Migrated ${migrated} legacy PRD item status(es) in ${pf}`);
1421
1411
  }
@@ -1425,36 +1415,27 @@ async function runCleanup(config, verbose = false) {
1425
1415
  // Reset orphaned PRD item statuses — dispatched/failed with no matching work item (#779)
1426
1416
  cleaned.orphanedPrdStatuses = 0;
1427
1417
  try {
1428
- const wiIds = new Set();
1429
- for (const project of projects) {
1430
- const items = safeJsonArr(projectWorkItemsPath(project));
1431
- for (const wi of items) { if (wi?.id) wiIds.add(wi.id); }
1432
- }
1433
- try {
1434
- const centralWi = safeJsonArr(path.join(MINIONS_DIR, 'work-items.json'));
1435
- for (const wi of centralWi) { if (wi?.id) wiIds.add(wi.id); }
1436
- } catch { /* optional */ }
1418
+ const wiIds = new Set(
1419
+ queries.getWorkItems(config, { enrich: false }).map(wi => wi?.id).filter(Boolean)
1420
+ );
1437
1421
 
1438
- let orphanPrdEntries;
1439
- try { orphanPrdEntries = fs.readdirSync(PRD_DIR).filter(f => f.endsWith('.json')); }
1440
- catch { orphanPrdEntries = []; }
1422
+ const prdStore = require('./prd-store');
1423
+ const orphanPrdEntries = prdStore.listPrdRows().filter(row => !row.archived).map(row => row.filename);
1441
1424
  for (const pf of orphanPrdEntries) {
1442
1425
  const prdPath = path.join(PRD_DIR, pf);
1443
- // safeJsonNoRestore — peek must not auto-restore archived PRDs
1444
- // from a stale .backup sidecar (W-mouptdh1000h9f39).
1445
- const peek = safeJsonNoRestore(prdPath);
1426
+ const peek = prdStore.readPrd(prdPath);
1446
1427
  if (!peek?.missing_features) continue;
1447
1428
  let reset = 0;
1448
- mutateJsonFileLocked(prdPath, (prd) => {
1449
- if (!prd?.missing_features) return prd;
1429
+ const prd = peek;
1430
+ if (prd?.missing_features) {
1450
1431
  for (const feat of prd.missing_features) {
1451
1432
  if ((feat.status === shared.WI_STATUS.DISPATCHED || feat.status === shared.WI_STATUS.FAILED) && !wiIds.has(feat.id)) {
1452
1433
  feat.status = shared.WI_STATUS.PENDING;
1453
1434
  reset++;
1454
1435
  }
1455
1436
  }
1456
- return prd;
1457
- }, { skipWriteIfUnchanged: true });
1437
+ }
1438
+ if (reset > 0) prdStore.writePrd(prdPath, prd);
1458
1439
  if (reset > 0) {
1459
1440
  log('info', `Reset ${reset} orphaned PRD item status(es) → pending in ${pf}`);
1460
1441
  cleaned.orphanedPrdStatuses += reset;
@@ -1669,7 +1650,6 @@ async function runCleanup(config, verbose = false) {
1669
1650
  * that were written before that guard existed. */
1670
1651
  function scrubStaleMetrics() {
1671
1652
  const metricsPath = path.join(ENGINE_DIR, 'metrics.json');
1672
- if (!fs.existsSync(metricsPath)) return;
1673
1653
  mutateJsonFileLocked(metricsPath, metrics => {
1674
1654
  for (const key of Object.keys(metrics)) {
1675
1655
  if (key.startsWith('temp-') || key === 'agent1' || key.startsWith('_test')) {
package/engine/cli.js CHANGED
@@ -247,10 +247,10 @@ const CLI_COMMAND_DOCS = Object.freeze({
247
247
  kill: { args: '', summary: 'Kill all active agents, reset to pending' },
248
248
  complete: { args: '<dispatch-id>', summary: 'Mark a dispatch as done' },
249
249
  cleanup: { args: '', summary: 'Clean temp files, worktrees, zombies' },
250
- 'mcp-sync': { args: '', summary: 'Print harness propagation diagnostic (same source as `minions doctor --harness`; read-only, no writes)' },
251
- doctor: { args: '[--harness]', summary: 'Check prerequisites and runtime health (--harness: print harness propagation diagnostic)' },
250
+ 'mcp-sync': { args: '', summary: 'Print runtime harness inventory (same source as `minions doctor --harness`; read-only, no writes)' },
251
+ doctor: { args: '[--harness]', summary: 'Check prerequisites and runtime health (--harness: print runtime harness inventory)' },
252
252
  config: { args: 'set-cli <R> [--model M]', summary: 'Persist defaultCli/defaultModel without starting' },
253
- pr: { args: 'comment <repo> <prNumber> --agent <id> --kind <k> [--wi <id>] [--resolved] [--harness-file <f>|--harness-json <j>] [--skill-skipped-file <f>|--skill-skipped-json <j>] [--body-file <f>|--body <text>]', summary: 'Post a marker-prepended PR comment via gh (ADO: --resolved posts pre-closed)' },
253
+ pr: { args: 'comment <repo> <prNumber> --agent <id> --kind <k> [--wi <id>] [--resolved] [--body-file <f>|--body <text>]', summary: 'Post a marker-prepended PR comment via gh (ADO: --resolved posts pre-closed)' },
254
254
  bridge: { args: 'status|health|enable|disable', summary: 'Constellation bridge: toggle and inspect the read-only cross-repo feed' },
255
255
  });
256
256
 
@@ -413,111 +413,6 @@ function _applyRuntimeFlags({ cli, model, modelExplicit }) {
413
413
  return { warnings, applied: true };
414
414
  }
415
415
 
416
- // resolveHarnessUsedForComment(flags) — P-7a3c9e21. Turn the `minions pr comment`
417
- // harness flags into a grounded `harnessUsed` record suitable for the posters'
418
- // `harnessUsed` param (engine/gh-comment.js / engine/ado-comment.js), which fold
419
- // it via buildMinionsCommentBody -> buildHarnessUsedSection.
420
- //
421
- // Source precedence: `--harness-file <path>` (JSON on disk) wins over the inline
422
- // `--harness-json <json>` convenience; neither supplied -> returns undefined so
423
- // the comment body is byte-identical to today (no section). Read/parse failures
424
- // are HARD errors (process.exit(2) with the offending message) — never a silent
425
- // drop. Grounding uses the spawn-time `_harnessPropagated` manifest located by
426
- // dispatch id (explicit `--dispatch-id` -> else basename(MINIONS_COMPLETION_REPORT,
427
- // '.json')); an unresolvable manifest passes null, marking every entry
428
- // grounded:false — the honest "no record" state, not a fatal condition.
429
- function resolveHarnessUsedForComment(flags) {
430
- const fileArg = flags['harness-file'];
431
- const jsonArg = flags['harness-json'];
432
-
433
- let raw;
434
- if (fileArg !== undefined) {
435
- try {
436
- raw = fs.readFileSync(fileArg, 'utf8');
437
- } catch (e) {
438
- console.error(`error: could not read --harness-file ${fileArg}: ${e.message}`);
439
- process.exit(2);
440
- }
441
- } else if (jsonArg !== undefined) {
442
- raw = String(jsonArg);
443
- } else {
444
- return undefined;
445
- }
446
-
447
- let parsed;
448
- try {
449
- parsed = JSON.parse(raw);
450
- } catch (e) {
451
- const src = fileArg !== undefined ? `--harness-file ${fileArg}` : '--harness-json';
452
- console.error(`error: invalid JSON in ${src}: ${e.message}`);
453
- process.exit(2);
454
- }
455
-
456
- // Resolve the grounding manifest best-effort: explicit id -> completion-report
457
- // basename. A missing/unresolvable manifest is non-fatal (groundHarnessUsed
458
- // with null propagated marks everything grounded:false).
459
- const dispatchId = flags['dispatch-id']
460
- || path.basename(process.env.MINIONS_COMPLETION_REPORT || '', '.json')
461
- || undefined;
462
- let propagated = null;
463
- if (dispatchId) {
464
- try {
465
- const dispatch = getDispatch();
466
- for (const queue of ['active', 'completed', 'pending']) {
467
- const list = Array.isArray(dispatch?.[queue]) ? dispatch[queue] : null;
468
- if (!list) continue;
469
- const found = list.find(d => d && d.id === dispatchId);
470
- if (found && found._harnessPropagated) { propagated = found._harnessPropagated; break; }
471
- }
472
- } catch { propagated = null; }
473
- }
474
-
475
- return shared.groundHarnessUsed(parsed, propagated);
476
- }
477
-
478
- // resolveSkillSkippedForComment(flags) — W-mr287sc0000uc214. Turn the
479
- // `minions pr comment` skip flags into a canonical `{name, reason}` record for
480
- // the posters' `skillSkipped` param (engine/gh-comment.js / engine/ado-comment.js),
481
- // which fold it via buildMinionsCommentBody -> buildSkippedSkillSection into a
482
- // VISIBLE callout in the same comment as the verdict.
483
- //
484
- // Source precedence mirrors the harness resolver: `--skill-skipped-file <path>`
485
- // (JSON on disk) wins over the inline `--skill-skipped-json <json>`; neither
486
- // supplied -> returns undefined so the comment body is byte-identical to today
487
- // (no callout). Read/parse failures are HARD errors (process.exit(2)). Accepts
488
- // the raw `{name, reason}` shape or a `meta.skill` / `meta.review` wrapper; a
489
- // record with no non-empty `name` yields undefined (nothing to surface).
490
- function resolveSkillSkippedForComment(flags) {
491
- const fileArg = flags['skill-skipped-file'];
492
- const jsonArg = flags['skill-skipped-json'];
493
-
494
- let raw;
495
- if (fileArg !== undefined) {
496
- try {
497
- raw = fs.readFileSync(fileArg, 'utf8');
498
- } catch (e) {
499
- console.error(`error: could not read --skill-skipped-file ${fileArg}: ${e.message}`);
500
- process.exit(2);
501
- }
502
- } else if (jsonArg !== undefined) {
503
- raw = String(jsonArg);
504
- } else {
505
- return undefined;
506
- }
507
-
508
- let parsed;
509
- try {
510
- parsed = JSON.parse(raw);
511
- } catch (e) {
512
- const src = fileArg !== undefined ? `--skill-skipped-file ${fileArg}` : '--skill-skipped-json';
513
- console.error(`error: invalid JSON in ${src}: ${e.message}`);
514
- process.exit(2);
515
- }
516
-
517
- const { _extractSkillSkipped } = require('./comment-format');
518
- return _extractSkillSkipped(parsed) || undefined;
519
- }
520
-
521
416
  const commands = {
522
417
  start(...startArgs) {
523
418
  // Apply --cli / --model fleet flags before any engine wiring touches
@@ -539,18 +434,6 @@ const commands = {
539
434
  throw err;
540
435
  }
541
436
 
542
- // Startup state-file size guard (#1167): dispatch.json / cooldowns.json
543
- // bloated past 100 MB silently OOMed V8 on JSON.parse at startup. Fail fast
544
- // with an actionable message instead.
545
- try {
546
- for (const fp of [DISPATCH_PATH, path.join(ENGINE_DIR, 'cooldowns.json')]) {
547
- shared.assertStateFileSize(fp);
548
- }
549
- } catch (stateErr) {
550
- console.error('\n[engine] STARTUP ABORTED — ' + stateErr.message + '\n');
551
- process.exit(78); // 78 = configuration error
552
- }
553
-
554
437
  // Run preflight checks (warn but don't block — engine may still be useful)
555
438
  try {
556
439
  const { runPreflight, printPreflight } = require('./preflight');
@@ -667,10 +550,7 @@ const commands = {
667
550
  }
668
551
  } catch (err) { e.log('warn', `note-link backfill failed: ${err.message}`); }
669
552
 
670
- // Backfill work_items.prd_item_id (the WI↔PRD-item FK) for rows that predate
671
- // the dual-write stamp (#546) or that a JSON re-hydrate transiently zeroed.
672
- // Set-based, only touches NULL FKs, no-op once everything is linked — safe on
673
- // every boot. Lets the Phase-10 render join move off the feature-id string.
553
+ // Backfill work_items.prd_item_id for rows created before FK stamping.
674
554
  try {
675
555
  const fk = require('./prd-store').backfillWorkItemPrdItemIds();
676
556
  const filled = (fk.byWorkItemId || 0) + (fk.bySourcePlan || 0);
@@ -1211,22 +1091,7 @@ const commands = {
1211
1091
  const _watchedFiles = new Set();
1212
1092
  let _globalDebounce = null;
1213
1093
  function watchForWorkChanges() {
1214
- const filesToWatch = [
1215
- path.join(MINIONS_DIR, 'work-items.json'),
1216
- // dispatch.json excluded — it changes every tick, causing a feedback loop
1217
- ];
1218
- // Watch project-specific work-items.json
1219
- const { getProjects } = require('./shared');
1220
- for (const p of getProjects(config)) {
1221
- filesToWatch.push(shared.projectWorkItemsPath(p));
1222
- }
1223
- // Watch PRD files
1224
- const prdDir = path.join(MINIONS_DIR, 'prd');
1225
- try {
1226
- for (const f of fs.readdirSync(prdDir).filter(f => f.endsWith('.json'))) {
1227
- filesToWatch.push(path.join(prdDir, f));
1228
- }
1229
- } catch { /* optional */ }
1094
+ const filesToWatch = [];
1230
1095
 
1231
1096
  for (const filePath of filesToWatch) {
1232
1097
  if (_watchedFiles.has(filePath)) continue;
@@ -1465,8 +1330,8 @@ const commands = {
1465
1330
  }
1466
1331
  const flagFields = [
1467
1332
  'claudeBareMode', 'claudeFallbackModel',
1468
- 'copilotDisableBuiltinMcps', 'copilotSuppressAgentsMd', 'copilotStreamMode', 'copilotReasoningSummaries',
1469
- 'harnessPropagateProjectLocal', 'claudePreApproveWorkspaceMcps', 'hermeticHarness',
1333
+ 'copilotDisableBuiltinMcps', 'copilotStreamMode', 'copilotReasoningSummaries',
1334
+ 'claudePreApproveWorkspaceMcps',
1470
1335
  'maxBudgetUsd', 'disableModelDiscovery',
1471
1336
  'ccUseWorkerPool',
1472
1337
  ];
@@ -1488,14 +1353,13 @@ const commands = {
1488
1353
  }
1489
1354
 
1490
1355
  console.log('');
1491
- const metrics = shared.safeJsonObj(path.join(ENGINE_DIR, 'metrics.json'));
1356
+ const metrics = queries.getMetrics();
1492
1357
  const lifetimeCompleted = Object.entries(metrics).filter(([k]) => !k.startsWith('_')).reduce((sum, [, m]) => sum + (m.tasksCompleted || 0) + (m.tasksErrored || 0), 0);
1493
1358
  console.log(`Dispatch: ${dispatch.pending.length} pending | ${(dispatch.active || []).length} active | ${lifetimeCompleted} completed`);
1494
1359
  const pidSummary = summarizeActiveDispatchPids(dispatch.active || []);
1495
1360
  console.log(`Active dispatch PID files: ${pidSummary.live} live | ${pidSummary.dead} dead | ${pidSummary.missing} missing`);
1496
1361
 
1497
- const metricsPath = path.join(ENGINE_DIR, 'metrics.json');
1498
- const metricsData = safeJson(metricsPath);
1362
+ const metricsData = queries.getMetrics();
1499
1363
  if (metricsData && Object.keys(metricsData).length > 0) {
1500
1364
  console.log('\nMetrics:');
1501
1365
  console.log(` ${'Agent'.padEnd(12)} ${'Done'.padEnd(6)} ${'Err'.padEnd(6)} ${'PRs'.padEnd(6)} ${'Appr'.padEnd(6)} ${'Rej'.padEnd(6)} ${'Reviews'.padEnd(8)} ${'Cost'.padEnd(8)}`);
@@ -1989,7 +1853,7 @@ const commands = {
1989
1853
  doctor(...doctorArgs) {
1990
1854
  const { doctor, runHarnessDoctor } = require('./preflight');
1991
1855
  if (doctorArgs.includes('--harness')) {
1992
- // P-a3f9b2c1 harness propagation diagnostic. See
1856
+ // Read-only runtime harness inventory. See
1993
1857
  // docs/harness-propagation.md and the parallel branch in bin/minions.js.
1994
1858
  const ok = runHarnessDoctor(MINIONS_DIR);
1995
1859
  if (!ok) process.exit(1);
@@ -2086,19 +1950,9 @@ const commands = {
2086
1950
  console.log('Usage:');
2087
1951
  console.log(' GitHub: minions pr comment <repo> <prNumber> --agent <id> --kind <k> [--wi <id>] (--body-file <path> | --body <text>)');
2088
1952
  console.log(' ADO: minions pr comment <prNumber> --host ado --ado-org <org> --ado-project <proj> --repo-id <id> --agent <id> --kind <k> [--wi <id>] (--body-file <path> | --body <text>)');
2089
- console.log('');
2090
- console.log('Optional harness self-report (folded into the collapsible "Harnesses used" section):');
2091
- console.log(' --harness-file <path> JSON file with the { skills, mcpServers, commands, docs } record (wins over --harness-json)');
2092
- console.log(' --harness-json <json> same record inline as a JSON string');
2093
- console.log(' --dispatch-id <id> grounding manifest override (else basename of $MINIONS_COMPLETION_REPORT)');
2094
- console.log('');
2095
- console.log('Skipped in-scope project skill (folded in as a VISIBLE "⚠️ Skipped project skill" callout):');
2096
- console.log(' --skill-skipped-file <path> JSON file with the { name, reason } skip record (wins over --skill-skipped-json)');
2097
- console.log(' --skill-skipped-json <json> same record inline as a JSON string (also accepts a meta.skill/meta.review wrapper)');
2098
1953
  console.log(' --resolved (ADO only) post the thread pre-resolved/closed — for non-actionable FYI/praise notes');
2099
1954
  console.log('');
2100
- console.log('Posts a PR comment with the hidden minions marker, the collapsible');
2101
- console.log('"Harnesses used" section, and the brand link folded in by the shared');
1955
+ console.log('Posts a PR comment with the hidden minions marker and brand link folded in by the shared');
2102
1956
  console.log('builder — so engine classifiers identify agent-authored comments by');
2103
1957
  console.log('structure, and the body is byte-identical on GitHub (`gh pr comment`)');
2104
1958
  console.log('and Azure DevOps (REST `…/threads`).');
@@ -2162,18 +2016,6 @@ const commands = {
2162
2016
  process.exit(2);
2163
2017
  }
2164
2018
 
2165
- // P-7a3c9e21 — thread the consulted-sources footprint into both posters so
2166
- // the auto-folded "Harnesses used" <details> section the help already
2167
- // promises is actually rendered. Resolved once (host-neutral); absent flags
2168
- // -> undefined -> body byte-identical to today.
2169
- const harnessUsed = resolveHarnessUsedForComment(flags);
2170
-
2171
- // W-mr287sc0000uc214 — thread the skipped-in-scope-project-skill self-report
2172
- // into both posters so it's surfaced as a VISIBLE callout in the same comment
2173
- // as the verdict (not buried in the completion-report JSON). Absent flags
2174
- // -> undefined -> body byte-identical to today.
2175
- const skillSkipped = resolveSkillSkippedForComment(flags);
2176
-
2177
2019
  // ── Azure DevOps: <prNumber> --host ado --ado-org --ado-project --repo-id ──
2178
2020
  if (isAdo) {
2179
2021
  const [prNumberRaw] = positional;
@@ -2193,7 +2035,7 @@ const commands = {
2193
2035
  const orgBase = flags['org-base'] || `https://dev.azure.com/${adoOrg}`;
2194
2036
  const adoComment = require('./ado-comment');
2195
2037
  adoComment.postAdoPrComment({
2196
- orgBase, project, repositoryId, prNumber, body, agentId, kind, workItemId, harnessUsed, skillSkipped,
2038
+ orgBase, project, repositoryId, prNumber, body, agentId, kind, workItemId,
2197
2039
  resolved: flags.resolved === true,
2198
2040
  }).then((result) => {
2199
2041
  if (result && result.threadId) {
@@ -2232,7 +2074,7 @@ const commands = {
2232
2074
 
2233
2075
  try {
2234
2076
  const result = ghComment.postPrComment({
2235
- repo, prNumber, body, agentId, kind, workItemId, harnessUsed, skillSkipped,
2077
+ repo, prNumber, body, agentId, kind, workItemId,
2236
2078
  });
2237
2079
  if (result.output) console.log(result.output);
2238
2080
  } catch (e) {
@@ -2371,10 +2213,6 @@ module.exports = {
2371
2213
  _readDispatchPid: readDispatchPid,
2372
2214
  _normalizeSessionBranch: normalizeSessionBranch,
2373
2215
  _dispatchSessionBranch: dispatchSessionBranch,
2374
- // P-7a3c9e21 — harness-flag resolver exported for CLI-threading tests.
2375
- _resolveHarnessUsedForComment: resolveHarnessUsedForComment,
2376
- // W-mr287sc0000uc214 — skipped-project-skill flag resolver exported for tests.
2377
- _resolveSkillSkippedForComment: resolveSkillSkippedForComment,
2378
2216
  // W-mpcyvff6000pf828 (#2653) — heartbeat writer + factory exported for tests
2379
2217
  _writeHeartbeatNow: writeHeartbeatNow,
2380
2218
  _createHeartbeatInterval: createHeartbeatInterval,