@yemi33/minions 0.1.897 → 0.1.899

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/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.899 (2026-04-12)
4
+
5
+ ### Fixes
6
+ - add backward-scan in syncPrdItemStatus to re-open done work items on PRD reset (closes #929) (#940)
7
+
8
+ ## 0.1.898 (2026-04-12)
9
+
10
+ ### Fixes
11
+ - preserve updated status across successive diff-aware regenerations (closes #928) (#938)
12
+
3
13
  ## 0.1.897 (2026-04-12)
4
14
 
5
15
  ### Fixes
@@ -95,6 +95,7 @@ function renderPrdProgress(prog) {
95
95
  const inProgress = items.filter(i => i.status === 'dispatched').length;
96
96
  const failed = items.filter(i => i.status === 'failed').length;
97
97
  const paused = items.filter(i => i.status === 'paused').length;
98
+ const updated = items.filter(i => i.status === 'updated').length;
98
99
  const missing = items.filter(i => i.status === 'missing' || !i.status).length;
99
100
  const pct = (n) => total > 0 ? ((n / total) * 100).toFixed(1) : 0;
100
101
 
@@ -103,6 +104,7 @@ function renderPrdProgress(prog) {
103
104
  '<div class="prd-stat"><div class="prd-stat-num" style="color:var(--yellow)">' + inProgress + '</div><div class="prd-stat-label">Active</div></div>' +
104
105
  (failed ? '<div class="prd-stat"><div class="prd-stat-num" style="color:var(--red)">' + failed + '</div><div class="prd-stat-label">Failed</div></div>' : '') +
105
106
  (paused ? '<div class="prd-stat"><div class="prd-stat-num" style="color:var(--muted)">' + paused + '</div><div class="prd-stat-label">Paused</div></div>' : '') +
107
+ (updated ? '<div class="prd-stat"><div class="prd-stat-num" style="color:var(--purple)">' + updated + '</div><div class="prd-stat-label">Needs Redo</div></div>' : '') +
106
108
  '<div class="prd-stat"><div class="prd-stat-num" style="color:var(--muted)">' + missing + '</div><div class="prd-stat-label">To Do</div></div>' +
107
109
  '<div class="prd-stat"><div class="prd-stat-num" style="color:var(--text)">' + total + '</div><div class="prd-stat-label">Total</div></div>' +
108
110
  '</div>';
@@ -110,6 +112,7 @@ function renderPrdProgress(prog) {
110
112
  const bar = '<div class="prd-progress-bar">' +
111
113
  '<div class="seg complete" style="width:' + pct(done) + '%"></div>' +
112
114
  '<div class="seg dispatched" style="width:' + pct(inProgress) + '%"></div>' +
115
+ '<div class="seg updated" style="width:' + pct(updated) + '%"></div>' +
113
116
  '<div class="seg paused" style="width:' + pct(paused) + '%"></div>' +
114
117
  '<div class="seg missing" style="width:' + pct(missing) + '%"></div>' +
115
118
  '</div>';
@@ -131,8 +134,9 @@ function renderPrdProgress(prog) {
131
134
  'dispatched': 'background:rgba(210,153,34,0.15);color:var(--yellow);animation:wipPulse 1.5s infinite',
132
135
  'failed': 'background:rgba(248,81,73,0.15);color:var(--red)',
133
136
  'paused': 'background:rgba(139,148,158,0.15);color:var(--muted)',
137
+ 'updated': 'background:rgba(188,140,255,0.15);color:var(--purple)',
134
138
  };
135
- const labels = { 'done': 'DONE', 'dispatched': 'WIP', 'failed': 'FAIL', 'paused': 'PAUSED', 'missing': '\u2014' };
139
+ const labels = { 'done': 'DONE', 'dispatched': 'WIP', 'failed': 'FAIL', 'paused': 'PAUSED', 'missing': '\u2014', 'updated': 'REDO' };
136
140
  const style = styles[s] || 'background:var(--surface);color:var(--muted)';
137
141
  const label = labels[s] || '—';
138
142
  return '<span style="font-size:9px;font-weight:700;padding:2px 6px;border-radius:3px;letter-spacing:0.5px;white-space:nowrap;' + style + '">' + label + '</span>';
@@ -370,6 +374,7 @@ function renderPrdProgress(prog) {
370
374
  if (s === 'done') return 'var(--green)';
371
375
  if (s === 'dispatched') return 'var(--yellow)';
372
376
  if (s === 'failed') return 'var(--red)';
377
+ if (s === 'updated') return 'var(--purple)';
373
378
  if (s === 'paused') return 'var(--muted)';
374
379
  return 'var(--border)';
375
380
  };
@@ -171,6 +171,7 @@
171
171
  .prd-progress-bar .seg { height: 100%; transition: width 0.5s ease; }
172
172
  .prd-progress-bar .seg.complete { background: var(--green); }
173
173
  .prd-progress-bar .seg.dispatched { background: var(--yellow); }
174
+ .prd-progress-bar .seg.updated { background: var(--purple); }
174
175
  .prd-progress-bar .seg.paused { background: var(--muted); opacity: 0.5; }
175
176
  .prd-progress-bar .seg.missing { background: var(--border); }
176
177
  .prd-progress-pct { font-size: 22px; font-weight: 700; color: var(--green); margin-bottom: var(--space-4); }
@@ -179,6 +180,7 @@
179
180
  .prd-legend-dot { width: 10px; height: 10px; border-radius: 2px; }
180
181
  .prd-legend-dot.complete { background: var(--green); }
181
182
  .prd-legend-dot.dispatched { background: var(--yellow); }
183
+ .prd-legend-dot.updated { background: var(--purple); }
182
184
  .prd-legend-dot.missing { background: var(--border); }
183
185
 
184
186
  /* Pipeline Step-Progress */
@@ -212,6 +214,7 @@
212
214
  @keyframes prdWipPulse { 0%, 100% { box-shadow: 0 0 0 0 rgba(210,153,34,0); } 50% { box-shadow: 0 0 0 4px rgba(210,153,34,0.2); } }
213
215
  .prd-item-row.st-failed { border-left-color: var(--red); }
214
216
  .prd-item-row.st-needs-human-review { border-left-color: var(--orange); }
217
+ .prd-item-row.st-updated { border-left-color: var(--purple); }
215
218
  .prd-item-row.st-paused { border-left-color: var(--muted); opacity: 0.5; }
216
219
  .prd-item-id { font-family: Consolas, monospace; color: var(--muted); min-width: 36px; font-size: 0.9em; }
217
220
  .prd-item-name { flex: 1; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
@@ -8,7 +8,7 @@ const path = require('path');
8
8
  const os = require('os');
9
9
  const shared = require('./shared');
10
10
  const { safeRead, safeJson, safeWrite, mutateJsonFileLocked, mutateWorkItems, execSilent, execAsync, projectPrPath, getPrLinks, addPrLink,
11
- log, ts, dateStamp, WI_STATUS, DONE_STATUSES, PLAN_TERMINAL_STATUSES, WORK_TYPE, PLAN_STATUS, PR_STATUS, DISPATCH_RESULT,
11
+ log, ts, dateStamp, WI_STATUS, DONE_STATUSES, PLAN_TERMINAL_STATUSES, WORK_TYPE, PLAN_STATUS, PRD_ITEM_STATUS, PR_STATUS, DISPATCH_RESULT,
12
12
  ENGINE_DEFAULTS, DEFAULT_AGENT_METRICS, FAILURE_CLASS } = shared;
13
13
  const { trackEngineUsage } = require('./llm');
14
14
  const queries = require('./queries');
@@ -637,6 +637,52 @@ function syncPrdItemStatus(itemId, status, sourcePlan) {
637
637
  } catch (err) { log('warn', `PRD status sync: ${err.message}`); }
638
638
  }
639
639
 
640
+ // ─── PRD Backward-Scan Reconciliation (#929) ────────────────────────────────
641
+ // Proactive counterpart to syncPrdItemStatus. Scans all active PRDs and promotes
642
+ // "missing" items to "updated" when a done work item already exists for that ID.
643
+ // This catches cases where a PRD regeneration incorrectly sets items to "missing"
644
+ // and no subsequent work item state change triggers the reactive sync.
645
+
646
+ function reconcilePrdStatuses(config) {
647
+ if (!fs.existsSync(PRD_DIR)) return;
648
+ let prdFiles;
649
+ try { prdFiles = fs.readdirSync(PRD_DIR).filter(f => f.endsWith('.json')); } catch { return; }
650
+ if (prdFiles.length === 0) return;
651
+
652
+ const allWorkItems = queries.getWorkItems(config);
653
+ if (allWorkItems.length === 0) return;
654
+
655
+ // Index done work items by ID for O(1) lookup
656
+ const doneWiById = new Map();
657
+ for (const wi of allWorkItems) {
658
+ if (wi.id && DONE_STATUSES.has(wi.status)) doneWiById.set(wi.id, wi);
659
+ }
660
+ if (doneWiById.size === 0) return;
661
+
662
+ for (const file of prdFiles) {
663
+ try {
664
+ const fpath = path.join(PRD_DIR, file);
665
+ const plan = safeJson(fpath);
666
+ if (!plan?.missing_features) continue;
667
+ // Skip completed/archived PRDs — no reconciliation needed
668
+ if (plan.status === PLAN_STATUS.COMPLETED) continue;
669
+
670
+ let modified = false;
671
+ for (const feature of plan.missing_features) {
672
+ if (feature.status === PRD_ITEM_STATUS.MISSING && doneWiById.has(feature.id)) {
673
+ feature.status = PRD_ITEM_STATUS.UPDATED;
674
+ modified = true;
675
+ log('info', `PRD backward-scan: promoted ${feature.id} from missing→updated in ${file} (done work item exists)`);
676
+ }
677
+ }
678
+
679
+ if (modified) {
680
+ safeWrite(fpath, plan);
681
+ }
682
+ } catch (err) { log('warn', `PRD backward-scan for ${file}: ${err.message}`); }
683
+ }
684
+ }
685
+
640
686
  // ─── PR Sync from Output ─────────────────────────────────────────────────────
641
687
 
642
688
  function syncPrsFromOutput(output, agentId, meta, config) {
@@ -1876,6 +1922,7 @@ module.exports = {
1876
1922
  cleanupPlanWorktrees,
1877
1923
  updateWorkItemStatus,
1878
1924
  syncPrdItemStatus,
1925
+ reconcilePrdStatuses,
1879
1926
  syncPrsFromOutput,
1880
1927
  updatePrAfterReview,
1881
1928
  updatePrAfterFix,
package/engine.js CHANGED
@@ -135,7 +135,7 @@ const { renderPlaybook, validatePlaybookVars, PLAYBOOK_REQUIRED_VARS,
135
135
 
136
136
  // ─── Lifecycle (extracted to engine/lifecycle.js) ────────────────────────────
137
137
 
138
- const { runPostCompletionHooks, updateWorkItemStatus, syncPrdItemStatus, handlePostMerge, checkPlanCompletion,
138
+ const { runPostCompletionHooks, updateWorkItemStatus, syncPrdItemStatus, reconcilePrdStatuses, handlePostMerge, checkPlanCompletion,
139
139
  syncPrsFromOutput, updatePrAfterReview, updatePrAfterFix, checkForLearnings, extractSkillsFromOutput,
140
140
  updateAgentHistory, updateMetrics, createReviewFeedbackForAuthor, parseAgentOutput, syncPrdFromPrs,
141
141
  isItemCompleted, classifyFailure, processPendingRebases } = require('./engine/lifecycle');
@@ -2692,6 +2692,7 @@ async function discoverWork(config) {
2692
2692
 
2693
2693
  // Side-effect passes: materialize plans and design docs into work-items.json
2694
2694
  // These write to project work queues — picked up by discoverFromWorkItems below.
2695
+ reconcilePrdStatuses(config); // Backward-scan: correct "missing" PRD items that have done work items (#929)
2695
2696
  materializePlansAsWorkItems(config);
2696
2697
 
2697
2698
  for (const project of projects) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.897",
3
+ "version": "0.1.899",
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"
@@ -105,7 +105,7 @@ When the engine detects an existing PRD for this plan (`source_plan` match), it
105
105
  1. **Parse the existing PRD JSON** — extract all `missing_features` items with their `id`, `status`, and metadata
106
106
  2. **Preserve item IDs** — match existing items to current plan items by name/description similarity. Each plan item that corresponds to an existing PRD item MUST reuse that item's `P-<id>`. Do NOT generate new IDs for items that already exist.
107
107
  3. **Preserve done items** — any existing item with `"status": "done"` carries forward as `"done"` with the same ID, description, and acceptance criteria. Do NOT reset done items to `"missing"`
108
- 4. **Carry forward in-progress items** — items with `"status": "missing"` or other non-done statuses keep their existing ID and reset to `"missing"`
108
+ 4. **Carry forward in-progress items** — items with `"status": "missing"` or other non-done statuses (except `"updated"`) keep their existing ID and reset to `"missing"`. Items with `"status": "updated"` keep their existing ID and remain `"updated"` — do NOT reset to `"missing"`
109
109
  5. **New items only** — only generate new `P-<uuid>` IDs for items in the plan that have no match in the existing PRD
110
110
  6. **Removed items** — if an existing PRD item has no match in the current plan, drop it from the output
111
111
  7. **Preserve plan-level fields** — keep `branch_strategy`, `feature_branch`, and `project` from the existing PRD unless the plan explicitly changes them
@@ -118,6 +118,7 @@ If the task description contains `mode: diff-aware-update`, you are updating an
118
118
 
119
119
  **Additional diff-aware rules** (on top of the reuse rules above):
120
120
  - **Done + modified** (plan added requirements/changed scope) → set `"status": "updated"` with same ID (engine re-opens the work item and dispatches to existing branch)
121
+ - **Already `"updated"`** (was previously flagged for rework and still needs changes) → keep `"status": "updated"` with same ID and revised description. Do NOT reset to `"missing"` — this preserves the existing branch reference so the agent continues from prior work
121
122
  - **Pending/failed items** → reset to `"missing"` with updated description if the plan changed their scope
122
123
 
123
124
  ## Important