@yemi33/minions 0.1.1631 → 0.1.1633

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,16 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.1633 (2026-04-30)
4
+
5
+ ### Features
6
+ - prevent completed stderr redispatch
7
+
8
+ ### Fixes
9
+ - yemi33/minions#1890
10
+
11
+ ### Other
12
+ - docs: relax minions playbook contracts
13
+
3
14
  ## 0.1.1631 (2026-04-30)
4
15
 
5
16
  ### Features
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "runtime": "copilot",
3
3
  "models": null,
4
- "cachedAt": "2026-04-30T08:43:57.738Z"
4
+ "cachedAt": "2026-04-30T08:56:05.025Z"
5
5
  }
@@ -184,6 +184,22 @@ function isRetryableFailureReason(reason = '', failureClass = '') {
184
184
  return !nonRetryable.some(s => r.includes(s));
185
185
  }
186
186
 
187
+ function isCompletedWorkItemForFailure(item) {
188
+ return !!item && (
189
+ item.status === WI_STATUS.DONE ||
190
+ (!!item.completedAt && (!!item._pr || !!item._prUrl))
191
+ );
192
+ }
193
+
194
+ function readLiveWorkItem(meta) {
195
+ const itemId = meta?.item?.id;
196
+ if (!itemId) return null;
197
+ const wiPath = lifecycle().resolveWorkItemPath(meta);
198
+ if (!wiPath) return null;
199
+ const items = safeJson(wiPath) || [];
200
+ return Array.isArray(items) ? items.find(i => i.id === itemId) || null : null;
201
+ }
202
+
187
203
  // ─── Complete Dispatch ───────────────────────────────────────────────────────
188
204
 
189
205
  function completeDispatch(id, result = DISPATCH_RESULT.SUCCESS, reason = '', resultSummary = '', opts = {}) {
@@ -225,83 +241,96 @@ function completeDispatch(id, result = DISPATCH_RESULT.SUCCESS, reason = '', res
225
241
 
226
242
  // Update source work item status on failure + auto-retry with backoff
227
243
  const retryableFailure = isRetryableFailureReason(reason, failureClass);
228
- if (result === DISPATCH_RESULT.ERROR && item.meta?.dispatchKey && retryableFailure) setCooldownFailure(item.meta.dispatchKey);
229
-
244
+ let completedWorkItemFailure = false;
230
245
  if (processWorkItemFailure && result === DISPATCH_RESULT.ERROR && item.meta?.item?.id) {
231
- let retries = (item.meta.item._retryCount || 0);
246
+ // If the live item cannot be resolved, keep the existing retry path.
232
247
  try {
233
- const wi = queries.getWorkItems().find(i => i.id === item.meta.item.id);
234
- if (wi) retries = wi._retryCount || 0;
235
- } catch (e) { log('warn', 'read retry count: ' + e.message); }
236
- const maxRetries = ENGINE_DEFAULTS.maxRetries;
237
- // Use per-class retry limits from recovery.js when failureClass is available
238
- const classAllowsRetry = failureClass ? recovery().shouldRetry(failureClass, retries) : (retries < maxRetries);
239
- if (retryableFailure && classAllowsRetry) {
240
- log('info', `Dispatch error for ${item.meta.item.id} — auto-retry ${retries + 1}/${maxRetries}${failureClass ? ' [' + failureClass + ']' : ''}`);
241
- lifecycle().updateWorkItemStatus(item.meta, WI_STATUS.PENDING, '');
242
- // Remove this dispatch key from completed so dedupe doesn't block immediate redispatch.
243
- if (item.meta?.dispatchKey) {
244
- try {
245
- mutateDispatch((dp) => {
246
- dp.completed = Array.isArray(dp.completed) ? dp.completed.filter(d => d.meta?.dispatchKey !== item.meta.dispatchKey) : [];
247
- return dp;
248
- });
249
- } catch (e) { log('warn', 'clear dispatch for retry: ' + e.message); }
250
- }
251
- // Increment retry counter on the source work item
252
- try {
253
- const wiPath = lifecycle().resolveWorkItemPath(item.meta);
254
- if (wiPath) {
255
- mutateWorkItems(wiPath, items => {
256
- const wi = items.find(i => i.id === item.meta.item.id);
257
- if (wi && wi.status !== WI_STATUS.PAUSED && wi.status !== WI_STATUS.DONE && !wi.completedAt) {
258
- wi._retryCount = retries + 1;
259
- wi.status = WI_STATUS.PENDING;
260
- wi._lastRetryReason = reason || '';
261
- wi._lastRetryAt = ts();
262
- delete wi.failReason;
263
- delete wi.failedAt;
264
- delete wi.dispatched_at;
265
- delete wi.dispatched_to;
266
- delete wi._pendingReason;
267
- }
268
- });
269
- }
270
- } catch (e) { log('warn', 'increment retry counter: ' + e.message); }
248
+ completedWorkItemFailure = isCompletedWorkItemForFailure(readLiveWorkItem(item.meta));
249
+ } catch (e) { log('warn', 'read live work item before retry: ' + e.message); }
250
+ }
251
+ if (result === DISPATCH_RESULT.ERROR && item.meta?.dispatchKey && retryableFailure && !completedWorkItemFailure) {
252
+ setCooldownFailure(item.meta.dispatchKey);
253
+ }
254
+
255
+ if (processWorkItemFailure && result === DISPATCH_RESULT.ERROR && item.meta?.item?.id) {
256
+ if (completedWorkItemFailure) {
257
+ log('info', `Dispatch error for ${item.meta.item.id} ignored work item is already completed`);
271
258
  } else {
272
- // Human-readable labels for each failure class — used as fallback when reason is empty
273
- const CLASS_LABELS = {
274
- [FAILURE_CLASS.EMPTY_OUTPUT]: 'agent produced no output \u2014 likely crashed on startup',
275
- [FAILURE_CLASS.BUILD_FAILURE]: 'build/test/lint failure in output',
276
- [FAILURE_CLASS.MERGE_CONFLICT]: 'merge conflict',
277
- [FAILURE_CLASS.MAX_TURNS]: 'reached max turn limit',
278
- [FAILURE_CLASS.TIMEOUT]: 'timed out waiting for agent',
279
- [FAILURE_CLASS.SPAWN_ERROR]: 'agent process failed to start',
280
- [FAILURE_CLASS.NETWORK_ERROR]: 'network or API error',
281
- [FAILURE_CLASS.OUT_OF_CONTEXT]: 'context window exhausted',
282
- [FAILURE_CLASS.CONFIG_ERROR]: 'configuration error',
283
- [FAILURE_CLASS.PERMISSION_BLOCKED]: 'permission or auth failure',
284
- [FAILURE_CLASS.UNKNOWN]: 'unknown error',
285
- };
286
- const classLabel = failureClass ? (CLASS_LABELS[failureClass] || failureClass) : '';
287
- const effectiveReason = reason || classLabel || 'Unknown error';
288
- const classSuffix = failureClass ? ` [${failureClass.toUpperCase().replace(/-/g, '_')}]` : '';
289
- const finalReason = !retryableFailure
290
- ? `Non-retryable failure: ${effectiveReason}${classSuffix}`
291
- : (reason || `Failed after ${maxRetries} retries${classSuffix}`);
292
- lifecycle().updateWorkItemStatus(item.meta, WI_STATUS.FAILED, finalReason);
293
- // Surface blocked dependents in logs without creating failure inbox noise.
259
+ let retries = (item.meta.item._retryCount || 0);
294
260
  try {
295
- const config = getConfig();
296
- const failedId = item.meta.item.id;
297
- const blockedItems = [];
298
- const allItems = queries.getWorkItems(config);
299
- allItems.filter(w => w.status === WI_STATUS.PENDING && (w.depends_on || []).includes(failedId))
300
- .forEach(w => blockedItems.push(`- \`${w.id}\` — ${w.title}`));
301
-
302
- log('warn', `Work item ${failedId} failed: ${finalReason}` +
303
- (blockedItems.length > 0 ? `; blocked dependents: ${blockedItems.map(line => line.replace(/^- `([^`]+)`.*/, '$1')).join(', ')}` : '; no downstream items blocked'));
304
- } catch (e) { log('warn', 'summarize failure dependents: ' + e.message); }
261
+ const wi = queries.getWorkItems().find(i => i.id === item.meta.item.id);
262
+ if (wi) retries = wi._retryCount || 0;
263
+ } catch (e) { log('warn', 'read retry count: ' + e.message); }
264
+ const maxRetries = ENGINE_DEFAULTS.maxRetries;
265
+ // Use per-class retry limits from recovery.js when failureClass is available
266
+ const classAllowsRetry = failureClass ? recovery().shouldRetry(failureClass, retries) : (retries < maxRetries);
267
+ if (retryableFailure && classAllowsRetry) {
268
+ log('info', `Dispatch error for ${item.meta.item.id} auto-retry ${retries + 1}/${maxRetries}${failureClass ? ' [' + failureClass + ']' : ''}`);
269
+ lifecycle().updateWorkItemStatus(item.meta, WI_STATUS.PENDING, '');
270
+ // Remove this dispatch key from completed so dedupe doesn't block immediate redispatch.
271
+ if (item.meta?.dispatchKey) {
272
+ try {
273
+ mutateDispatch((dp) => {
274
+ dp.completed = Array.isArray(dp.completed) ? dp.completed.filter(d => d.meta?.dispatchKey !== item.meta.dispatchKey) : [];
275
+ return dp;
276
+ });
277
+ } catch (e) { log('warn', 'clear dispatch for retry: ' + e.message); }
278
+ }
279
+ // Increment retry counter on the source work item
280
+ try {
281
+ const wiPath = lifecycle().resolveWorkItemPath(item.meta);
282
+ if (wiPath) {
283
+ mutateWorkItems(wiPath, items => {
284
+ const wi = items.find(i => i.id === item.meta.item.id);
285
+ if (wi && wi.status !== WI_STATUS.PAUSED && wi.status !== WI_STATUS.DONE && !wi.completedAt) {
286
+ wi._retryCount = retries + 1;
287
+ wi.status = WI_STATUS.PENDING;
288
+ wi._lastRetryReason = reason || '';
289
+ wi._lastRetryAt = ts();
290
+ delete wi.failReason;
291
+ delete wi.failedAt;
292
+ delete wi.dispatched_at;
293
+ delete wi.dispatched_to;
294
+ delete wi._pendingReason;
295
+ }
296
+ });
297
+ }
298
+ } catch (e) { log('warn', 'increment retry counter: ' + e.message); }
299
+ } else {
300
+ // Human-readable labels for each failure class — used as fallback when reason is empty
301
+ const CLASS_LABELS = {
302
+ [FAILURE_CLASS.EMPTY_OUTPUT]: 'agent produced no output \u2014 likely crashed on startup',
303
+ [FAILURE_CLASS.BUILD_FAILURE]: 'build/test/lint failure in output',
304
+ [FAILURE_CLASS.MERGE_CONFLICT]: 'merge conflict',
305
+ [FAILURE_CLASS.MAX_TURNS]: 'reached max turn limit',
306
+ [FAILURE_CLASS.TIMEOUT]: 'timed out waiting for agent',
307
+ [FAILURE_CLASS.SPAWN_ERROR]: 'agent process failed to start',
308
+ [FAILURE_CLASS.NETWORK_ERROR]: 'network or API error',
309
+ [FAILURE_CLASS.OUT_OF_CONTEXT]: 'context window exhausted',
310
+ [FAILURE_CLASS.CONFIG_ERROR]: 'configuration error',
311
+ [FAILURE_CLASS.PERMISSION_BLOCKED]: 'permission or auth failure',
312
+ [FAILURE_CLASS.UNKNOWN]: 'unknown error',
313
+ };
314
+ const classLabel = failureClass ? (CLASS_LABELS[failureClass] || failureClass) : '';
315
+ const effectiveReason = reason || classLabel || 'Unknown error';
316
+ const classSuffix = failureClass ? ` [${failureClass.toUpperCase().replace(/-/g, '_')}]` : '';
317
+ const finalReason = !retryableFailure
318
+ ? `Non-retryable failure: ${effectiveReason}${classSuffix}`
319
+ : (reason || `Failed after ${maxRetries} retries${classSuffix}`);
320
+ lifecycle().updateWorkItemStatus(item.meta, WI_STATUS.FAILED, finalReason);
321
+ // Surface blocked dependents in logs without creating failure inbox noise.
322
+ try {
323
+ const config = getConfig();
324
+ const failedId = item.meta.item.id;
325
+ const blockedItems = [];
326
+ const allItems = queries.getWorkItems(config);
327
+ allItems.filter(w => w.status === WI_STATUS.PENDING && (w.depends_on || []).includes(failedId))
328
+ .forEach(w => blockedItems.push(`- \`${w.id}\` — ${w.title}`));
329
+
330
+ log('warn', `Work item ${failedId} failed: ${finalReason}` +
331
+ (blockedItems.length > 0 ? `; blocked dependents: ${blockedItems.map(line => line.replace(/^- `([^`]+)`.*/, '$1')).join(', ')}` : '; no downstream items blocked'));
332
+ } catch (e) { log('warn', 'summarize failure dependents: ' + e.message); }
333
+ }
305
334
  }
306
335
  }
307
336
 
@@ -560,10 +560,18 @@ function updateWorkItemStatus(meta, status, reason) {
560
560
  const wiPath = resolveWorkItemPath(meta);
561
561
  if (!wiPath) return;
562
562
 
563
+ let completionGuarded = false;
563
564
  mutateJsonFileLocked(wiPath, (items) => {
564
565
  if (!items || !Array.isArray(items)) return items;
565
566
  const target = items.find(i => i.id === itemId);
566
567
  if (!target) return items;
568
+ if (status !== WI_STATUS.DONE && (
569
+ target.status === WI_STATUS.DONE ||
570
+ (!!target.completedAt && (!!target._pr || !!target._prUrl))
571
+ )) {
572
+ completionGuarded = true;
573
+ return items;
574
+ }
567
575
 
568
576
  if (meta.source === 'central-work-item-fanout') {
569
577
  if (!target.agentResults) target.agentResults = {};
@@ -609,6 +617,10 @@ function updateWorkItemStatus(meta, status, reason) {
609
617
  return items;
610
618
  }, { defaultValue: [], skipWriteIfUnchanged: true });
611
619
 
620
+ if (completionGuarded) {
621
+ log('info', `Work item ${itemId} already completed — ignoring ${status} status update`);
622
+ return;
623
+ }
612
624
  log('info', `Work item ${itemId} → ${status}${reason ? ': ' + reason : ''}`);
613
625
  syncPrdItemStatus(itemId, status, meta.item?.sourcePlan);
614
626
  }
@@ -570,7 +570,7 @@ function buildAgentContext(agentId, config, project) {
570
570
  } catch (e) { log('warn', 'read central pull-requests: ' + e.message); }
571
571
  if (allPrs.length > 0) {
572
572
  const prLines = allPrs.map(pr =>
573
- `- **${pr.id}** (${pr._project}): ${(pr.title || '').slice(0, 80)} [${(pr.reviewStatus || 'pending')}${pr.buildStatus === 'failing' ? ', BUILD FAILING' : ''}]${pr.branch ? ' branch: `' + pr.branch + '`' : ''}${pr._context ? ' — ' + pr._context.slice(0, 100) : ''}`
573
+ `- **${pr.id}** (${pr._project}): ${(pr.title || '').slice(0, 80)} [${(pr.reviewStatus || 'pending')}${pr.buildStatus === 'failing' ? ', BUILD FAILING' : ''}]${pr.branch ? ' branch: `' + pr.branch + '`' : ''}${formatPrContextSuffix(pr)}`
574
574
  );
575
575
  context += `## Active Pull Requests\n\n${prLines.join('\n')}\n\n`;
576
576
  }
@@ -589,6 +589,18 @@ function buildAgentContext(agentId, config, project) {
589
589
  return context;
590
590
  }
591
591
 
592
+ function formatPrContextSuffix(pr) {
593
+ if (!Object.prototype.hasOwnProperty.call(pr, '_context')) return '';
594
+ const value = pr._context;
595
+ if (value === undefined || value === '') return '';
596
+ if (typeof value === 'string') return ` — ${value.slice(0, 100)}`;
597
+
598
+ const type = value === null ? 'null' : Array.isArray(value) ? 'array' : typeof value;
599
+ const serialized = (value !== null && typeof value === 'object') ? JSON.stringify(value) : String(value);
600
+ const preview = serialized ? `: ${serialized.slice(0, 100)}` : '';
601
+ return ` — [invalid _context: expected string, got ${type}${preview}]`;
602
+ }
603
+
592
604
  // ─── Work Discovery Helpers ──────────────────────────────────────────────────
593
605
 
594
606
  function buildBaseVars(agentId, config, project) {
package/engine.js CHANGED
@@ -2411,6 +2411,7 @@ function discoverFromWorkItems(config, project) {
2411
2411
 
2412
2412
  for (const item of items) {
2413
2413
  try {
2414
+ if (isItemCompleted(item)) continue;
2414
2415
  // Re-evaluate failed items: if deps have recovered, reset to pending
2415
2416
  if (item.status === WI_STATUS.FAILED && !isItemCompleted(item) && item.failReason === 'Dependency failed — cannot proceed') {
2416
2417
  const depStatus = areDependenciesMet(item, config);
@@ -2908,6 +2909,7 @@ function discoverCentralWorkItems(config) {
2908
2909
 
2909
2910
  for (const item of items) {
2910
2911
  try {
2912
+ if (isItemCompleted(item)) continue;
2911
2913
  if (item.status !== WI_STATUS.QUEUED && item.status !== WI_STATUS.PENDING) continue;
2912
2914
 
2913
2915
  const key = `central-work-${item.id}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.1631",
3
+ "version": "0.1.1633",
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/fix.md CHANGED
@@ -25,33 +25,31 @@ Before starting work, run `git status` and verify the worktree is clean and on t
25
25
 
26
26
  Use subagents only for genuinely parallel, independent tasks. For sequential work, single-file edits, searches, and file reads, work directly — do not spawn subagents.
27
27
 
28
- ## How to Fix
28
+ ## Delivery Contract
29
29
 
30
- 1. You are already in the correct worktree on branch `{{pr_branch}}`. Do NOT create additional worktrees.
30
+ Handle this like the PR author responding directly from a CLI:
31
31
 
32
- 2. For each issue listed above, use your judgment:
33
- - **Fix it** if the feedback is valid and improves the code
34
- - **Explain your rationale** if you believe the current approach is correct reply on the review thread explaining why, with specific reasoning (e.g., performance, consistency with codebase patterns, intentional design choice). Do NOT silently ignore feedback — always respond.
32
+ - You are already in the correct worktree on branch `{{pr_branch}}`. Do NOT create additional worktrees.
33
+ - For each review finding, use engineering judgment:
34
+ - Fix it if the feedback is valid and improves correctness, safety, maintainability, or test coverage.
35
+ - If the current approach is intentionally correct, reply with specific rationale instead of silently changing code or ignoring the thread.
36
+ - Handle merge conflicts when needed, preserving the PR's intended changes while keeping the branch reviewable.
37
+ - Do not add unrelated cleanups or broaden the PR beyond the review feedback unless that is necessary to make the fix correct.
35
38
 
36
- 3. Handle merge conflicts if any:
37
- - If `git pull` or the PR shows conflicts, resolve them in the worktree
38
- - Prefer the PR branch changes, commit the resolution
39
+ ## Validation
39
40
 
40
- ## Build & Test (MANDATORY before pushing)
41
+ Before pushing, prove the review fix did not break the branch:
41
42
 
42
- Before pushing, verify the fix doesn't break anything:
43
-
44
- 1. **Build** the project using its build system (check CLAUDE.md, README, package.json, Makefile). If the build fails, fix it before proceeding.
45
- 2. **Run the full test suite** using whatever command the project specifies (check CLAUDE.md, agent.md, README, or package.json scripts).
46
- 3. If any tests fail due to your changes, fix them before pushing.
47
- 4. If the build fails 3 times, report the errors in your PR comment and stop.
48
- 5. Do NOT push code that breaks existing tests or the build.
43
+ - Use the project's source of truth for commands: `CLAUDE.md`, README, package scripts, Makefile, or equivalent build config.
44
+ - Run checks that are relevant to the addressed findings. Prefer the full suite when practical.
45
+ - Fix regressions you introduced. If failures are pre-existing or unrelated, capture the evidence and include it in the PR comment.
46
+ - Do not push code that breaks existing tests or the build because of your changes.
49
47
 
50
48
  > ⚠️ **Long builds (Gradle, MSBuild, dotnet, fresh `npm install`)**: any command that may stay silent for more than ~4 minutes will be killed by the heartbeat monitor. Run it via `Bash(run_in_background: true)` then `Monitor` to stream stdout, OR pass an explicit `timeout` (max 600000 ms). See **Long-Running Build / Test Commands** below for the full pattern.
51
49
 
52
- ## Push & Comment on PR
50
+ ## Publish & Comment on PR
53
51
 
54
- Only after build and tests pass:
52
+ After the fix is validated or any unavoidable limitation is clearly documented, commit only relevant files and push:
55
53
 
56
54
  ```bash
57
55
  git add <specific files>
@@ -76,7 +74,7 @@ After pushing, respond to each review comment/thread:
76
74
 
77
75
  ## When to Stop
78
76
 
79
- Your task is complete once you have: (1) confirmed build and tests pass, (2) pushed the fix, (3) commented on the PR, and (4) resolved addressed review threads. Do NOT continue exploring unrelated code or making additional improvements. Stop immediately.
77
+ Your task is complete when each review finding has either been fixed or answered with rationale, the validation story is truthful and sufficient for review, the fix is pushed if code changed, the PR is commented, and addressed threads are resolved. Do NOT continue into unrelated improvements.
80
78
 
81
79
  **NEVER run `gh pr merge` or any merge command on this PR.** The engine handles merging after review approval. Self-merging bypasses the review cycle and is prohibited.
82
80
 
@@ -47,29 +47,30 @@ Before starting work, run `git status` and verify the worktree is clean and on t
47
47
 
48
48
  Use subagents only for genuinely parallel, independent tasks. For sequential work, single-file edits, searches, and file reads, work directly — do not spawn subagents.
49
49
 
50
- ## Instructions
50
+ ## Delivery Contract
51
51
 
52
- 1. Read relevant source code and reference implementations before writing anything
53
- 2. Check what prior plan items already committed on this branch (`git log {{main_branch}}..HEAD`)
54
- 3. Follow existing patterns exactly — check `CLAUDE.md` for conventions
55
- 4. Build on existing work — don't duplicate or conflict with prior commits
52
+ Deliver this as if the user asked you directly in a CLI, with the added constraint that this branch may already contain related work:
56
53
 
57
- ## Build & Test (MANDATORY before pushing)
54
+ - Understand the requested behavior and how prior commits on `{{branch_name}}` affect it.
55
+ - Read the smallest useful set of source, tests, docs, and comparable implementations needed to make the change correctly.
56
+ - Follow existing project conventions from `CLAUDE.md` and nearby code.
57
+ - Build on previous plan-item work instead of duplicating or conflicting with it.
58
+ - Make the complete change required by this item; do not add unrelated cleanups or speculative improvements.
58
59
 
59
- After implementation, verify everything works:
60
+ ## Validation
60
61
 
61
- 1. **Build** the project using its build system (check CLAUDE.md, package.json, README, Makefile)
62
- 2. Verify the build succeeds with your changes AND all prior commits on this branch
63
- 3. **Run the full test suite** fix any regressions you introduced
64
- 4. **Run any other checks** the repo defines (linting, type checking, formatting)
65
- 5. If the build fails 3 times, report the errors in your findings and stop
66
- 6. Do NOT push code with a broken build or failing tests that you introduced
62
+ Before publishing, prove the shared branch still works with your change included:
63
+
64
+ - Use the project's source of truth for commands: `CLAUDE.md`, README, package scripts, Makefile, or equivalent build config.
65
+ - Run checks that are relevant to this item and to the integrated branch state. Prefer the full suite when practical.
66
+ - Fix regressions you introduced. If failures are pre-existing or caused by earlier branch work, capture the evidence and say so clearly.
67
+ - Do not push code with a broken build or failing tests that you introduced.
67
68
 
68
69
  > ⚠️ **Long builds (Gradle, MSBuild, dotnet, fresh `npm install`)**: any command that may stay silent for more than ~4 minutes will be killed by the heartbeat monitor. Run it via `Bash(run_in_background: true)` then `Monitor` to stream stdout, OR pass an explicit `timeout` (max 600000 ms). See **Long-Running Build / Test Commands** below for the full pattern.
69
70
 
70
- ## Push
71
+ ## Publish
71
72
 
72
- Only after build and tests pass:
73
+ After the change is validated or any unavoidable limitation is clearly documented, commit only the relevant files and push to the shared branch:
73
74
 
74
75
  ```bash
75
76
  git add <specific files>
@@ -79,7 +80,7 @@ git push origin {{branch_name}}
79
80
 
80
81
  ## When to Stop
81
82
 
82
- Your task is complete once you have: (1) confirmed build and tests pass, and (2) pushed to the shared branch. Do NOT create a PR — the engine creates one when all plan items are done. Stop after pushing.
83
+ Your task is complete when the requested implementation is delivered, the validation story is truthful and sufficient for review, and your commit is pushed to the shared branch. Do NOT create a PR — the engine creates one when all plan items are done.
83
84
 
84
85
  ## Completion
85
86
 
@@ -38,32 +38,35 @@ Before starting work, run `git status` and verify the worktree is clean and on t
38
38
 
39
39
  Use subagents only for genuinely parallel, independent tasks (e.g., editing files in unrelated modules simultaneously). For sequential work, single-file edits, searches, and file reads, work directly — do not spawn subagents.
40
40
 
41
- ## Instructions
41
+ ## Delivery Contract
42
42
 
43
- 1. Read relevant source code and reference implementations before writing anything
44
- 2. Follow existing patterns exactly — check `agents/create-agent/` or the closest comparable agent
45
- 3. Follow the project's logging and coding conventions (check CLAUDE.md)
43
+ Deliver this as if the user asked you directly in a CLI:
44
+
45
+ - Understand the requested behavior and relevant acceptance criteria before editing.
46
+ - Read the smallest useful set of source, tests, docs, and comparable implementations needed to make the change correctly.
47
+ - Follow existing project conventions, including logging, typing, error handling, and test structure.
48
+ - Make the complete change required by the task; do not add unrelated cleanups or speculative improvements.
49
+ - Keep working through failures you introduced until the implementation is either correct or honestly blocked with concrete evidence.
46
50
 
47
51
  ## Git Workflow
48
52
 
49
53
  You are already running in a git worktree on branch `{{branch_name}}`. Do NOT create additional worktrees — the engine pre-created one for you.
50
54
  Do NOT remove the worktree — the engine handles cleanup automatically.
51
55
 
52
- ## Build & Test (MANDATORY before pushing)
56
+ ## Validation
53
57
 
54
- Build and test before pushing:
58
+ Before publishing, prove the change with the repo's own documented checks:
55
59
 
56
- 1. **Build** the project using its build system (check CLAUDE.md, package.json, README, or Makefile). Retry up to 3 times; if it still fails, report the errors and stop.
57
- 2. **Run the full test suite** using the command the project defines. Fix regressions you introduced and re-run until your changes are green.
58
- 3. If tests were already failing before your changes, note that in the PR description but do not block on pre-existing failures.
59
- 4. **Run any other checks** the repo defines (linting, type checking, formatting).
60
- 5. Do NOT push code with a broken build or failing tests that you introduced.
60
+ - Use the project's source of truth for commands: `CLAUDE.md`, README, package scripts, Makefile, or equivalent build config.
61
+ - Run the checks that are relevant to this task, including tests that cover the changed behavior. Prefer the full suite when practical.
62
+ - Fix regressions you introduced. If failures are pre-existing or outside the task, capture the evidence and make that explicit in the PR.
63
+ - Do not publish changes with a broken build or failing tests that you introduced.
61
64
 
62
65
  > ⚠️ **Long builds (Gradle, MSBuild, dotnet, fresh `npm install`)**: any command that may stay silent for more than ~4 minutes will be killed by the heartbeat monitor. Run it via `Bash(run_in_background: true)` then `Monitor` to stream stdout, OR pass an explicit `timeout` (max 600000 ms). See **Long-Running Build / Test Commands** below for the full pattern.
63
66
 
64
- ## Push & Create PR
67
+ ## Publish
65
68
 
66
- Only after build and tests pass:
69
+ After the change is validated or any unavoidable limitation is clearly documented, commit only the relevant files and push this branch:
67
70
 
68
71
  ```bash
69
72
  git add <specific files>
@@ -73,10 +76,12 @@ git push -u origin {{branch_name}}
73
76
 
74
77
  {{pr_section}}
75
78
 
79
+ PR creation is MANDATORY for implement tasks because the engine tracks review and completion from the PR.
80
+
76
81
  Include build/test status and run instructions in the PR description. If the project has a runnable app, include the localhost URL.
77
82
 
78
83
  ## When to Stop
79
84
 
80
- Your task is complete once you have: (1) confirmed build and tests pass, (2) pushed your branch, and (3) created the PR. Your final message MUST include the PR URL so the engine can track it. Stop immediately after.
85
+ Your task is complete when the requested implementation is delivered, the validation story is truthful and sufficient for review, the branch is pushed, and the PR exists. Your final message MUST include the PR URL so the engine can track it.
81
86
 
82
87
  Do NOT run `gh pr merge` or any other merge command on your own PR. The engine reviews and merges PRs through a separate review cycle. Self-merging is prohibited.
@@ -17,6 +17,17 @@ Codex will review your changes — make sure your implementation is thorough and
17
17
 
18
18
  Your context window may be compacted or summarized mid-task by Claude's automatic context management. This is normal and expected for long-running tasks. Do NOT interpret compacted or truncated context as a signal to stop early, wrap up prematurely, or skip remaining work. Continue working toward your stated objective regardless of context window state — re-read key files if needed to recover context.
19
19
 
20
+ ## Delegated Task Contract
21
+
22
+ Treat a Minions assignment like the user typed the same task directly into a capable CLI agent. Preserve the user's actual task contract first; the playbook adds orchestration guardrails, not a rigid script for thinking or implementation.
23
+
24
+ - Optimize for the requested outcome, not for mechanically completing checklist steps.
25
+ - Use judgment to choose the smallest reliable workflow that fully satisfies the task.
26
+ - Read only the context needed to make correct decisions; do not perform broad archaeology unless the task requires it.
27
+ - Validate with the repo's own documented commands and acceptance criteria. If full validation is impossible or pre-existing failures block it, explain that precisely instead of inventing a green result.
28
+ - Prefer direct work over ceremony. Branches, PRs, inbox notes, completion blocks, and status comments exist for traceability; they should not change what "done" means for the user.
29
+ - Safety and observability rules still win: stay in the engine-created worktree, do not self-merge, do not edit engine-managed status files, do not hide failures, and leave enough evidence for the human and engine to track the result.
30
+
20
31
  ## Engine Rules (apply to all tasks)
21
32
 
22
33
  **Context compaction:** Your context window may be compacted mid-task by Claude's infrastructure. If you notice your earlier conversation history appears truncated or summarized, this is normal and expected. Do not interpret compaction as a signal to stop early or wrap up. Continue working toward your task objective — all relevant instructions and state remain available.
@@ -20,28 +20,31 @@ Team root: {{team_root}}
20
20
  Branch format: `feat/{{item_id}}-<short-description>`
21
21
  Keep branch names lowercase, use hyphens, max 60 chars.
22
22
 
23
- ## Steps
24
-
25
- 1. **Understand the task** read the description carefully, explore relevant code
26
- 2. **Navigate** to the correct project directory: `{{project_path}}`
27
- 3. You are already in a worktree on branch `{{branch_name}}`. Do NOT create additional worktrees.
28
- 4. **Implement** the changes
29
- 5. **Build** — using the repo's build system (check CLAUDE.md, package.json, README, Makefile). If it fails, fix and retry (up to 3 times).
30
- 6. **Run the full test suite** find the test command from project docs. Fix any regressions you introduced. Do NOT push with failing tests.
31
- 7. **Run any other checks** the repo defines (linting, type checking, formatting)
32
- 8. **Commit and push** (only after build and tests pass):
33
- ```bash
34
- git add <specific files>
35
- git commit -m "feat({{item_id}}): <description>"
36
- git push -u origin {{branch_name}}
37
- ```
38
- 9. **Create a PR:**
39
- {{pr_create_instructions}}
40
- - sourceRefName: `refs/heads/feat/{{item_id}}-<short-desc>`
41
- - targetRefName: `refs/heads/{{main_branch}}`
42
- - title: `feat({{item_id}}): <description>`
43
- 10. **Post implementation notes** as a PR thread comment:
44
- {{pr_comment_instructions}}
23
+ ## Delivery Contract
24
+
25
+ Treat this like the user typed the task directly into a CLI agent:
26
+
27
+ - Work in the correct project directory: `{{project_path}}`.
28
+ - You are already in a worktree on branch `{{branch_name}}`. Do NOT create additional worktrees.
29
+ - Understand the requested outcome, inspect the relevant source/tests/docs, and make the complete change needed.
30
+ - Follow existing repo conventions and avoid unrelated cleanups.
31
+ - Validate with the repo's documented build/test/check commands. Fix regressions you introduced; if failures are pre-existing or outside the task, document the evidence.
32
+ - Do NOT publish code with a broken build or failing tests that you introduced.
33
+
34
+ After the change is ready for review, commit only relevant files, push `{{branch_name}}`, create the PR, and post implementation notes with the validation result:
35
+
36
+ ```bash
37
+ git add <specific files>
38
+ git commit -m "feat({{item_id}}): <description>"
39
+ git push -u origin {{branch_name}}
40
+ ```
41
+
42
+ {{pr_create_instructions}}
43
+ - sourceRefName: `refs/heads/feat/{{item_id}}-<short-desc>`
44
+ - targetRefName: `refs/heads/{{main_branch}}`
45
+ - title: `feat({{item_id}}): <description>`
46
+
47
+ {{pr_comment_instructions}}
45
48
 
46
49
  Do NOT remove the worktree — the engine handles cleanup automatically.
47
50
 
@@ -59,6 +62,6 @@ If you encounter merge conflicts during push or PR creation:
59
62
 
60
63
  ## When to Stop
61
64
 
62
- Your task is complete once you have: (1) confirmed build and tests pass, (2) pushed your branch, and (3) created the PR. Do NOT continue beyond the task description. Stop immediately.
65
+ Your task is complete when the requested work item is delivered, the validation story is truthful and sufficient for review, the branch is pushed, and the PR exists. Do NOT continue into unrelated improvements.
63
66
 
64
67
  Do NOT run `gh pr merge` or any other merge command on your own PR. The engine reviews and merges PRs through a separate review cycle. Self-merging is prohibited.
package/routing.md CHANGED
@@ -25,6 +25,7 @@ Notes:
25
25
  - `_any_` means route to any available idle agent (lowest error rate first)
26
26
  - `implement:large` is for items with `estimated_complexity: "large"`
27
27
  - Engine falls back to any idle agent if both preferred and fallback are busy
28
+ - Routing selects an owner; it should not narrow the user's task contract. The assigned agent should behave like the user typed the same task directly into a CLI, with Minions adding only safety, status, and review guardrails.
28
29
 
29
30
  ## Rules
30
31