@yemi33/minions 0.1.741 → 0.1.743

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,11 +1,13 @@
1
1
  # Changelog
2
2
 
3
- ## 0.1.741 (2026-04-09)
3
+ ## 0.1.743 (2026-04-09)
4
4
 
5
5
  ### Features
6
6
  - add prNumber field to pull-requests.json records (#711)
7
7
 
8
8
  ### Fixes
9
+ - link plan files as artifacts on plan/plan-to-prd work items
10
+ - escalate failed plan items instead of blocking indefinitely (closes #722) (#733)
9
11
  - handle NUL pseudo-file in Windows worktree cleanup (#731)
10
12
 
11
13
  ## 0.1.739 (2026-04-09)
@@ -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, projectPrPath, getPrLinks, addPrLink,
11
- log, ts, dateStamp, WI_STATUS, DONE_STATUSES, WORK_TYPE, PLAN_STATUS, PR_STATUS, DISPATCH_RESULT,
11
+ log, ts, dateStamp, WI_STATUS, DONE_STATUSES, PLAN_TERMINAL_STATUSES, WORK_TYPE, PLAN_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');
@@ -34,7 +34,7 @@ function checkPlanCompletion(meta, config) {
34
34
  const planItems = allWorkItems.filter(w => w.sourcePlan === planFile && w.itemType !== 'pr' && w.itemType !== 'verify');
35
35
  if (planItems.length === 0) return;
36
36
 
37
- // Hard completion gate: every PRD feature ID must have a corresponding work item in done status.
37
+ // Hard completion gate: every PRD feature ID must have a corresponding work item in a terminal state.
38
38
  const planFeatureIds = new Set((plan.missing_features || []).map(f => f.id).filter(Boolean));
39
39
  const workItemById = {};
40
40
  for (const w of planItems) { if (w.id) workItemById[w.id] = w; }
@@ -51,20 +51,36 @@ function checkPlanCompletion(meta, config) {
51
51
  return;
52
52
  }
53
53
 
54
- // Check 2: every feature's work item must be done (or PRD item marked done externally)
55
- const notDone = [...planFeatureIds].filter(id => {
54
+ // Check 2: every feature must be in a terminal state (done, failed, or cancelled).
55
+ // Failed/cancelled items are unrecoverable — waiting on them blocks the plan indefinitely.
56
+ const notTerminal = [...planFeatureIds].filter(id => {
56
57
  const w = workItemById[id];
57
- if (w && DONE_STATUSES.has(w.status)) return false;
58
+ if (w && PLAN_TERMINAL_STATUSES.has(w.status)) return false;
58
59
  const prdItem = (plan.missing_features || []).find(f => f.id === id);
59
- return !(prdItem && DONE_STATUSES.has(prdItem.status));
60
+ return !(prdItem && PLAN_TERMINAL_STATUSES.has(prdItem.status));
60
61
  });
61
- if (notDone.length > 0) {
62
- log('info', `Plan ${planFile}: waiting for done on ${notDone.length}/${planFeatureIds.size} item(s): ${notDone.join(', ')}`);
62
+ if (notTerminal.length > 0) {
63
+ log('info', `Plan ${planFile}: waiting for ${notTerminal.length}/${planFeatureIds.size} item(s) to reach terminal state: ${notTerminal.join(', ')}`);
63
64
  return;
64
65
  }
65
66
 
66
67
  const doneItems = planItems.filter(w => DONE_STATUSES.has(w.status));
67
- const failedItems = planItems.filter(w => w.status === WI_STATUS.FAILED);
68
+ const failedItems = planItems.filter(w => w.status === WI_STATUS.FAILED || w.status === WI_STATUS.CANCELLED);
69
+
70
+ // Escalate failed/cancelled items to human — write inbox alert (deduped by slug)
71
+ if (failedItems.length > 0) {
72
+ const alertSlug = `plan-failure-escalation-${planFile.replace('.json', '')}`;
73
+ const failDetails = failedItems.map(w =>
74
+ `- \`${w.id}\`: ${w.title || 'Unknown'} — ${w.failReason || w.status}`
75
+ ).join('\n');
76
+ shared.writeToInbox('engine', alertSlug,
77
+ `# Plan Items Failed: ${plan.plan_summary || planFile}\n\n` +
78
+ `**${failedItems.length}** of ${planFeatureIds.size} item(s) failed or were cancelled:\n\n${failDetails}\n\n` +
79
+ `The plan is completing with partial results (${doneItems.length} done, ${failedItems.length} failed).\n` +
80
+ `Review failed items and re-dispatch manually if needed.\n`
81
+ );
82
+ log('warn', `Plan ${planFile}: ${failedItems.length} item(s) failed/cancelled — escalating to human: ${failedItems.map(w => w.id).join(', ')}`);
83
+ }
68
84
 
69
85
  // 1. Mark plan as completed
70
86
  plan.status = PLAN_STATUS.COMPLETED;
package/engine/queries.js CHANGED
@@ -754,6 +754,8 @@ function getWorkItems(config) {
754
754
  }
755
755
  if (item.branch || item.featureBranch) arts.branch = item.branch || item.featureBranch;
756
756
  if (item.sourcePlan) arts.sourcePlan = item.sourcePlan;
757
+ if (item._planFileName) arts.plan = item._planFileName;
758
+ else if (item.planFile) arts.plan = item.planFile;
757
759
  if (item._pr) arts.pr = item._pr;
758
760
  if (Object.keys(arts).length > 0) item._artifacts = arts;
759
761
  }
package/engine/shared.js CHANGED
@@ -562,6 +562,9 @@ const WI_STATUS = {
562
562
  // Read-side: accept legacy aliases for backward compat with old data/clients.
563
563
  // Write-side: only WI_STATUS.DONE is written (cleanup.js migrates old values on each run).
564
564
  const DONE_STATUSES = new Set([WI_STATUS.DONE, 'in-pr', 'implemented', 'complete']);
565
+ // Terminal statuses for plan completion — item won't progress further (done, failed, cancelled).
566
+ // Used by checkPlanCompletion to unblock the gate when items are in an unrecoverable state.
567
+ const PLAN_TERMINAL_STATUSES = new Set([...DONE_STATUSES, WI_STATUS.FAILED, WI_STATUS.CANCELLED]);
565
568
  const WORK_TYPE = {
566
569
  IMPLEMENT: 'implement', IMPLEMENT_LARGE: 'implement:large', FIX: 'fix', REVIEW: 'review',
567
570
  VERIFY: 'verify', PLAN: 'plan', PLAN_TO_PRD: 'plan-to-prd', DECOMPOSE: 'decompose',
@@ -989,7 +992,7 @@ module.exports = {
989
992
  KB_CATEGORIES,
990
993
  classifyInboxItem,
991
994
  ENGINE_DEFAULTS,
992
- WI_STATUS, DONE_STATUSES, WORK_TYPE, PLAN_STATUS, PR_STATUS, DISPATCH_RESULT,
995
+ WI_STATUS, DONE_STATUSES, PLAN_TERMINAL_STATUSES, WORK_TYPE, PLAN_STATUS, PR_STATUS, DISPATCH_RESULT,
993
996
  PIPELINE_STATUS, STAGE_TYPE, MEETING_STATUS, AGENT_STATUS,
994
997
  FAILURE_CLASS, ESCALATION_POLICY, COMPLETION_FIELDS,
995
998
  DEFAULT_AGENT_METRICS,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.741",
3
+ "version": "0.1.743",
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"