@bonesofspring/ai-rules 0.2.12 → 0.2.13

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.
Files changed (163) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/package.json +1 -1
  3. package/presets/_shared/core/agent-team/agent-artifact-contracts.md +114 -0
  4. package/presets/_shared/core/agent-team/agent-team-orchestrator.md +64 -5
  5. package/presets/claude/go/agents/build-verifier.md +9 -2
  6. package/presets/claude/go/agents/code-reviewer.md +8 -1
  7. package/presets/claude/go/agents/feature-developer.md +8 -1
  8. package/presets/claude/go/agents/task-analyst.md +8 -1
  9. package/presets/claude/go/agents/task-router.md +33 -5
  10. package/presets/claude/go/commands/task.md +8 -6
  11. package/presets/claude/go/hooks/chain-team-phases.sh +195 -53
  12. package/presets/claude/go/rules/tooling-and-review/agent-team-orchestrator.md +30 -5
  13. package/presets/claude/go/team/README.md +13 -2
  14. package/presets/claude/ios-swift/agents/build-verifier.md +8 -1
  15. package/presets/claude/ios-swift/agents/code-reviewer.md +9 -14
  16. package/presets/claude/ios-swift/agents/feature-developer.md +8 -1
  17. package/presets/claude/ios-swift/agents/task-analyst.md +8 -1
  18. package/presets/claude/ios-swift/agents/task-router.md +31 -3
  19. package/presets/claude/ios-swift/commands/task.md +8 -6
  20. package/presets/claude/ios-swift/hooks/chain-team-phases.sh +177 -54
  21. package/presets/claude/ios-swift/rules/tooling-and-review/agent-team-orchestrator.md +33 -2
  22. package/presets/claude/ios-swift/team/README.md +13 -2
  23. package/presets/claude/java/agents/build-verifier.md +8 -1
  24. package/presets/claude/java/agents/code-reviewer.md +8 -1
  25. package/presets/claude/java/agents/feature-developer.md +8 -1
  26. package/presets/claude/java/agents/task-analyst.md +8 -1
  27. package/presets/claude/java/agents/task-router.md +31 -3
  28. package/presets/claude/java/commands/task.md +8 -6
  29. package/presets/claude/java/hooks/chain-team-phases.sh +195 -53
  30. package/presets/claude/java/rules/tooling-and-review/agent-team-orchestrator.md +30 -5
  31. package/presets/claude/java/team/README.md +13 -2
  32. package/presets/claude/mcp-ts/agents/build-verifier.md +9 -2
  33. package/presets/claude/mcp-ts/agents/feature-developer.md +7 -1
  34. package/presets/claude/mcp-ts/agents/task-analyst.md +8 -1
  35. package/presets/claude/mcp-ts/agents/task-router.md +33 -5
  36. package/presets/claude/mcp-ts/commands/task.md +8 -6
  37. package/presets/claude/mcp-ts/hooks/chain-team-phases.sh +181 -39
  38. package/presets/claude/mcp-ts/rules/tooling-and-review/agent-team-orchestrator.md +30 -5
  39. package/presets/claude/mcp-ts/team/README.md +13 -2
  40. package/presets/claude/next/agents/accessibility-reviewer.md +6 -12
  41. package/presets/claude/next/agents/api-contract-reviewer.md +6 -12
  42. package/presets/claude/next/agents/build-verifier.md +8 -12
  43. package/presets/claude/next/agents/ci-investigator.md +7 -13
  44. package/presets/claude/next/agents/code-reviewer.md +9 -14
  45. package/presets/claude/next/agents/debugger.md +6 -12
  46. package/presets/claude/next/agents/feature-developer.md +8 -1
  47. package/presets/claude/next/agents/migration-specialist.md +6 -12
  48. package/presets/claude/next/agents/performance-auditor.md +5 -11
  49. package/presets/claude/next/agents/qa-tester.md +6 -12
  50. package/presets/claude/next/agents/security-reviewer.md +6 -12
  51. package/presets/claude/next/agents/solution-architect.md +6 -12
  52. package/presets/claude/next/agents/task-analyst.md +10 -16
  53. package/presets/claude/next/agents/task-router.md +31 -3
  54. package/presets/claude/next/agents/tech-writer.md +6 -13
  55. package/presets/claude/next/agents/unit-test-generator.md +6 -10
  56. package/presets/claude/next/agents/unit-test-healer.md +6 -10
  57. package/presets/claude/next/agents/unit-test-planner.md +5 -11
  58. package/presets/claude/next/commands/task.md +8 -6
  59. package/presets/claude/next/hooks/chain-team-phases.sh +197 -54
  60. package/presets/claude/next/rules/tooling-and-review/agent-team-orchestrator.md +29 -4
  61. package/presets/claude/next/team/README.md +15 -3
  62. package/presets/claude/next/team/fixtures/artifact-manifest.example.json +61 -0
  63. package/presets/claude/next/team/fixtures/bugfix-standard.json +10 -0
  64. package/presets/claude/next/team/fixtures/feature-full.json +10 -0
  65. package/presets/claude/next/team/fixtures/feature-light.json +10 -0
  66. package/presets/claude/php-hexagonal/agents/build-verifier.md +9 -2
  67. package/presets/claude/php-hexagonal/agents/code-reviewer.md +8 -1
  68. package/presets/claude/php-hexagonal/agents/feature-developer.md +8 -1
  69. package/presets/claude/php-hexagonal/agents/task-analyst.md +8 -1
  70. package/presets/claude/php-hexagonal/agents/task-router.md +31 -3
  71. package/presets/claude/php-hexagonal/commands/task.md +8 -6
  72. package/presets/claude/php-hexagonal/hooks/README.md +1 -1
  73. package/presets/claude/php-hexagonal/hooks/chain-team-phases.sh +195 -53
  74. package/presets/claude/php-hexagonal/rules/tooling-and-review/agent-team-orchestrator.md +30 -5
  75. package/presets/claude/php-hexagonal/team/README.md +13 -2
  76. package/presets/claude/php-laravel/agents/build-verifier.md +9 -2
  77. package/presets/claude/php-laravel/agents/code-reviewer.md +8 -1
  78. package/presets/claude/php-laravel/agents/feature-developer.md +8 -1
  79. package/presets/claude/php-laravel/agents/task-analyst.md +8 -1
  80. package/presets/claude/php-laravel/agents/task-router.md +31 -3
  81. package/presets/claude/php-laravel/commands/task.md +8 -6
  82. package/presets/claude/php-laravel/hooks/chain-team-phases.sh +195 -53
  83. package/presets/claude/php-laravel/rules/tooling-and-review/agent-team-orchestrator.md +30 -5
  84. package/presets/claude/php-laravel/team/README.md +13 -2
  85. package/presets/cursor/go/agents/build-verifier.md +8 -1
  86. package/presets/cursor/go/agents/code-reviewer.md +8 -1
  87. package/presets/cursor/go/agents/feature-developer.md +8 -1
  88. package/presets/cursor/go/agents/task-analyst.md +8 -1
  89. package/presets/cursor/go/agents/task-router.md +31 -3
  90. package/presets/cursor/go/commands/task.md +8 -6
  91. package/presets/cursor/go/hooks/chain-team-phases.sh +195 -53
  92. package/presets/cursor/go/rules/agent-team-orchestrator.mdc +30 -5
  93. package/presets/cursor/go/team/README.md +13 -2
  94. package/presets/cursor/ios-swift/agents/build-verifier.md +8 -1
  95. package/presets/cursor/ios-swift/agents/code-reviewer.md +9 -14
  96. package/presets/cursor/ios-swift/agents/feature-developer.md +8 -1
  97. package/presets/cursor/ios-swift/agents/task-analyst.md +8 -1
  98. package/presets/cursor/ios-swift/agents/task-router.md +31 -3
  99. package/presets/cursor/ios-swift/commands/task.md +8 -6
  100. package/presets/cursor/ios-swift/hooks/chain-team-phases.sh +195 -54
  101. package/presets/cursor/ios-swift/rules/agent-team-orchestrator.mdc +23 -2
  102. package/presets/cursor/ios-swift/team/README.md +13 -2
  103. package/presets/cursor/java/agents/build-verifier.md +8 -1
  104. package/presets/cursor/java/agents/code-reviewer.md +8 -1
  105. package/presets/cursor/java/agents/feature-developer.md +8 -1
  106. package/presets/cursor/java/agents/task-analyst.md +8 -1
  107. package/presets/cursor/java/agents/task-router.md +31 -3
  108. package/presets/cursor/java/commands/task.md +8 -6
  109. package/presets/cursor/java/hooks/chain-team-phases.sh +195 -53
  110. package/presets/cursor/java/rules/agent-team-orchestrator.mdc +30 -5
  111. package/presets/cursor/java/team/README.md +13 -2
  112. package/presets/cursor/mcp-ts/agents/build-verifier.md +8 -1
  113. package/presets/cursor/mcp-ts/agents/feature-developer.md +7 -1
  114. package/presets/cursor/mcp-ts/agents/task-analyst.md +8 -1
  115. package/presets/cursor/mcp-ts/agents/task-router.md +31 -3
  116. package/presets/cursor/mcp-ts/commands/task.md +8 -6
  117. package/presets/cursor/mcp-ts/hooks/chain-team-phases.sh +181 -39
  118. package/presets/cursor/mcp-ts/rules/agent-team-orchestrator.mdc +30 -5
  119. package/presets/cursor/mcp-ts/team/README.md +13 -2
  120. package/presets/cursor/next/agents/accessibility-reviewer.md +7 -14
  121. package/presets/cursor/next/agents/api-contract-reviewer.md +7 -14
  122. package/presets/cursor/next/agents/build-verifier.md +8 -13
  123. package/presets/cursor/next/agents/ci-investigator.md +7 -14
  124. package/presets/cursor/next/agents/code-reviewer.md +9 -14
  125. package/presets/cursor/next/agents/debugger.md +6 -12
  126. package/presets/cursor/next/agents/feature-developer.md +8 -1
  127. package/presets/cursor/next/agents/migration-specialist.md +7 -14
  128. package/presets/cursor/next/agents/performance-auditor.md +6 -13
  129. package/presets/cursor/next/agents/qa-tester.md +6 -13
  130. package/presets/cursor/next/agents/security-reviewer.md +7 -14
  131. package/presets/cursor/next/agents/solution-architect.md +6 -12
  132. package/presets/cursor/next/agents/task-analyst.md +10 -16
  133. package/presets/cursor/next/agents/task-router.md +31 -3
  134. package/presets/cursor/next/agents/tech-writer.md +6 -13
  135. package/presets/cursor/next/agents/unit-test-generator.md +6 -11
  136. package/presets/cursor/next/agents/unit-test-healer.md +6 -11
  137. package/presets/cursor/next/agents/unit-test-planner.md +6 -13
  138. package/presets/cursor/next/commands/task.md +8 -6
  139. package/presets/cursor/next/hooks/chain-team-phases.sh +197 -54
  140. package/presets/cursor/next/rules/agent-team-orchestrator.mdc +30 -5
  141. package/presets/cursor/next/team/README.md +16 -4
  142. package/presets/cursor/next/team/fixtures/artifact-manifest.example.json +61 -0
  143. package/presets/cursor/next/team/fixtures/bugfix-standard.json +10 -0
  144. package/presets/cursor/next/team/fixtures/feature-full.json +10 -0
  145. package/presets/cursor/next/team/fixtures/feature-light.json +10 -0
  146. package/presets/cursor/php-hexagonal/agents/build-verifier.md +8 -1
  147. package/presets/cursor/php-hexagonal/agents/code-reviewer.md +8 -1
  148. package/presets/cursor/php-hexagonal/agents/feature-developer.md +8 -1
  149. package/presets/cursor/php-hexagonal/agents/task-analyst.md +8 -1
  150. package/presets/cursor/php-hexagonal/agents/task-router.md +31 -3
  151. package/presets/cursor/php-hexagonal/commands/task.md +8 -6
  152. package/presets/cursor/php-hexagonal/hooks/chain-team-phases.sh +195 -53
  153. package/presets/cursor/php-hexagonal/rules/agent-team-orchestrator.mdc +30 -5
  154. package/presets/cursor/php-hexagonal/team/README.md +13 -2
  155. package/presets/cursor/php-laravel/agents/build-verifier.md +8 -1
  156. package/presets/cursor/php-laravel/agents/code-reviewer.md +8 -1
  157. package/presets/cursor/php-laravel/agents/feature-developer.md +8 -1
  158. package/presets/cursor/php-laravel/agents/task-analyst.md +8 -1
  159. package/presets/cursor/php-laravel/agents/task-router.md +31 -3
  160. package/presets/cursor/php-laravel/commands/task.md +8 -6
  161. package/presets/cursor/php-laravel/hooks/chain-team-phases.sh +195 -53
  162. package/presets/cursor/php-laravel/rules/agent-team-orchestrator.mdc +30 -5
  163. package/presets/cursor/php-laravel/team/README.md +13 -2
@@ -123,6 +123,27 @@ function roleFor(agent) {
123
123
  if (/writer/.test(agent || '')) return 'docs';
124
124
  return 'implementation';
125
125
  }
126
+ const DEFAULT_ARTIFACT_OUTPUT_IDS = {
127
+ 'task-router': 'routing',
128
+ 'task-analyst': 'analysis',
129
+ 'solution-architect': 'architecture',
130
+ 'migration-specialist': 'migration-plan',
131
+ debugger: 'debug',
132
+ 'ci-investigator': 'ci',
133
+ 'feature-developer': 'implementation',
134
+ 'build-verifier': 'validation',
135
+ 'code-reviewer': 'review',
136
+ 'security-reviewer': 'security-review',
137
+ 'accessibility-reviewer': 'a11y-review',
138
+ 'performance-auditor': 'perf-report',
139
+ 'api-contract-reviewer': 'api-contract-review',
140
+ 'tech-writer': 'documentation',
141
+ };
142
+ function artifactOutputIdFor(step, agent) {
143
+ if (step && typeof step.artifactOutputId === 'string') return step.artifactOutputId;
144
+ if (step?.artifactOutputIds && typeof step.artifactOutputIds[agent] === 'string') return step.artifactOutputIds[agent];
145
+ return DEFAULT_ARTIFACT_OUTPUT_IDS[agent] || agent;
146
+ }
126
147
  function activateAttempts(step, stepIndex) {
127
148
  const active = { ...(status.activeAttempts || {}) }; const agents = getStepAgents(step);
128
149
  const parallelGroupId = step.parallel && agents.length > 1 ? `${status.metricsRunId || slug}:step-${stepIndex}` : null;
@@ -145,11 +166,61 @@ function resolvedAttempt() {
145
166
  try { const receipts = JSON.parse(fs.readFileSync(path.join(taskDir, '.metrics-receipts.json'), 'utf8')); const receipt = Array.isArray(receipts) ? receipts.find((item) => active[item.attemptId]) : null; if (receipt) return { attemptId: receipt.attemptId, entry: active[receipt.attemptId], confidence: 'fallback', source: 'agent' }; } catch {}
146
167
  return null;
147
168
  }
169
+ function readBoundReceiptOutcome(agent, step, resolved) {
170
+ if (!agent || !resolved) return { outcome: null, receiptRequired: false };
171
+ const manifestPath = path.join(taskDir, 'artifact-manifest.json');
172
+ if (!fs.existsSync(manifestPath)) return { outcome: null, receiptRequired: false };
173
+ try {
174
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
175
+ const artifacts = Array.isArray(manifest.artifacts) ? manifest.artifacts : [];
176
+ const expectedOutputId = artifactOutputIdFor(step, agent);
177
+ const attemptStartedAt = Date.parse(resolved.entry.startedAt);
178
+ const owned = artifacts
179
+ .filter((entry) => {
180
+ if (!entry || entry.producer !== agent || !entry.receipt || typeof entry.receipt.outcome !== 'string') return false;
181
+ const boundByAttempt = typeof entry.receipt.attemptId === 'string' && entry.receipt.attemptId === resolved.attemptId;
182
+ const boundByOutput = entry.receipt.attemptId == null && entry.id === expectedOutputId;
183
+ if (!boundByAttempt && !boundByOutput) return false;
184
+ const terminalAt = Date.parse(entry.timestamps?.completedAt || entry.timestamps?.updatedAt || '');
185
+ return Number.isFinite(terminalAt) && (!Number.isFinite(attemptStartedAt) || terminalAt >= attemptStartedAt);
186
+ })
187
+ .sort((a, b) => {
188
+ const ta = Date.parse(a.timestamps?.completedAt || a.timestamps?.updatedAt || 0) || 0;
189
+ const tb = Date.parse(b.timestamps?.completedAt || b.timestamps?.updatedAt || 0) || 0;
190
+ return tb - ta;
191
+ });
192
+ return { outcome: owned[0]?.receipt?.outcome || null, receiptRequired: true };
193
+ } catch {
194
+ return { outcome: null, receiptRequired: false };
195
+ }
196
+ }
197
+ function reconcileStatusFromManifest(agent, step, resolved) {
198
+ const terminal = new Set(['completed', 'blocked', 'changes_requested', 'validation_failed']);
199
+ const { outcome, receiptRequired } = readBoundReceiptOutcome(agent, step, resolved);
200
+ if (terminal.has(outcome)) {
201
+ status.state = outcome;
202
+ return outcome;
203
+ }
204
+ if (receiptRequired) {
205
+ status.state = 'awaiting_artifact_receipt';
206
+ status.phase = 'blocked';
207
+ return status.state;
208
+ }
209
+ if (terminal.has(status.state)) return status.state;
210
+ // Host reported subagent completed and no blocking receipt → treat as completed for metrics/advance.
211
+ if (hookInput.status === 'completed') {
212
+ status.state = 'completed';
213
+ return 'completed';
214
+ }
215
+ return status.state;
216
+ }
217
+
148
218
  function finishActiveAttempt() {
149
219
  const resolved = resolvedAttempt(); if (!resolved) return null;
150
220
  const durationMs = Date.parse(now) - Date.parse(resolved.entry.startedAt);
151
- appendMetric({ type: 'attempt_finished', at: now, attemptId: resolved.attemptId, outcome: ['completed', 'changes_requested', 'validation_failed'].includes(status.state) ? status.state : 'unknown', durationMs: Number.isFinite(durationMs) && durationMs >= 0 ? durationMs : null, retryAfterFix: status.retryAfterFix || null, source: resolved.source || 'hook', attributionConfidence: resolved.confidence });
152
- const active = { ...(status.activeAttempts || {}) }; delete active[resolved.attemptId]; status.activeAttempts = active; status.lastCompletedAttempt = { attemptId: resolved.attemptId, completedAt: now }; return resolved;
221
+ const outcome = ['completed', 'blocked', 'changes_requested', 'validation_failed'].includes(status.state) ? status.state : 'unknown';
222
+ appendMetric({ type: 'attempt_finished', at: now, attemptId: resolved.attemptId, outcome, durationMs: Number.isFinite(durationMs) && durationMs >= 0 ? durationMs : null, retryAfterFix: status.retryAfterFix || null, source: resolved.source || 'hook', attributionConfidence: resolved.confidence });
223
+ const active = { ...(status.activeAttempts || {}) }; delete active[resolved.attemptId]; status.activeAttempts = active; status.lastCompletedAttempt = { attemptId: resolved.attemptId, completedAt: now }; return { ...resolved, outcome };
153
224
  }
154
225
  function beginHumanGate() { const eventId = `${status.metricsRunId || slug}:gate:${Date.now()}`; appendMetric({ eventId, type: 'human_gate_started', at: now, source: 'hook', attributionConfidence: 'high' }); status.activeHumanGateEventId = eventId; }
155
226
  function endHumanGateIfResumed() {
@@ -218,54 +289,54 @@ function findNextExecutableStep(steps, startIdx, dir) {
218
289
  const AGENT_HANDOFF = {
219
290
  'task-analyst': (s) =>
220
291
  `Invoke task-analyst for slug "${s}". Write brief.md and decomposition.md under .cursor/team/tasks/${s}/. ` +
221
- `Set status.json: phase executing, state awaiting_approval if human gate applies. Do not use legacy phase analysis.`,
292
+ `Upsert the analysis receipt in artifact-manifest.json; do not mutate status.json lifecycle fields. Do not use legacy phase analysis.`,
222
293
  debugger: (s) =>
223
294
  `Invoke debugger subagent for slug "${s}". Investigate root cause with go test/logs; write debug-report.md with fixApplied: true/false. ` +
224
- `Update status.json when done.`,
295
+ `Upsert the owned terminal receipt in artifact-manifest.json; do not mutate status.json lifecycle fields.`,
225
296
  'solution-architect': (s) =>
226
- `Invoke solution-architect for slug "${s}". Read brief.md; write architecture.md for Go hexagonal layers. Update status.json when done.`,
297
+ `Invoke solution-architect for slug "${s}". Read brief.md; write architecture.md for Go hexagonal layers. Upsert the owned terminal receipt in artifact-manifest.json; do not mutate status.json lifecycle fields.`,
227
298
  'migration-specialist': (s) =>
228
- `Invoke migration-specialist for slug "${s}". Read brief.md; write migration-plan.md for Go module/toolchain. Update status.json when done.`,
299
+ `Invoke migration-specialist for slug "${s}". Read brief.md; write migration-plan.md for Go module/toolchain. Upsert the owned terminal receipt in artifact-manifest.json; do not mutate status.json lifecycle fields.`,
229
300
  'api-contract-reviewer': (s) =>
230
- `Invoke api-contract-reviewer (readonly) for slug "${s}". Review OpenAPI/Protobuf vs adapters; write api-contract-review.md. Update status.json when done.`,
301
+ `Invoke api-contract-reviewer (readonly) for slug "${s}". Review OpenAPI/Protobuf vs adapters; write api-contract-review.md. Upsert the owned terminal receipt in artifact-manifest.json; do not mutate status.json lifecycle fields.`,
231
302
  'ci-investigator': (s) =>
232
303
  `Invoke ci-investigator for slug "${s}". Diagnose CI failure (go test/vet/lint or preset-structure); write ci-report.md; apply minimal fix. ` +
233
- `Use ci-investigation skill. Update status.json when done.`,
304
+ `Use ci-investigation skill. Upsert the owned terminal receipt in artifact-manifest.json; do not mutate status.json lifecycle fields.`,
234
305
  'feature-developer': (s) =>
235
306
  `Invoke feature-developer for slug "${s}". Read brief.md, decomposition.md, validation-report.md, review.md, architecture.md, migration-plan.md, api-contract-review.md, debug-report.md if present. ` +
236
307
  `Follow feature-delivery-workflow.mdc and reference-features.mdc. Prefer internal/domain|application|adapters and cmd/ composition root. ` +
237
- `Include table-driven go test coverage for domain/use cases before handoff. Update status.json when done.`,
308
+ `Include table-driven go test coverage for domain/use cases before handoff. Upsert the owned terminal receipt in artifact-manifest.json; do not mutate status.json lifecycle fields.`,
238
309
  'build-verifier': (s) =>
239
310
  `Invoke build-verifier (readonly) for slug "${s}". Follow go-tooling.mdc and post-change-test.mdc. ` +
240
311
  `Run go test ./..., go vet ./..., and golangci-lint/staticcheck if configured — never yarn/ESLint/Stylelint. ` +
241
- `For preset packaging: yarn check:preset-structure. Write validation-report.md. Set state completed or validation_failed. Do not edit production code.`,
312
+ `For preset packaging: yarn check:preset-structure. Write validation-report.md and emit receipt outcome completed or validation_failed. Do not edit production code.`,
242
313
  'performance-auditor': (s) =>
243
- `Invoke performance-auditor (readonly) for slug "${s}". Audit Go hot paths/allocs/DB N+1/pprof risks; write perf-report.md. Update status.json when done.`,
314
+ `Invoke performance-auditor (readonly) for slug "${s}". Audit Go hot paths/allocs/DB N+1/pprof risks; write perf-report.md. Upsert the owned terminal receipt in artifact-manifest.json; do not mutate status.json lifecycle fields.`,
244
315
  'code-reviewer': (s) =>
245
316
  `Invoke code-reviewer subagent (readonly) for slug "${s}". Review git diff vs brief.md per code-review-mr.mdc (hexagonal boundaries). ` +
246
- `Update status.json when done.`,
317
+ `Upsert the owned terminal receipt in artifact-manifest.json; do not mutate status.json lifecycle fields.`,
247
318
  'security-reviewer': (s) =>
248
- `Invoke security-reviewer (readonly) for slug "${s}". Follow security-go.mdc; write security-review.md. Update status.json when done.`,
319
+ `Invoke security-reviewer (readonly) for slug "${s}". Follow security-go.mdc; write security-review.md. Upsert the owned terminal receipt in artifact-manifest.json; do not mutate status.json lifecycle fields.`,
249
320
  'qa-tester': (s, scope) =>
250
321
  `Invoke qa-tester for slug "${s}"${scope ? ` (scope: ${scope})` : ''}. ` +
251
- `Add/update go unit and integration tests per brief. Follow tests-unit.mdc, tests-integration.mdc, and skill integration-testing. Update status.json when done.`,
322
+ `Add/update go unit and integration tests per brief. Follow tests-unit.mdc, tests-integration.mdc, and skill integration-testing. Upsert the owned terminal receipt in artifact-manifest.json; do not mutate status.json lifecycle fields.`,
252
323
  'unit-test-planner': (s) =>
253
- `Invoke unit-test-planner for slug "${s}". Write unit-test-plan.md for table-driven go test. Use unit-testing skill. Update status.json when done.`,
324
+ `Invoke unit-test-planner for slug "${s}". Write unit-test-plan.md for table-driven go test. Use unit-testing skill. Upsert the owned terminal receipt in artifact-manifest.json; do not mutate status.json lifecycle fields.`,
254
325
  'unit-test-generator': (s) =>
255
- `Invoke unit-test-generator for slug "${s}". Implement *_test.go from unit-test-plan.md. Use unit-testing skill. Update status.json when done.`,
326
+ `Invoke unit-test-generator for slug "${s}". Implement *_test.go from unit-test-plan.md. Use unit-testing skill. Upsert the owned terminal receipt in artifact-manifest.json; do not mutate status.json lifecycle fields.`,
256
327
  'unit-test-healer': (s) =>
257
- `Invoke unit-test-healer for slug "${s}". Fix failing unit tests. Use unit-testing and debug-investigation skills. Update status.json when done.`,
328
+ `Invoke unit-test-healer for slug "${s}". Fix failing unit tests. Use unit-testing and debug-investigation skills. Upsert the owned terminal receipt in artifact-manifest.json; do not mutate status.json lifecycle fields.`,
258
329
  'integration-test-planner': (s) =>
259
330
  `Invoke integration-test-planner for slug "${s}". Create/update integration test plans (httptest/testcontainers/adapter contracts). ` +
260
- `Follow tests-integration.mdc and the integration-testing skill. Do not use browser e2e tooling. Update status.json when done.`,
331
+ `Follow tests-integration.mdc and the integration-testing skill. Do not use browser e2e tooling. Upsert the owned terminal receipt in artifact-manifest.json; do not mutate status.json lifecycle fields.`,
261
332
  'integration-test-generator': (s) =>
262
333
  `Invoke integration-test-generator for slug "${s}". Generate/update *_integration_test.go from plans. ` +
263
- `Follow tests-integration.mdc and the integration-testing skill. Do not use browser e2e tooling. Update status.json when done.`,
334
+ `Follow tests-integration.mdc and the integration-testing skill. Do not use browser e2e tooling. Upsert the owned terminal receipt in artifact-manifest.json; do not mutate status.json lifecycle fields.`,
264
335
  'integration-test-healer': (s) =>
265
336
  `Invoke integration-test-healer for slug "${s}". Fix failing/flaky integration tests while preserving plan assertions. ` +
266
- `Follow tests-integration.mdc, integration-testing, and debug-investigation. Do not use browser e2e tooling. Update status.json when done.`,
337
+ `Follow tests-integration.mdc, integration-testing, and debug-investigation. Do not use browser e2e tooling. Upsert the owned terminal receipt in artifact-manifest.json; do not mutate status.json lifecycle fields.`,
267
338
  'tech-writer': (s) =>
268
- `Invoke tech-writer for slug "${s}". Write documentation.md from task artifacts. Update status.json when done.`,
339
+ `Invoke tech-writer for slug "${s}". Write documentation.md from task artifacts. Upsert the owned terminal receipt in artifact-manifest.json; do not mutate status.json lifecycle fields.`,
269
340
  };
270
341
 
271
342
  const REVIEW_AGENTS = new Set([
@@ -278,15 +349,20 @@ const REVIEW_AGENTS = new Set([
278
349
  function metricsHandoff(agent) {
279
350
  const matches = Object.entries(status.activeAttempts || {}).filter(([, entry]) => entry.agent === agent);
280
351
  if (matches.length === 1) {
281
- return ` Metrics attemptId: ${matches[0][0]}; update normal status only. If the hook cannot identify a parallel stop, append a minimal { attemptId, outcome } receipt to .metrics-receipts.json; do not calculate duration.`;
352
+ return ` Metrics attemptId: ${matches[0][0]}; write owned artifact-manifest receipt outcome. If the hook cannot identify a parallel stop, append a minimal { attemptId, outcome } receipt to .metrics-receipts.json; do not mutate status.json or calculate duration.`;
282
353
  }
283
354
  return ' Metrics: use the supplied attemptId when available; for a parallel completion, write a minimal { attemptId, outcome } receipt to .metrics-receipts.json rather than relying on currentAgent. Do not calculate duration.';
284
355
  }
285
356
 
286
- function handoffForAgent(agent, scope) {
357
+ function artifactHandoff(step, agent) {
358
+ const inputIds = Array.isArray(step?.artifactInputIds) ? step.artifactInputIds.join(', ') : 'manifest defaults';
359
+ return ` Artifact inputs: ${inputIds}; owned output ID: ${artifactOutputIdFor(step, agent)}. Bind the terminal receipt to the supplied attemptId.`;
360
+ }
361
+
362
+ function handoffForAgent(agent, scope, step = null) {
287
363
  const fn = AGENT_HANDOFF[agent];
288
- const handoff = fn ? fn(slug, scope) : `Invoke ${agent} subagent for slug "${slug}". Update status.json when done.`;
289
- return handoff + metricsHandoff(agent);
364
+ const handoff = fn ? fn(slug, scope) : `Invoke ${agent} subagent for slug "${slug}". Upsert the owned terminal receipt in artifact-manifest.json; do not mutate status.json lifecycle fields.`;
365
+ return handoff + artifactHandoff(step, agent) + metricsHandoff(agent);
290
366
  }
291
367
 
292
368
  function handoffForParallelStep(step) {
@@ -296,7 +372,8 @@ function handoffForParallelStep(step) {
296
372
  .map(([attemptId, entry]) => `${entry.agent}=${attemptId}`);
297
373
  return (
298
374
  `Invoke IN PARALLEL (one message, multiple Task calls): ${agents.join(' AND ')} for slug "${slug}". ` +
299
- `Each agent writes its artifact and appends itself to status.json parallelCompleted when done. ` +
375
+ `Each agent writes its artifact and owned receipt; the hook records parallelCompleted from stops. Do not mutate status.json lifecycle fields. ` +
376
+ `Artifact inputs: ${Array.isArray(step.artifactInputIds) ? step.artifactInputIds.join(', ') : 'manifest defaults'}; owned output IDs: ${agents.map((agent) => `${agent}=${artifactOutputIdFor(step, agent)}`).join('; ')}. ` +
300
377
  `Metrics attemptIds: ${attemptIds.join('; ')}. If the hook cannot identify a parallel stop, append a minimal { attemptId, outcome } receipt to .metrics-receipts.json; do not calculate duration.`
301
378
  );
302
379
  }
@@ -335,8 +412,12 @@ function legacyFlow() {
335
412
  appendMetric({ type: 'retry_scheduled', at: now, attemptId: predecessorAttemptId, nextAgent: agent, retryAfterFix: retryAfterFix || null, source: 'hook', attributionConfidence: 'unknown' });
336
413
  activateAttempts({ agent }, stepIndex);
337
414
  }
338
- if (status.state === 'changes_requested' && REVIEW_AGENTS.has(status.currentAgent)) {
415
+ if (status.state === 'changes_requested') {
339
416
  const reviewer = status.currentAgent;
417
+ if (!reviewer) {
418
+ process.stdout.write('{}');
419
+ process.exit(0);
420
+ }
340
421
  scheduleLegacyRetry('feature-developer', reviewer);
341
422
  writeStatus({
342
423
  currentAgent: 'feature-developer',
@@ -410,7 +491,7 @@ if (hookInput.action === 'end_human_gate') {
410
491
  parallelCompleted: [],
411
492
  });
412
493
  if (resumeNext.step.parallel && resumeAgents.length > 1) out(handoffForParallelStep(resumeNext.step));
413
- else out(handoffForAgent(resumeAgents[0], resumeNext.step.scope));
494
+ else out(handoffForAgent(resumeAgents[0], resumeNext.step.scope, resumeNext.step));
414
495
  process.exit(0);
415
496
  }
416
497
  }
@@ -439,6 +520,19 @@ const steps = pipeline.steps || [];
439
520
  const initialIdx = typeof status.pipelineIndex === 'number' ? status.pipelineIndex : 0;
440
521
  const initialStep = steps[initialIdx];
441
522
  const activeAttemptCountBeforeFinish = Object.keys(status.activeAttempts || {}).length;
523
+ const completingAgentHint = (() => {
524
+ const resolved = resolvedAttempt();
525
+ if (resolved?.entry?.agent) return resolved.entry.agent;
526
+ if (status.currentAgent) return status.currentAgent;
527
+ return getStepAgents(initialStep)[0];
528
+ })();
529
+ const reconcilingAttempt = resolvedAttempt();
530
+ reconcileStatusFromManifest(completingAgentHint, initialStep, reconcilingAttempt);
531
+ if (status.state === 'awaiting_artifact_receipt') {
532
+ writeStatus({});
533
+ process.stdout.write('{}');
534
+ process.exit(0);
535
+ }
442
536
  const finishedAttempt = finishActiveAttempt();
443
537
  if (!finishedAttempt && (hookInput.attemptId || activeAttemptCountBeforeFinish > 0)) {
444
538
  process.stdout.write('{}');
@@ -446,6 +540,7 @@ if (!finishedAttempt && (hookInput.attemptId || activeAttemptCountBeforeFinish >
446
540
  }
447
541
  endHumanGateIfResumed();
448
542
  if (steps.length === 0 || pipeline.autoChain === false) {
543
+ writeStatus({});
449
544
  process.stdout.write('{}');
450
545
  process.exit(0);
451
546
  }
@@ -471,8 +566,74 @@ function scheduleRetry(agent, retryAfterFix, predecessor, explicitStepIndex = nu
471
566
  return stepIndex;
472
567
  }
473
568
 
474
- if (status.state === 'changes_requested' && REVIEW_AGENTS.has(status.currentAgent)) {
475
- const reviewer = status.currentAgent;
569
+ const idx = typeof status.pipelineIndex === 'number' ? status.pipelineIndex : 0;
570
+ const currentStep = steps[idx];
571
+ const completedAgent = finishedAttempt?.entry?.agent || status.currentAgent || getStepAgents(currentStep)[0];
572
+ const stepAgents = getStepAgents(currentStep);
573
+ if (currentStep?.parallel && stepAgents.length > 1) {
574
+ const done = [...(status.parallelCompleted || [])];
575
+ const outcomes = { ...(status.parallelOutcomes || {}) };
576
+ if (finishedAttempt && completedAgent) {
577
+ if (!done.includes(completedAgent)) done.push(completedAgent);
578
+ outcomes[completedAgent] = {
579
+ outcome: finishedAttempt.outcome || status.state,
580
+ attemptId: finishedAttempt.attemptId,
581
+ confidence: finishedAttempt.confidence,
582
+ };
583
+ }
584
+ writeStatus({ parallelCompleted: done, parallelOutcomes: outcomes });
585
+ const remaining = stepAgents.filter((agent) => !done.includes(agent));
586
+ if (remaining.length > 0) {
587
+ process.stdout.write('{}');
588
+ process.exit(0);
589
+ }
590
+
591
+ const blockedProducer = stepAgents.find((agent) => outcomes[agent]?.outcome === 'blocked');
592
+ if (blockedProducer) {
593
+ writeStatus({ parallelCompleted: [], parallelOutcomes: {}, state: 'blocked', phase: 'blocked', currentAgent: blockedProducer });
594
+ process.stdout.write('{}');
595
+ process.exit(0);
596
+ }
597
+
598
+ const retryReviewer = stepAgents.find((agent) => outcomes[agent]?.outcome === 'changes_requested');
599
+ if (retryReviewer) {
600
+ const retryAfterFixStepIndex = idx;
601
+ const predecessor = outcomes[retryReviewer]
602
+ ? {
603
+ attemptId: outcomes[retryReviewer].attemptId,
604
+ confidence: outcomes[retryReviewer].confidence,
605
+ }
606
+ : finishedAttempt;
607
+ const developerIndex = scheduleRetry('feature-developer', retryReviewer, predecessor);
608
+ writeStatus({
609
+ pipelineIndex: developerIndex,
610
+ currentAgent: 'feature-developer',
611
+ state: 'in_progress',
612
+ phase: 'executing',
613
+ retryAfterFix: retryReviewer,
614
+ retryAfterFixStepIndex,
615
+ parallelCompleted: [],
616
+ parallelOutcomes: {},
617
+ });
618
+ out(handoffForAgent('feature-developer', steps[developerIndex]?.scope, steps[developerIndex]) + ` After fixes, re-run ${retryReviewer}.`);
619
+ process.exit(0);
620
+ }
621
+
622
+ writeStatus({ parallelCompleted: [], parallelOutcomes: {}, state: 'completed' });
623
+ }
624
+
625
+ if (status.state === 'blocked') {
626
+ writeStatus({ state: 'blocked', phase: 'blocked', currentAgent: completedAgent });
627
+ process.stdout.write('{}');
628
+ process.exit(0);
629
+ }
630
+
631
+ if (status.state === 'changes_requested') {
632
+ const reviewer = status.currentAgent || finishedAttempt?.entry?.agent;
633
+ if (!reviewer) {
634
+ process.stdout.write('{}');
635
+ process.exit(0);
636
+ }
476
637
  const retryAfterFixStepIndex = typeof status.pipelineIndex === 'number' ? status.pipelineIndex : 0;
477
638
  const developerIndex = scheduleRetry('feature-developer', reviewer, finishedAttempt);
478
639
  writeStatus({
@@ -483,7 +644,7 @@ if (status.state === 'changes_requested' && REVIEW_AGENTS.has(status.currentAgen
483
644
  retryAfterFix: reviewer,
484
645
  retryAfterFixStepIndex,
485
646
  });
486
- out(handoffForAgent('feature-developer') + ` After fixes, re-run ${reviewer}.`);
647
+ out(handoffForAgent('feature-developer', steps[developerIndex]?.scope, steps[developerIndex]) + ` After fixes, re-run ${reviewer}.`);
487
648
  process.exit(0);
488
649
  }
489
650
 
@@ -498,7 +659,7 @@ if (status.state === 'validation_failed' && status.currentAgent === 'build-verif
498
659
  retryAfterFix: 'build-verifier',
499
660
  retryAfterFixStepIndex,
500
661
  });
501
- out(handoffForAgent('feature-developer') + ' Read validation-report.md. After fixes, re-run build-verifier.');
662
+ out(handoffForAgent('feature-developer', steps[developerIndex]?.scope, steps[developerIndex]) + ' Read validation-report.md. After fixes, re-run build-verifier.');
502
663
  process.exit(0);
503
664
  }
504
665
 
@@ -506,35 +667,16 @@ if (status.state === 'completed' && status.retryAfterFix) {
506
667
  const retry = status.retryAfterFix;
507
668
  const retryIndex = scheduleRetry(retry, null, finishedAttempt, status.retryAfterFixStepIndex);
508
669
  writeStatus({ pipelineIndex: retryIndex, currentAgent: retry, state: 'in_progress', retryAfterFix: null, retryAfterFixStepIndex: null });
509
- out(handoffForAgent(retry));
670
+ out(handoffForAgent(retry, steps[retryIndex]?.scope, steps[retryIndex]));
510
671
  process.exit(0);
511
672
  }
512
673
 
513
- const idx = typeof status.pipelineIndex === 'number' ? status.pipelineIndex : 0;
514
- const currentStep = steps[idx];
515
674
  if (!currentStep) {
516
675
  writeStatus({ phase: 'done', state: 'completed', currentAgent: null });
517
676
  out(`Pipeline complete for "${slug}". Run /technical-retro ${slug}.`);
518
677
  process.exit(0);
519
678
  }
520
679
 
521
- const completedAgent = finishedAttempt?.entry?.agent || status.currentAgent || getStepAgents(currentStep)[0];
522
- const stepAgents = getStepAgents(currentStep);
523
-
524
- if (currentStep.parallel && stepAgents.length > 1) {
525
- const done = [...(status.parallelCompleted || [])];
526
- if (completedAgent && !done.includes(completedAgent)) {
527
- done.push(completedAgent);
528
- }
529
- writeStatus({ parallelCompleted: done });
530
- const remaining = stepAgents.filter((a) => !done.includes(a));
531
- if (remaining.length > 0) {
532
- process.stdout.write('{}');
533
- process.exit(0);
534
- }
535
- writeStatus({ parallelCompleted: [] });
536
- }
537
-
538
680
  if (hasGateAfterStep(currentStep, pipeline.humanGates)) {
539
681
  beginHumanGate();
540
682
  writeStatus({
@@ -576,6 +718,6 @@ writeStatus({
576
718
  if (nextStep.parallel && nextAgents.length > 1) {
577
719
  out(handoffForParallelStep(nextStep));
578
720
  } else {
579
- out(handoffForAgent(nextAgents[0], nextStep.scope));
721
+ out(handoffForAgent(nextAgents[0], nextStep.scope, nextStep));
580
722
  }
581
723
  NODE
@@ -35,7 +35,7 @@ Roster of specialists is **stack-specific** (agent names and write scopes differ
35
35
 
36
36
  **Cursor frontmatter:** all team pipeline agents use `readonly: false` so they can write team artifacts. Production/app restrictions live in the agent prompt body — do **not** set `readonly: true`.
37
37
 
38
- Full prompts: stack `agents/*.md`. Artifact conventions: stack `team/README.md`.
38
+ Full prompts: stack `agents/*.md`. Artifact conventions: stack `team/README.md`; schema SoT: **agent-artifact-contracts**.
39
39
 
40
40
  ## Dynamic pipeline
41
41
 
@@ -61,9 +61,21 @@ flowchart TD
61
61
 
62
62
  **Source of truth for order:** `pipeline.json` → `steps[]`. Never hardcode analyst → dev → review → QA when `pipeline.json` exists.
63
63
 
64
+ New pipelines retain top-level `profile` and include `routingDecision` with `complexity`, `riskFlags`, non-empty `routingReasons`, `estimatedWorkPackages`, and `openDecisionCount`. The stack `task-router` owns heuristics; this metadata is auditable evidence and does not change hook semantics.
65
+
64
66
  ## Metrics lifecycle
65
67
 
66
- Use the local `metrics.json` ledger described by **agent-task-metrics**: initialize attempts before invocation, let hooks record host-clock ends, and never guess a parallel completion from `currentAgent`.
68
+ Use the local `metrics.json` ledger described by **agent-task-metrics**: the orchestration runtime initializes attempts before invocation, hooks record host-clock ends, and agents never guess a parallel completion from `currentAgent`.
69
+
70
+ ## Artifact context index and receipts
71
+
72
+ Each slug has one `artifact-manifest.json`: `.cursor/team/tasks/<slug>/` or `.claude/team/tasks/<slug>/`. Schema SoT: **agent-artifact-contracts**.
73
+
74
+ - Read the manifest before broad prior-artifact discovery. Each step receives authoritative input entry IDs, owns named output entry IDs, and finishes with one terminal receipt linked to AC/task IDs.
75
+ - Markdown remains detailed evidence; the manifest records producer, status, authoritative paths, timestamps, outcome, and evidence paths.
76
+ - Agents upsert only entries they own and preserve foreign entries. Parallel agents use distinct IDs; the parent/orchestrator serializes manifest merges.
77
+ - The orchestration runtime/hook is the single writer for `status.json` lifecycle fields and `metrics.json`. Agents read status for eligibility but never rewrite state, gate, retry, attempt, timestamp, or metrics fields.
78
+ - Missing/stale manifests are reported as a handoff gap; fall back to `brief.md`, `decomposition.md`, and `pipeline.json` rather than inventing authority.
67
79
 
68
80
  ## status.json (pipeline mode)
69
81
 
@@ -75,10 +87,23 @@ Use the local `metrics.json` ledger described by **agent-task-metrics**: initial
75
87
  "currentAgent": "task-analyst",
76
88
  "phase": "executing",
77
89
  "state": "in_progress",
78
- "awaitingHumanGate": false
90
+ "awaitingHumanGate": false,
91
+ "activeAttempts": {},
92
+ "lastCompletedAttempt": null,
93
+ "parallelCompleted": [],
94
+ "parallelOutcomes": {}
79
95
  }
80
96
  ```
81
97
 
98
+ | Field | Meaning |
99
+ |-------|---------|
100
+ | `activeAttempts` | Transient timing bridge keyed by `attemptId` |
101
+ | `lastCompletedAttempt` | Diagnostic pointer to the latest finished attempt |
102
+ | `parallelCompleted` | Agents that have stopped in the current parallel step |
103
+ | `parallelOutcomes` | Per-agent `{ outcome, attemptId, confidence }` for the current parallel join; cleared when the step advances or retries |
104
+
105
+ On each `subagentStop`, the hook reconciles `status.state` from the completing producer's terminal `artifact-manifest.json` receipt when agents left lifecycle fields untouched. Wait for that handoff before invoking the next step.
106
+
82
107
  | state | Meaning |
83
108
  |-------|---------|
84
109
  | `in_progress` | Current step running |
@@ -102,7 +127,7 @@ Use the local `metrics.json` ledger described by **agent-task-metrics**: initial
102
127
  2. Read `pipeline.json` before every subagent invocation.
103
128
  3. One role per Task call — **except** parallel steps: multiple Task calls in one message.
104
129
  4. **Never** skip `humanGates` without `/task-continue` or explicit user approval.
105
- 5. Persist handoffs to disk (`brief.md`, `decomposition.md`, `debug-report.md`, `validation-report.md`, `architecture.md`, `migration-plan.md`, `ci-report.md`, `a11y-review.md`, `perf-report.md`, `security-review.md`, `api-contract-review.md`, `unit-test-plan.md`, `documentation.md`, `review.md`).
130
+ 5. Persist handoffs to disk and update the producer-owned terminal receipt in `artifact-manifest.json`; Markdown remains detailed evidence.
106
131
  6. On `changes_requested` from reviewers: hook sets `retryAfterFix` → developer → same reviewer.
107
132
  7. On `validation_failed` from `build-verifier`: hook sets `retryAfterFix: build-verifier` → developer → build-verifier.
108
133
  8. When all steps complete, suggest `/technical-retro <slug>`.
@@ -127,7 +152,7 @@ Use Task tool or `/agent-name`. Pass: slug, artifact paths, step `scope` if set.
127
152
  - **`strong`** → **never** pass `fast`; rely on agent pin or parent Max model.
128
153
  3. Parallel steps: apply the step-level `model` to **each** agent in the array (or omit → each agent’s own frontmatter).
129
154
 
130
- After each agent completes, ensure `status.json` has `state: completed` (or `awaiting_approval` if gate applies).
155
+ After each agent completes, verify its terminal manifest receipt, then let the orchestration runtime/hook reconcile `status.json` (`completed`, a blocking outcome, or `awaiting_approval`) from that receipt. Do not ask the agent to mutate shared lifecycle fields, and do not invoke the next specialist until the hook has written the handoff / updated `status.json`.
131
156
 
132
157
  ## Legacy mode
133
158
 
@@ -30,10 +30,21 @@
30
30
 
31
31
  Bootstrap `tasks/<slug>/` by **writing** artifacts (Write tool), not Shell `mkdir`. Cursor sandbox → `Operation not permitted` otherwise (worse on Desktop/Documents).
32
32
 
33
+ ## Artifact manifest and receipts
34
+
35
+ Each slug stores one context index at `.cursor/team/tasks/<slug>/artifact-manifest.json`. Schema v1 is defined by shared core `agent-team/agent-artifact-contracts.md`: every entry declares its producer, status, authoritative paths, acceptance-criteria IDs, decomposition task IDs, timestamps, and a terminal completion receipt.
36
+
37
+ Producer/consumer rules:
38
+
39
+ - Read the manifest first and use only the authoritative inputs needed by the current step; fall back to `brief.md`, `decomposition.md`, and `pipeline.json` when it is missing or stale, and report that gap.
40
+ - Producers upsert only their stable entry IDs, preserve foreign entries, and finish with `completed`, `blocked`, `changes_requested`, or `validation_failed` receipt evidence.
41
+ - The router owns `pipeline.json` and its routing entry; analyst, developer, verifier, and reviewer own their named outputs and receipts. Consumers do not edit another producer's receipt.
42
+ - Agents do not mutate `status.json` or `metrics.json`. The orchestration runtime/hook is their single writer. Parallel receipt merges are serialized by the parent/orchestrator.
43
+
33
44
  ## Local metrics ledger
34
45
 
35
- `metrics.json` is an append-only schema-v1 event ledger for one task run. It contains only allowlisted IDs, task dimensions, timestamps, outcomes, and attribution confidence—never prompts, secrets, file paths, or contents. The router writes `task_started` / `attempt_started`; the hook records host-clock completion and human-gate edges under a per-task lock with temp-and-rename.
46
+ `metrics.json` is an append-only schema-v1 event ledger for one task run. It contains only allowlisted IDs, task dimensions, timestamps, outcomes, and attribution confidence—never prompts, secrets, file paths, or contents. The orchestration runtime writes `task_started` / `attempt_started`; the hook records host-clock completion and human-gate edges under a per-task lock with temp-and-rename.
36
47
 
37
- `status.json.activeAttempts` is a transient timing bridge keyed by `attemptId`; `lastCompletedAttempt` is diagnostic. For parallel stops, the hook must use a stable payload identity or the sole active attempt. Otherwise an explicit `.metrics-receipts.json` receipt is fallback data marked `source: agent`, `attributionConfidence: fallback`; it must never guess from `currentAgent`.
48
+ `status.json.activeAttempts` is a transient timing bridge keyed by `attemptId`; `lastCompletedAttempt` is diagnostic; `parallelOutcomes` holds per-agent outcomes for the current parallel join until the step advances. For parallel stops, the hook must use a stable payload identity or the sole active attempt. Otherwise an explicit `.metrics-receipts.json` receipt is fallback data marked `source: agent`, `attributionConfidence: fallback`; it must never guess from `currentAgent`.
38
49
 
39
50
  Aggregate with `node packages/ai-rules/scripts/aggregate-agent-task-metrics.mjs --team-root .cursor/team`. It reports role/agent count, completed count, total/average duration, distinct completed run IDs, separate human-gate wait, and incomplete artifacts.
@@ -75,4 +75,11 @@ PASS | FAIL
75
75
 
76
76
  ## On completion
77
77
 
78
- Update `status.json`: `currentAgent: build-verifier`, `state: completed` (or `validation_failed` on FAIL). Do not fix code — report only.
78
+ Write the owned validation receipt (`completed` or `validation_failed`). Do not fix code — report only; the runtime owns `status.json`.
79
+
80
+ ## Artifact contract
81
+
82
+ - **Authoritative inputs:** read `.cursor/team/tasks/<slug>/artifact-manifest.json` first; consume handoff `artifactInputIds` when supplied, otherwise consume the manifest entries for brief/decomposition, implementation evidence, pipeline scope, and changed files.
83
+ - **Owned outputs:** `validation-report.md`; upsert only the manifest entry assigned by handoff (`artifactOutputId`; fallback `validation`) and preserve all foreign entries.
84
+ - **Terminal receipt:** cover applicable AC/task IDs, list authoritative evidence paths, and emit `completed` or `validation_failed`. Bind it to the supplied `attemptId` when present.
85
+ - **Shared state:** never create or mutate `status.json` or `metrics.json`; the orchestration runtime/hook owns lifecycle, gates, retries, attempts, and timestamps.
@@ -5,7 +5,7 @@ readonly: false
5
5
  model: fast
6
6
  ---
7
7
 
8
- You are a senior iOS / Swift / SwiftUI code reviewer. You may write only review artifacts under `.cursor/team/**` and update task `status.json`; do not modify production code.
8
+ You are a senior iOS / Swift / SwiftUI code reviewer. You may write only review artifacts under `.cursor/team/**` and terminal receipts; do not modify production code.
9
9
 
10
10
  ## Inputs
11
11
 
@@ -58,22 +58,17 @@ APPROVE | APPROVE_WITH_NOTES | REQUEST_CHANGES
58
58
 
59
59
  ## On completion
60
60
 
61
- Update `status.json`:
62
-
63
- ```json
64
- {
65
- "slug": "<slug>",
66
- "currentAgent": "code-reviewer",
67
- "phase": "executing",
68
- "state": "completed",
69
- "updatedAt": "<ISO8601>"
70
- }
71
- ```
72
-
73
- If **REQUEST_CHANGES**, set `"state": "changes_requested"` (hook will re-invoke feature-developer).
61
+ Write the owned terminal receipt described under **Artifact contract**. If **REQUEST_CHANGES**, emit receipt outcome `changes_requested`; the hook decides retry state.
74
62
 
75
63
  Do not mix this output with retrospective facilitation — keep review and retro separate.
76
64
 
77
65
  ## Design guidance
78
66
 
79
67
  - Load `design-guidance` (rule stem) when assessing structure, smells, or pattern fit.
68
+
69
+ ## Artifact contract
70
+
71
+ - **Authoritative inputs:** read `.cursor/team/tasks/<slug>/artifact-manifest.json` first; consume handoff `artifactInputIds` when supplied, otherwise consume the manifest entries for brief/decomposition, implementation, validation evidence, and the scoped git diff.
72
+ - **Owned outputs:** `review.md`; upsert only the manifest entry assigned by handoff (`artifactOutputId`; fallback `review`) and preserve all foreign entries.
73
+ - **Terminal receipt:** cover applicable AC/task IDs, list authoritative evidence paths, and emit `completed` or `changes_requested`. Bind it to the supplied `attemptId` when present.
74
+ - **Shared state:** never create or mutate `status.json` or `metrics.json`; the orchestration runtime/hook owns lifecycle, gates, retries, attempts, and timestamps.
@@ -45,7 +45,7 @@ When copying from `next` into `ios-swift` (or another stack):
45
45
 
46
46
  ## On completion
47
47
 
48
- `status.json`: `currentAgent: feature-developer`, `state: completed`. Handoff by layer + gaps for verifier/reviewer. No formal code review.
48
+ Emit the owned implementation receipt with outcome `completed`. Handoff by layer + gaps for verifier/reviewer. No formal code review.
49
49
 
50
50
  ## Design guidance
51
51
 
@@ -54,3 +54,10 @@ When copying from `next` into `ios-swift` (or another stack):
54
54
  ## Anti-sycophancy discipline
55
55
 
56
56
  - When writing or changing code, load / follow rule `anti-sycophancy-discipline`.
57
+
58
+ ## Artifact contract
59
+
60
+ - **Authoritative inputs:** read `.cursor/team/tasks/<slug>/artifact-manifest.json` first; consume handoff `artifactInputIds` when supplied, otherwise consume the manifest entries for `brief.md`, `decomposition.md`, current scope, and applicable prior findings.
61
+ - **Owned outputs:** `implementation.md` plus changed implementation/test paths; upsert only the manifest entry assigned by handoff (`artifactOutputId`; fallback `implementation`) and preserve all foreign entries.
62
+ - **Terminal receipt:** cover applicable AC/task IDs, list authoritative evidence paths, and emit `completed` or `blocked`. Bind it to the supplied `attemptId` when present.
63
+ - **Shared state:** never create or mutate `status.json` or `metrics.json`; the orchestration runtime/hook owns lifecycle, gates, retries, attempts, and timestamps.
@@ -20,10 +20,17 @@ Ask only blocking questions (AC gaps, Feature scope, platforms, offline/auth, AP
20
20
 
21
21
  1. `brief.md` — goal, AC, out of scope, assumptions.
22
22
  2. `decomposition.md` — ordered WPs with Depends on / Done when; mark unit/UI test WPs.
23
- 3. Update `status.json` `awaiting_approval` if human gate after analyst.
23
+ 3. Emit the analysis receipt; the runtime applies the configured analyst human gate.
24
24
 
25
25
  Do not invent architecture stacks (TCA, SwiftData, …) — frame «как в репо».
26
26
 
27
27
  ## Design guidance (optional)
28
28
 
29
29
  - Optional: Load `design-guidance` (rule stem) when assessing structure, smells, or pattern fit — only when decomposing refactor/architecture tasks (not default).
30
+
31
+ ## Artifact contract
32
+
33
+ - **Authoritative inputs:** read `.cursor/team/tasks/<slug>/artifact-manifest.json` first; consume `pipeline.json`, the manifest routing entry, user-approved decisions, and any authoritative architecture/approval entries.
34
+ - **Owned outputs:** `brief.md` and `decomposition.md`; upsert only the manifest entry assigned by handoff (`artifactOutputId`; fallback `analysis`) and preserve all foreign entries.
35
+ - **Terminal receipt:** cover applicable AC/task IDs, list authoritative evidence paths, and emit `completed` or `blocked`. Bind it to the supplied `attemptId` when present.
36
+ - **Shared state:** never create or mutate `status.json` or `metrics.json`; the orchestration runtime/hook owns lifecycle, gates, retries, attempts, and timestamps.