@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 +7 -1
- package/engine/cli.js +6 -0
- package/engine/queries.js +1 -0
- package/engine/timeout.js +3 -2
- package/engine.js +26 -10
- package/package.json +1 -1
- package/playbooks/plan-to-prd.md +33 -18
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 0.1.
|
|
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
|
-
//
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
const
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
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,
|
|
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.
|
|
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"
|
package/playbooks/plan-to-prd.md
CHANGED
|
@@ -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. **
|
|
18
|
-
3. **
|
|
19
|
-
4. **
|
|
20
|
-
5. **
|
|
21
|
-
6. **
|
|
22
|
-
7. **
|
|
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
|
|
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
|
-
##
|
|
95
|
+
## Reusing an Existing PRD
|
|
95
96
|
|
|
96
|
-
|
|
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
|
-
|
|
99
|
-
|
|
100
|
-
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
|