@yemi33/minions 0.1.872 → 0.1.874

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,6 +1,11 @@
1
1
  # Changelog
2
2
 
3
- ## 0.1.872 (2026-04-11)
3
+ ## 0.1.874 (2026-04-11)
4
+
5
+ ### Fixes
6
+ - include started_at in done/error agent status so duration renders
7
+
8
+ ## 0.1.873 (2026-04-11)
4
9
 
5
10
  ### Features
6
11
  - show last task duration in thought-process tab when agent is idle
@@ -10,6 +15,7 @@
10
15
  - optimistic running state on pipeline Run Now click
11
16
 
12
17
  ### Fixes
18
+ - short-circuit restart grace period for verifiably-dead agent processes (closes #869) (#886)
13
19
  - orphan recovery misclassifies mid-run agents as failed on engine restart
14
20
  - dep merge failures identify conflicting branch and auto-queue fix (closes #814) (#882)
15
21
  - always show doc-chat expand bar when thread is collapsed
package/engine/cli.js CHANGED
@@ -161,6 +161,7 @@ const commands = {
161
161
  }
162
162
  }
163
163
 
164
+ const hadPid = agentPid && agentPid > 0; // track before liveness check
164
165
  if (agentPid && agentPid > 0) {
165
166
  try {
166
167
  if (process.platform === 'win32') {
@@ -172,6 +173,11 @@ const commands = {
172
173
  } catch { agentPid = null; }
173
174
  }
174
175
 
176
+ // PID was found but confirmed dead — exempt from restart grace period (#869)
177
+ if (hadPid && !agentPid) {
178
+ e.engineRestartGraceExempt.add(item.id);
179
+ }
180
+
175
181
  if (agentPid) {
176
182
  // Load sessionId from session.json for steering support
177
183
  let sessionId = null;
package/engine/queries.js CHANGED
@@ -257,6 +257,7 @@ function getAgentStatus(agentId) {
257
257
  task: latest.task || '',
258
258
  dispatch_id: latest.id,
259
259
  type: latest.type || '',
260
+ started_at: latest.started_at || null,
260
261
  completed_at: latest.completed_at,
261
262
  resultSummary: latest.resultSummary || latest.reason || '',
262
263
  };
package/engine/timeout.js CHANGED
@@ -127,6 +127,7 @@ function checkSteering(config) {
127
127
  function checkTimeouts(config) {
128
128
  const activeProcesses = engine().activeProcesses;
129
129
  const engineRestartGraceUntil = engine().engineRestartGraceUntil;
130
+ const engineRestartGraceExempt = engine().engineRestartGraceExempt;
130
131
  const { completeDispatch } = dispatch();
131
132
  const { runPostCompletionHooks } = require('./lifecycle');
132
133
 
@@ -307,8 +308,8 @@ function checkTimeouts(config) {
307
308
  const procInfo = activeProcesses.get(item.id);
308
309
  if (procInfo?._steeringAt && Date.now() - procInfo._steeringAt < 60000) continue;
309
310
 
310
- if (!hasProcess && silentMs > effectiveTimeout && Date.now() > engineRestartGraceUntil) {
311
- // No tracked process AND no recent output past effective timeout AND grace period expired → orphaned
311
+ if (!hasProcess && silentMs > effectiveTimeout && (Date.now() > engineRestartGraceUntil || engineRestartGraceExempt?.has(item.id))) {
312
+ // No tracked process AND no recent output past effective timeout AND (grace period expired OR confirmed-dead at restart) → orphaned
312
313
  log('warn', `Orphan detected: ${item.agent} (${item.id}) — no process tracked, silent for ${silentSec}s${isBlocking ? ' (blocking timeout exceeded)' : ''}`);
313
314
  dispatch().updateAgentStatus(item.id, AGENT_STATUS.TIMED_OUT, `Orphaned — no process, silent for ${silentSec}s`);
314
315
  // Clear session so retry starts fresh
package/engine.js CHANGED
@@ -146,6 +146,7 @@ const activeProcesses = new Map(); // dispatchId → { proc, agentId, startedAt
146
146
  const realActivityMap = new Map(); // dispatchId → timestamp of last REAL agent output (not engine heartbeat)
147
147
  // tempAgents imported from engine/routing.js
148
148
  let engineRestartGraceUntil = 0; // timestamp — suppress orphan detection until this time
149
+ const engineRestartGraceExempt = new Set(); // dispatch IDs with confirmed-dead PIDs at restart — bypass grace period
149
150
 
150
151
  // Per-tick cache of refs that failed to fetch — avoids repeating 30s ETIMEDOUT for same missing ref
151
152
  // Cleared at the start of each tick cycle (see tickInner)
@@ -2591,15 +2592,29 @@ function discoverCentralWorkItems(config) {
2591
2592
  vars.plan_summary = (item.title || item.planFile).substring(0, 80);
2592
2593
  vars.plan_file = item.planFile || '';
2593
2594
  vars.project_name_lower = (firstProject?.name || 'project').toLowerCase();
2594
- // Generate unique PRD filenamecheck prd/ and prd/archive/ for collisions
2595
- const prdBase = vars.project_name_lower + '-' + dateStamp();
2596
- let prdFilename = prdBase + '.json';
2597
- const prdExisting = new Set([
2598
- ...safeReadDir(PRD_DIR).filter(f => f.endsWith('.json')),
2599
- ...safeReadDir(path.join(PRD_DIR, 'archive')).filter(f => f.endsWith('.json')),
2600
- ]);
2601
- let prdCounter = 2;
2602
- while (prdExisting.has(prdFilename)) { prdFilename = prdBase + '-' + prdCounter + '.json'; prdCounter++; }
2595
+ // Check if a PRD already exists for this plan reuse its filename to avoid duplicates (#884)
2596
+ let prdFilename = null;
2597
+ const prdFiles = safeReadDir(PRD_DIR).filter(f => f.endsWith('.json'));
2598
+ for (const pf of prdFiles) {
2599
+ const prd = safeJson(path.join(PRD_DIR, pf));
2600
+ if (prd?.source_plan === item.planFile) {
2601
+ prdFilename = pf;
2602
+ try { vars.existing_prd_json = fs.readFileSync(path.join(PRD_DIR, pf), 'utf8'); } catch (_) { /* ignore */ }
2603
+ log('info', `plan-to-prd: reusing existing PRD "${pf}" for plan "${item.planFile}" (#884)`);
2604
+ break;
2605
+ }
2606
+ }
2607
+ if (!prdFilename) {
2608
+ // Generate unique PRD filename — check prd/ and prd/archive/ for collisions
2609
+ const prdBase = vars.project_name_lower + '-' + dateStamp();
2610
+ prdFilename = prdBase + '.json';
2611
+ const prdExisting = new Set([
2612
+ ...prdFiles,
2613
+ ...safeReadDir(path.join(PRD_DIR, 'archive')).filter(f => f.endsWith('.json')),
2614
+ ]);
2615
+ let prdCounter = 2;
2616
+ while (prdExisting.has(prdFilename)) { prdFilename = prdBase + '-' + prdCounter + '.json'; prdCounter++; }
2617
+ }
2603
2618
  vars.prd_filename = prdFilename;
2604
2619
  mutations.set(item.id, Object.assign(mutations.get(item.id) || {}, { _prdFilename: prdFilename }));
2605
2620
  vars.branch_strategy_hint = item.branchStrategy
@@ -3155,7 +3170,8 @@ module.exports = {
3155
3170
 
3156
3171
  // Dispatch management (re-exported from engine/dispatch.js)
3157
3172
  mutateDispatch, addToDispatch, isRetryableFailureReason, completeDispatch, writeInboxAlert, updateAgentStatus,
3158
- activeProcesses, realActivityMap, get engineRestartGraceUntil() { return engineRestartGraceUntil; },
3173
+ activeProcesses, realActivityMap, engineRestartGraceExempt,
3174
+ get engineRestartGraceUntil() { return engineRestartGraceUntil; },
3159
3175
  set engineRestartGraceUntil(v) { engineRestartGraceUntil = v; },
3160
3176
 
3161
3177
  // Agent lifecycle
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.872",
3
+ "version": "0.1.874",
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"
@@ -14,12 +14,13 @@ A user has provided a plan. Analyze it against the codebase and produce a struct
14
14
  ## Instructions
15
15
 
16
16
  1. **Read the plan carefully** — understand the goals, scope, and requirements
17
- 2. **Explore the codebase** at `{{project_path}}`understand the existing structure to write accurate descriptions and acceptance criteria. Do NOT use observations about existing PRs or partial work to set item statuses — all items are always `"missing"` regardless of codebase state
18
- 3. **Break the plan into discrete, implementable items**each should be a single PR's worth of work
19
- 4. **Estimate complexity** `small` (< 1 file), `medium` (2-5 files), `large` (6+ files or cross-cutting)
20
- 5. **Order by dependency** — items that others depend on come first
21
- 6. **Use unique item IDs** — generate a short uuid for each item (e.g. `P-a3f9b2c1`). Do not use sequential `P001`/`P002` — IDs must be globally unique across all PRDs to avoid collisions
22
- 7. **Identify open questions** — flag anything ambiguous in the plan that needs user input
17
+ 2. **Check for an existing PRD**if the engine provides `existing_prd_json` below, a PRD already exists for this plan. See "Reusing an Existing PRD" section for how to preserve item IDs and done statuses. If no existing PRD is provided, this is a fresh run — all items start as `"missing"`.
18
+ 3. **Explore the codebase** at `{{project_path}}` understand the existing structure to write accurate descriptions and acceptance criteria. Do NOT use observations about existing PRs or partial work to set item statuses status is determined only by existing PRD items (step 2), not codebase state
19
+ 4. **Break the plan into discrete, implementable items** each should be a single PR's worth of work
20
+ 5. **Estimate complexity** — `small` (< 1 file), `medium` (2-5 files), `large` (6+ files or cross-cutting)
21
+ 6. **Order by dependency** — items that others depend on come first
22
+ 7. **Use unique item IDs** — generate a short uuid for each item (e.g. `P-a3f9b2c1`). Do not use sequential `P001`/`P002` — IDs must be globally unique across all PRDs to avoid collisions. **If reusing an existing PRD, keep all existing IDs — only generate new UUIDs for genuinely new items.**
23
+ 8. **Identify open questions** — flag anything ambiguous in the plan that needs user input
23
24
 
24
25
  ## Output
25
26
 
@@ -83,7 +84,7 @@ When using `parallel`:
83
84
 
84
85
  Rules for items:
85
86
  - IDs must be `P-<uuid>` format (e.g. `P-a3f9b2c1`) — globally unique, never sequential
86
- - **`status` is always `"missing"`** — do not set `done`, `complete`, `implemented`, or any other value, even if you observe active PRs or completed work in the codebase. Status is exclusively engine-managed after the PRD is written. Pre-setting any other status causes items to be silently skipped by the engine and breaks dependency resolution for all downstream items.
87
+ - **`status` is `"missing"` for new items** — do not set `done`, `complete`, `implemented`, or any other value based on codebase observations. The only exception is when reusing an existing PRD (see below) items already `"done"` in the existing PRD carry forward as `"done"`. Pre-setting any other status on new items causes them to be silently skipped by the engine.
87
88
  - **Do NOT include a "verify" or "test" or "integration test" item** — the engine automatically creates a verify task when all PRD items are done. Adding one manually creates a duplicate that blocks plan completion.
88
89
  - **`project` field is REQUIRED** — set it to the project name where the code changes go (e.g., `"OfficeAgent"`, `"office-bohemia"`). Cross-repo plans must route each item to the correct project. The engine materializes items into that project's work queue.
89
90
  - `depends_on` lists IDs of items that must be done first
@@ -91,19 +92,33 @@ Rules for items:
91
92
  - Include `acceptance_criteria` so reviewers know when it's done
92
93
  - Aim for 5-25 items depending on plan scope. If more than 25, group related work
93
94
 
94
- ## Updating an Existing PRD
95
+ ## Reusing an Existing PRD
95
96
 
96
- If the task description contains `mode: diff-aware-update`, you are updating an existing PRD, not creating one from scratch. The plan was revised and you need to produce an updated PRD that reflects the changes while preserving existing work.
97
+ When the engine detects an existing PRD for this plan (`source_plan` match), it passes the content below. If this section is empty or absent, skip to normal generation (all items `"missing"` with new UUIDs).
97
98
 
98
- **Rules for diff-aware updates:**
99
- - **Read the existing PRD file first** — it's at `{{team_root}}/prd/{{prd_filename}}`
100
- - **Preserve item IDs** — do NOT generate new IDs for items that already exist. The engine maps work items by ID.
101
- - **Done + unchanged** → keep `"status": "done"` and the same ID (no work dispatched)
102
- - **Done + modified** (plan added requirements/scope) → set `"status": "updated"` with same ID (engine re-opens the work item and dispatches to existing branch)
103
- - **New items** in the updated plan → generate new `P-<uuid>` IDs, set `"status": "missing"`
104
- - **Removed items** (in old PRD but not in updated plan) drop from the PRD entirely (engine cancels pending work items)
105
- - **Pending/failed items** reset to `"missing"` with updated description
106
- - Preserve `branch_strategy`, `feature_branch`, `project`, and other plan-level fields from the existing PRD unless the plan explicitly changes them
99
+ <existing-prd>
100
+ {{existing_prd_json}}
101
+ </existing-prd>
102
+
103
+ **When an existing PRD is provided:**
104
+
105
+ 1. **Parse the existing PRD JSON** extract all `missing_features` items with their `id`, `status`, and metadata
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
+ 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"`
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
+ 6. **Removed items** — if an existing PRD item has no match in the current plan, drop it from the output
111
+ 7. **Preserve plan-level fields** — keep `branch_strategy`, `feature_branch`, and `project` from the existing PRD unless the plan explicitly changes them
112
+
113
+ This ensures re-running plan-to-prd on the same plan produces a clean update (new items added, completed items preserved, IDs stable) rather than a full reset with orphaned duplicates.
114
+
115
+ ## Updating an Existing PRD (Diff-Aware)
116
+
117
+ If the task description contains `mode: diff-aware-update`, you are updating an existing PRD because the plan was revised. Follow the same reuse rules above, plus these additional diff-aware rules:
118
+
119
+ **Additional diff-aware rules** (on top of the reuse rules above):
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
+ - **Pending/failed items** → reset to `"missing"` with updated description if the plan changed their scope
107
122
 
108
123
  ## Important
109
124