@yemi33/minions 0.1.2379 → 0.1.2380

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 (96) hide show
  1. package/bin/minions.js +19 -9
  2. package/dashboard/js/refresh.js +2 -3
  3. package/dashboard/js/render-prd.js +1 -1
  4. package/dashboard/js/render-work-items.js +13 -6
  5. package/dashboard.js +393 -721
  6. package/docs/architecture-review-2026-07-09.md +2 -4
  7. package/docs/auto-discovery.md +36 -49
  8. package/docs/blog-first-successful-dispatch.md +1 -1
  9. package/docs/branch-derivation.md +2 -2
  10. package/docs/command-center.md +1 -1
  11. package/docs/completion-reports.md +14 -4
  12. package/docs/constellation-bridge.md +59 -10
  13. package/docs/constellation-style-telemetry.md +6 -6
  14. package/docs/cooldown-merge-semantics.md +4 -0
  15. package/docs/copilot-cli-schema.md +3 -3
  16. package/docs/cross-repo-plans.md +17 -17
  17. package/docs/deprecated.json +2 -2
  18. package/docs/design-state-storage.md +1 -1
  19. package/docs/documentation-audit-2026-07-09.md +2 -2
  20. package/docs/engine-restart.md +20 -8
  21. package/docs/harness-mode.md +1 -1
  22. package/docs/managed-spawn.md +4 -4
  23. package/docs/onboarding.md +1 -2
  24. package/docs/pr-comment-followup.md +3 -3
  25. package/docs/pr-review-fix-loop.md +1 -1
  26. package/docs/proposals/repo-pool-for-live-checkout.md +1 -2
  27. package/docs/qa-runbook-lifecycle.md +4 -4
  28. package/docs/qa-runbooks.md +2 -2
  29. package/docs/rfc-completion-json.md +4 -1
  30. package/docs/runtime-adapters.md +1 -1
  31. package/docs/self-improvement.md +4 -5
  32. package/docs/shared-lifecycle-module-map.md +3 -1
  33. package/docs/slim-ux/architecture-suggestions.md +5 -6
  34. package/docs/slim-ux/concepts.md +23 -25
  35. package/docs/watches.md +7 -7
  36. package/docs/workspace-manifests.md +1 -1
  37. package/docs/worktree-lifecycle.md +1 -1
  38. package/engine/abandoned-pr-reconciliation.js +4 -5
  39. package/engine/ado-status.js +5 -5
  40. package/engine/ado.js +20 -25
  41. package/engine/agent-worker-pool.js +58 -1
  42. package/engine/bridge.js +260 -5
  43. package/engine/cleanup.js +48 -131
  44. package/engine/cli.js +125 -83
  45. package/engine/cooldown.js +9 -16
  46. package/engine/db/index.js +22 -9
  47. package/engine/db/migrations/009-qa.js +1 -1
  48. package/engine/db/migrations/020-qa-session-scopes.js +23 -0
  49. package/engine/db/migrations/021-archived-work-items.js +72 -0
  50. package/engine/db/migrations/022-global-cc-session.js +31 -0
  51. package/engine/db/migrations/023-engine-state.js +30 -0
  52. package/engine/db/migrations/024-prd-ghost-collisions.js +53 -0
  53. package/engine/db/migrations/025-malformed-work-item-phantoms.js +33 -0
  54. package/engine/dispatch-store.js +2 -7
  55. package/engine/dispatch.js +34 -41
  56. package/engine/github.js +20 -27
  57. package/engine/lifecycle.js +221 -283
  58. package/engine/llm.js +1 -1
  59. package/engine/logs-store.js +2 -2
  60. package/engine/managed-spawn.js +2 -23
  61. package/engine/meeting.js +6 -6
  62. package/engine/metrics-store.js +2 -2
  63. package/engine/note-link-backfill.js +6 -11
  64. package/engine/pipeline.js +18 -36
  65. package/engine/playbook.js +1 -1
  66. package/engine/prd-store.js +73 -54
  67. package/engine/preflight.js +2 -5
  68. package/engine/projects.js +15 -62
  69. package/engine/pull-requests-store.js +0 -17
  70. package/engine/qa-runbooks.js +2 -2
  71. package/engine/qa-runs.js +1 -8
  72. package/engine/qa-sessions.js +41 -64
  73. package/engine/queries.js +120 -219
  74. package/engine/routing.js +3 -5
  75. package/engine/scheduler.js +0 -4
  76. package/engine/shared-branch-pr-reconcile.js +2 -3
  77. package/engine/shared.js +132 -637
  78. package/engine/small-state-store.js +89 -10
  79. package/engine/state-operations.js +16 -4
  80. package/engine/stdio-timestamps.js +1 -1
  81. package/engine/timeout.js +5 -12
  82. package/engine/watch-actions.js +20 -22
  83. package/engine/watches-store.js +1 -1
  84. package/engine/watches.js +6 -10
  85. package/engine/work-item-validation.js +52 -0
  86. package/engine/work-items-store.js +127 -29
  87. package/engine/worktree-gc.js +2 -2
  88. package/engine/worktree-pool.js +8 -18
  89. package/engine.js +147 -343
  90. package/minions.js +2 -2
  91. package/package.json +1 -1
  92. package/playbooks/plan-to-prd.md +2 -2
  93. package/playbooks/shared-rules.md +3 -3
  94. package/playbooks/templates/followup-dispatch.md +1 -1
  95. package/playbooks/verify.md +1 -1
  96. package/prompts/cc-system.md +2 -2
package/bin/minions.js CHANGED
@@ -1404,9 +1404,21 @@ ${fs.existsSync(path.join(PKG_ROOT, '.git')) ? `
1404
1404
  // PowerShell is unavailable, the engine is hung, or its cmdline doesn't match.
1405
1405
  const oldEnginePid = readEnginePid(MINIONS_HOME);
1406
1406
  try { execSync(`node "${path.join(MINIONS_HOME, 'engine.js')}" stop`, { stdio: 'ignore', cwd: MINIONS_HOME, timeout: 10000, windowsHide: true }); } catch {}
1407
- // Force-kill the recorded engine PID (NOT the tree agent children must
1408
- // survive so the new engine can re-attach them via PID files).
1409
- killPidOnly(oldEnginePid);
1407
+ // Give the engine's graceful handler time to drain non-reattachable pooled
1408
+ // leases. Detached cold agents are not part of that drain and remain alive
1409
+ // for normal PID-file reattachment by the replacement engine.
1410
+ const shutdownTimeout = (() => {
1411
+ try {
1412
+ const cfg = JSON.parse(fs.readFileSync(path.join(MINIONS_HOME, 'config.json'), 'utf8'));
1413
+ return Number(cfg?.engine?.shutdownTimeout) || shared.ENGINE_DEFAULTS.shutdownTimeout;
1414
+ } catch { return shared.ENGINE_DEFAULTS.shutdownTimeout; }
1415
+ })();
1416
+ const gracefulEngineStop = waitForPidDeath(oldEnginePid, shutdownTimeout + 5000);
1417
+ if (!gracefulEngineStop.ok) {
1418
+ // Fallback for a hung engine. Kill only the engine PID, never its detached
1419
+ // cold-agent children; pooled leases have already exceeded their drain budget.
1420
+ killPidOnly(oldEnginePid);
1421
+ }
1410
1422
  killByPort(restartResolved.port);
1411
1423
  killMinionsProcesses(['engine.js', 'dashboard.js', 'supervisor.js']);
1412
1424
  // Confirm the OS finished the asynchronous termination before we spawn new
@@ -1491,15 +1503,13 @@ ${fs.existsSync(path.join(PKG_ROOT, '.git')) ? `
1491
1503
  const engineDir = path.join(MINIONS_HOME, 'engine');
1492
1504
 
1493
1505
  // Engine state
1494
- for (const f of ['dispatch.json', 'control.json', 'log.json', 'metrics.json', 'cooldowns.json', 'schedule-runs.json', 'kb-checkpoint.json', 'cc-session.json', 'doc-sessions.json', 'pipeline-runs.json']) rm(path.join(engineDir, f));
1506
+ require(path.join(PKG_ROOT, 'engine', 'db')).cleanupRetiredJsonFiles(MINIONS_HOME);
1507
+ for (const f of ['state.db', 'state.db-wal', 'state.db-shm', 'control.json', 'kb-checkpoint.json']) {
1508
+ rm(path.join(engineDir, f));
1509
+ }
1495
1510
  glob(engineDir, /^pid-.*\.pid$/).forEach(rm);
1496
1511
  rmDir(path.join(engineDir, 'tmp'));
1497
1512
 
1498
- // Work items + PRs
1499
- rm(path.join(MINIONS_HOME, 'work-items.json'));
1500
- rm(path.join(MINIONS_HOME, 'work-items-archive.json'));
1501
- rm(path.join(MINIONS_HOME, 'pull-requests.json'));
1502
-
1503
1513
  // Plans + PRDs + Pipelines + Meetings
1504
1514
  rmDir(path.join(MINIONS_HOME, 'plans'));
1505
1515
  rmDir(path.join(MINIONS_HOME, 'prd'));
@@ -151,9 +151,8 @@ const RENDER_VERSIONS = {
151
151
  dispatch: 2,
152
152
  engineLog: 2,
153
153
  metrics: 1,
154
- // Bumped 7→8: WI detail modal's branch pill moved out of the Artifacts
155
- // chip row into its own non-interactive "Branch" field (W-mr2qjr3b0002522f).
156
- workItems: 8,
154
+ // Bumped 8->9: row actions now preserve work-item source scope when IDs collide.
155
+ workItems: 9,
157
156
  skills: 1,
158
157
  commands: 1,
159
158
  mcpServers: 1,
@@ -855,7 +855,7 @@ async function prdItemEdit(source, itemId) {
855
855
 
856
856
  completionHtml = '<div style="background:var(--surface);border:1px solid var(--border);border-left:3px solid ' + statusColor + ';border-radius:4px;padding:10px 12px;margin-bottom:12px">' +
857
857
  '<div style="display:flex;align-items:center;gap:8px;margin-bottom:6px">' +
858
- (wi ? '<a href="#" onclick="event.preventDefault();event.stopPropagation();openWorkItemDetail(\'' + escHtml(wi.id) + '\')" title="Open ' + escHtml(wi.id) + ' detail" style="font-size:var(--text-base);font-weight:700;color:' + statusColor + ';text-decoration:none;cursor:pointer" onmouseover="this.style.textDecoration=\'underline\'" onmouseout="this.style.textDecoration=\'none\'">' + statusLabel + ' &rarr;</a>' : '<span style="font-size:var(--text-base);font-weight:700;color:' + statusColor + '">' + statusLabel + '</span>') +
858
+ (wi ? '<a href="#" onclick="event.preventDefault();event.stopPropagation();openWorkItemDetail(\'' + escHtml(wi.id) + '\',\'' + escHtml(wi._source || '') + '\')" title="Open ' + escHtml(wi.id) + ' detail" style="font-size:var(--text-base);font-weight:700;color:' + statusColor + ';text-decoration:none;cursor:pointer" onmouseover="this.style.textDecoration=\'underline\'" onmouseout="this.style.textDecoration=\'none\'">' + statusLabel + ' &rarr;</a>' : '<span style="font-size:var(--text-base);font-weight:700;color:' + statusColor + '">' + statusLabel + '</span>') +
859
859
  typePillHtml +
860
860
  (agent ? '<span style="font-size:var(--text-base);color:var(--muted)">by ' + escHtml(agent) + '</span>' : '') +
861
861
  (completedAt ? '<span style="font-size:var(--text-sm);color:var(--muted)">' + escHtml(completedAt.slice(0, 16).replace('T', ' ')) + '</span>' : '') +
@@ -192,7 +192,7 @@ function wiRow(item) {
192
192
  var prLabel = prNumMatch ? ('PR #' + prNumMatch[1]) : 'parent PR';
193
193
  followupChip = ' <a class="pr-badge draft" style="font-size:var(--text-xs);text-decoration:none" target="_blank" rel="noopener" href="' + escapeHtml(prFollowup.parent_pr_url) + '" title="Follow-up dispatched from ' + escapeHtml(prRef) + (prFollowup.parent_comment_author ? ' by ' + escapeHtml(prFollowup.parent_comment_author) : '') + '" onclick="event.stopPropagation()">&#8617; from ' + escapeHtml(prLabel) + '</a>';
194
194
  }
195
- return '<tr data-wi-id="' + escapeHtml(item.id) + '" style="cursor:pointer" onclick="if(shouldIgnoreSelectionClick(event))return;openWorkItemDetail(\'' + escapeHtml(item.id) + '\')">' +
195
+ return '<tr data-wi-id="' + escapeHtml(item.id) + '" style="cursor:pointer" onclick="if(shouldIgnoreSelectionClick(event))return;openWorkItemDetail(\'' + escapeHtml(item.id) + '\',\'' + escapeHtml(item._source || '') + '\')">' +
196
196
  '<td style="min-width:280px;max-width:320px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap" title="' + escapeHtml((item.title || '').slice(0, 200)) + '">' + escapeHtml(item.title || '') + followupChip + '</td>' +
197
197
  '<td><span style="font-size:var(--text-sm);color:var(--muted)">' + escapeHtml(item._source || '') + '</span>' +
198
198
  (item.scope === 'fan-out' ? ' <span class="pr-badge ' + (item.status === 'done' || item.status === 'failed' ? 'draft' : 'building') + '" style="font-size:var(--text-xs)">fan-out</span>' : '') + '</td>' +
@@ -312,7 +312,8 @@ function renderWorkItems(items, opts) {
312
312
  }
313
313
 
314
314
  async function editWorkItem(id, source) {
315
- const cached = allWorkItems.find(i => i.id === id);
315
+ source = source || '';
316
+ const cached = allWorkItems.find(i => i.id === id && (!source || i._source === source));
316
317
  if (!cached) return;
317
318
  // Hydrate the full record before rendering the form. The bulk
318
319
  // /api/work-items list endpoint truncates `description` and replaces
@@ -331,7 +332,7 @@ async function editWorkItem(id, source) {
331
332
  (cached.referencesCount > 0 && !Array.isArray(cached.references));
332
333
  if (needsHydration) {
333
334
  try {
334
- const r = await fetch('/api/work-items/' + encodeURIComponent(id));
335
+ const r = await fetch('/api/work-items/' + encodeURIComponent(id) + '?source=' + encodeURIComponent(source));
335
336
  if (!r.ok) throw new Error('HTTP ' + r.status);
336
337
  const data = await r.json();
337
338
  const full = data && data.item;
@@ -972,9 +973,15 @@ function _wiRenderDetail(item) {
972
973
  return html;
973
974
  }
974
975
 
975
- function openWorkItemDetail(id) {
976
+ function openWorkItemDetail(id, source) {
976
977
  if (window.getSelection && window.getSelection().toString().length > 0) return;
977
- const cached = allWorkItems.find(i => i.id === id);
978
+ source = source || '';
979
+ const matches = allWorkItems.filter(i => i.id === id && (!source || i._source === source));
980
+ if (!source && matches.length > 1) {
981
+ alert('Multiple work items share this ID. Open it from the Work Items page to select the project.');
982
+ return;
983
+ }
984
+ const cached = matches[0];
978
985
  if (!cached) return;
979
986
 
980
987
  // Render the modal immediately from the cached (slim) record so the click
@@ -999,7 +1006,7 @@ function openWorkItemDetail(id) {
999
1006
 
1000
1007
  if (!needsHydration) return;
1001
1008
 
1002
- fetch('/api/work-items/' + encodeURIComponent(id))
1009
+ fetch('/api/work-items/' + encodeURIComponent(id) + '?source=' + encodeURIComponent(source))
1003
1010
  .then(function(r) { if (!r.ok) throw new Error('HTTP ' + r.status); return r.json(); })
1004
1011
  .then(function(data) {
1005
1012
  // Guard against modal navigation away from this WI during the fetch.