@yemi33/minions 0.1.1632 → 0.1.1634

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,9 +1,24 @@
1
1
  # Changelog
2
2
 
3
- ## 0.1.1632 (2026-04-30)
3
+ ## 0.1.1634 (2026-04-30)
4
+
5
+ ### Features
6
+ - build-and-test CC action + docs playbook
7
+
8
+ ## 0.1.1633 (2026-04-30)
9
+
10
+ ### Features
11
+ - prevent completed stderr redispatch
12
+
13
+ ### Fixes
14
+ - yemi33/minions#1890
15
+
16
+ ### Other
17
+ - docs: relax minions playbook contracts
18
+
19
+ ## 0.1.1631 (2026-04-30)
4
20
 
5
21
  ### Features
6
- - guard malformed PR context
7
22
  - clear stale pending reason on retry
8
23
 
9
24
  ## 0.1.1629 (2026-04-29)
package/dashboard.js CHANGED
@@ -25,6 +25,9 @@ const ado = require('./engine/ado');
25
25
  const gh = require('./engine/github');
26
26
  const issues = require('./engine/issues');
27
27
  const watchesMod = require('./engine/watches');
28
+ const routing = require('./engine/routing');
29
+ const playbook = require('./engine/playbook');
30
+ const dispatchMod = require('./engine/dispatch');
28
31
  const os = require('os');
29
32
 
30
33
  const { safeRead, safeReadDir, safeWrite, safeJson, safeJsonObj, safeJsonArr, safeUnlink, mutateJsonFileLocked, mutateWorkItems, getProjects: _getProjects, DONE_STATUSES, WI_STATUS, reopenWorkItem } = shared;
@@ -1238,6 +1241,49 @@ async function executeCCActions(actions) {
1238
1241
  results.push({ type: action.type, id, ok: true });
1239
1242
  break;
1240
1243
  }
1244
+ case 'build-and-test': {
1245
+ // Resolve PR by number, ID, or URL — same lookup that drives the link-pr / PR-row paths.
1246
+ const allPrs = getPullRequests().filter(p => !p._ghost);
1247
+ const pr = shared.findPrRecord(allPrs, action.pr) || null;
1248
+ if (!pr) {
1249
+ results.push({ type: 'build-and-test', error: `PR not found: ${action.pr}` });
1250
+ break;
1251
+ }
1252
+ // Resolve project: explicit param wins, else PR's _project, else first configured project as last resort.
1253
+ const projectName = action.project || pr._project || null;
1254
+ const project = projectName
1255
+ ? PROJECTS.find(p => p.name?.toLowerCase() === String(projectName).toLowerCase())
1256
+ : null;
1257
+ if (!project) {
1258
+ results.push({ type: 'build-and-test', error: `Project not found for PR ${pr.id}: ${projectName || '(none)'}` });
1259
+ break;
1260
+ }
1261
+ // Pick agent: explicit param wins; else routing for 'test' work type.
1262
+ let agentId = action.agent && CONFIG.agents?.[action.agent] ? action.agent : null;
1263
+ if (!agentId) {
1264
+ agentId = routing.resolveAgent('test', CONFIG, { authorAgent: pr.agent });
1265
+ }
1266
+ if (!agentId) {
1267
+ results.push({ type: 'build-and-test', error: 'No available agent for test routing' });
1268
+ break;
1269
+ }
1270
+ const prNumber = shared.getPrNumber(pr);
1271
+ const dispatchKey = `cc-bt-${project.name}-${pr.id}`;
1272
+ const item = playbook.buildPrDispatch(agentId, CONFIG, project, pr, 'test', {
1273
+ pr_id: pr.id, pr_number: prNumber, pr_title: pr.title || '', pr_branch: pr.branch || '',
1274
+ pr_author: pr.agent || '', pr_url: pr.url || '',
1275
+ project_path: project.localPath || '',
1276
+ task: `Build & test ${pr.id}: ${pr.title || ''}`,
1277
+ }, `Build & test ${pr.id}: ${pr.title || ''}`,
1278
+ { dispatchKey, source: 'cc-build-and-test', pr, branch: pr.branch, project: { name: project.name, localPath: project.localPath } });
1279
+ if (!item) {
1280
+ results.push({ type: 'build-and-test', error: 'Failed to render build-and-test playbook' });
1281
+ break;
1282
+ }
1283
+ const id = dispatchMod.addToDispatch(item);
1284
+ results.push({ type: 'build-and-test', id, agent: agentId, pr: pr.id, ok: true });
1285
+ break;
1286
+ }
1241
1287
  case 'note': {
1242
1288
  shared.writeToInbox('command-center', shared.slugify(action.title || 'note'), `# ${action.title || 'Note'}\n\n${action.content || action.description || ''}`);
1243
1289
  results.push({ type: 'note', ok: true });
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "runtime": "copilot",
3
3
  "models": null,
4
- "cachedAt": "2026-04-30T08:44:52.884Z"
4
+ "cachedAt": "2026-04-30T10:08:32.077Z"
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
  }
@@ -278,6 +278,7 @@ const PLAYBOOK_REQUIRED_VARS = {
278
278
  'decompose': ['item_id', 'item_description', 'project_path'],
279
279
  'verify': ['task_description'],
280
280
  'test': ['item_name'],
281
+ 'docs': ['item_id', 'item_name'],
281
282
  'work-item': ['item_id', 'item_name'],
282
283
  'meeting-investigate': ['meeting_title', 'agenda'],
283
284
  'meeting-debate': ['meeting_title', 'agenda'],
@@ -630,7 +631,7 @@ function selectPlaybook(workType, item) {
630
631
  if (workType === WORK_TYPE.REVIEW && !item?._pr && !item?.pr_id) {
631
632
  return 'work-item';
632
633
  }
633
- const typeSpecificPlaybooks = ['explore', 'review', 'test', 'plan-to-prd', 'plan', 'ask', 'verify', 'decompose', 'meeting-investigate', 'meeting-debate', 'meeting-conclude'];
634
+ const typeSpecificPlaybooks = ['explore', 'review', 'test', 'plan-to-prd', 'plan', 'ask', 'verify', 'decompose', 'docs', 'meeting-investigate', 'meeting-debate', 'meeting-conclude'];
634
635
  return typeSpecificPlaybooks.includes(workType) ? workType : 'work-item';
635
636
  }
636
637
 
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.1632",
3
+ "version": "0.1.1634",
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"
@@ -0,0 +1,113 @@
1
+ # Docs Playbook
2
+
3
+ > Agent: {{agent_name}} ({{agent_role}}) | Task: {{item_name}} | ID: {{item_id}}
4
+
5
+ ## Context
6
+
7
+ Repo: {{repo_name}} | Org: {{ado_org}} | Project: {{ado_project}}
8
+ Team root: {{team_root}}
9
+ Project path: {{project_path}}
10
+
11
+ ## Mission
12
+
13
+ Update, expand, or rewrite project documentation. Targets include READMEs, CLAUDE.md,
14
+ files under `docs/`, JSDoc/TSDoc on exported APIs, and inline comments where they add
15
+ real WHY value (not WHAT — the code already says what). Keep voice consistent with the
16
+ project's existing docs.
17
+
18
+ ## Task
19
+
20
+ **{{item_name}}**
21
+
22
+ {{item_description}}
23
+
24
+ {{additional_context}}
25
+
26
+ {{references}}
27
+
28
+ {{acceptance_criteria}}
29
+
30
+ ## Steps
31
+
32
+ ### 1. Read the doc(s) and the code they describe
33
+
34
+ - Open the doc(s) being changed end-to-end before writing.
35
+ - Read the source they describe — function signatures, exported symbols, config keys,
36
+ CLI flags, file paths. Don't trust the existing doc; trust the code.
37
+ - For project-level docs (README, CLAUDE.md, /docs/*.md), skim adjacent docs so your
38
+ voice and structure match.
39
+
40
+ ### 2. Confirm doc reflects current code
41
+
42
+ For every claim in the doc you're touching, verify it against current code:
43
+
44
+ - Does the function still exist with that signature?
45
+ - Are the file paths correct?
46
+ - Are the listed flags / config keys still accepted?
47
+ - Are removed features still being documented?
48
+ - Are new features (visible in code) missing from the doc?
49
+
50
+ If the doc describes vapor, delete the section. If real features are missing, add them.
51
+
52
+ ### 3. Write or update concisely
53
+
54
+ - Match the project's existing voice — read 2-3 nearby docs to calibrate tone.
55
+ - Prefer concrete examples over abstract description.
56
+ - For code comments: follow the project's "Default to writing no comments" rule
57
+ (CLAUDE.md). Add comments only where they explain WHY a non-obvious choice was made,
58
+ never to restate WHAT the code does.
59
+ - For project-level docs: the bar is "would a new contributor understand this?"
60
+ - Keep tables, lists, and code blocks formatted consistently with surrounding docs.
61
+
62
+ ### 4. Verify
63
+
64
+ - Re-read the changed doc end-to-end after editing — does it still flow?
65
+ - If the project has doc-validation tests (lint, link-check, snippet-execution), run
66
+ them. Otherwise run `npm test` (or the project's documented test command) to make
67
+ sure nothing else broke.
68
+ - For docs with embedded code samples, mentally execute each sample against current
69
+ code — stale samples are worse than missing ones.
70
+
71
+ ## Acceptance
72
+
73
+ - Doc accurately reflects current code (no vapor, no missing features).
74
+ - Voice and structure match the rest of the project's docs.
75
+ - For inline code comments: follow project conventions; add comments only where they
76
+ explain WHY, never WHAT.
77
+ - For project-level docs: a new contributor could read it and understand the topic.
78
+ - Existing tests still pass; any doc-validation tests pass.
79
+
80
+ ## Git Workflow
81
+
82
+ You are already running in a git worktree on branch `{{branch_name}}`. Do NOT create
83
+ additional worktrees — the engine pre-created one for you. Do NOT remove the worktree —
84
+ the engine handles cleanup automatically.
85
+
86
+ Commit only the doc files (and any helper assets they reference). Do not bundle
87
+ unrelated code changes into a docs PR.
88
+
89
+ ```bash
90
+ git add <doc files>
91
+ git commit -m "{{commit_message}}"
92
+ git push -u origin {{branch_name}}
93
+ ```
94
+
95
+ PR creation is MANDATORY for docs tasks — docs go through the same review flow as code.
96
+ Use the appropriate repo-host tooling for PR creation. For Azure DevOps, prefer the
97
+ `az` CLI first and use the ADO MCP only as a fallback.
98
+
99
+ ## Rules
100
+
101
+ - Do NOT modify product code unless the task explicitly asks for it.
102
+ - Do NOT add comments that restate what the code does.
103
+ - Do NOT invent features that don't exist; verify against current code.
104
+ - Read `notes.md` for all team rules before starting.
105
+
106
+ ## When to Stop
107
+
108
+ Your task is complete once the doc accurately reflects current code, the PR is created
109
+ with the changed doc files, and any doc-validation tests pass. Do not continue editing
110
+ adjacent docs that weren't part of the task.
111
+
112
+ ## Team Decisions
113
+ {{notes_content}}
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.
@@ -77,6 +77,8 @@ Core action types:
77
77
  workTypes: `explore` (research/report only, NO PR), `ask` (answer/report, NO PR), `implement` (new code, PR REQUIRED), `fix` (bug fix, PR REQUIRED), `review` (code review, NO PR), `test` (tests, PR if new), `verify` (merge/build/maintenance, NO PR)
78
78
  If the user wants a design/architecture artifact committed through a PR, dispatch `implement` or `docs` rather than `explore`.
79
79
  When the user names a specific agent ("assign this to lambert"), put exactly that one name in `agents` (e.g. `"agents": ["lambert"]`). A single-agent assignment is hard-pinned by the server — it will queue for that agent only and skip the routing table. Use multi-agent arrays only when the user names multiple agents or asks for fan-out.
80
+ - **build-and-test**: pr, project (optional), agent (optional) — Run the build-and-test playbook against a PR. The agent will checkout the PR branch, run the project's build/test commands, and report results. Use when the user asks to "run tests on PR X" or "build PR X" or after a fix to verify nothing regressed.
81
+ Example: user says "run build and test on PR 1834" → `{"type":"build-and-test","pr":"1834"}`
80
82
  - **note**: title, content — save to inbox
81
83
  - **knowledge**: title, content, category (architecture/conventions/project-notes/build-reports/reviews) — create new KB entry or copy existing doc to KB
82
84
  - **pin-to-pinned**: title, content, level (critical/warning) — write to pinned.md, force-injected into ALL agent prompts (rarely needed)
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