@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.
- package/CHANGELOG.md +12 -0
- package/package.json +1 -1
- package/presets/_shared/core/agent-team/agent-artifact-contracts.md +114 -0
- package/presets/_shared/core/agent-team/agent-team-orchestrator.md +64 -5
- package/presets/claude/go/agents/build-verifier.md +9 -2
- package/presets/claude/go/agents/code-reviewer.md +8 -1
- package/presets/claude/go/agents/feature-developer.md +8 -1
- package/presets/claude/go/agents/task-analyst.md +8 -1
- package/presets/claude/go/agents/task-router.md +33 -5
- package/presets/claude/go/commands/task.md +8 -6
- package/presets/claude/go/hooks/chain-team-phases.sh +195 -53
- package/presets/claude/go/rules/tooling-and-review/agent-team-orchestrator.md +30 -5
- package/presets/claude/go/team/README.md +13 -2
- package/presets/claude/ios-swift/agents/build-verifier.md +8 -1
- package/presets/claude/ios-swift/agents/code-reviewer.md +9 -14
- package/presets/claude/ios-swift/agents/feature-developer.md +8 -1
- package/presets/claude/ios-swift/agents/task-analyst.md +8 -1
- package/presets/claude/ios-swift/agents/task-router.md +31 -3
- package/presets/claude/ios-swift/commands/task.md +8 -6
- package/presets/claude/ios-swift/hooks/chain-team-phases.sh +177 -54
- package/presets/claude/ios-swift/rules/tooling-and-review/agent-team-orchestrator.md +33 -2
- package/presets/claude/ios-swift/team/README.md +13 -2
- package/presets/claude/java/agents/build-verifier.md +8 -1
- package/presets/claude/java/agents/code-reviewer.md +8 -1
- package/presets/claude/java/agents/feature-developer.md +8 -1
- package/presets/claude/java/agents/task-analyst.md +8 -1
- package/presets/claude/java/agents/task-router.md +31 -3
- package/presets/claude/java/commands/task.md +8 -6
- package/presets/claude/java/hooks/chain-team-phases.sh +195 -53
- package/presets/claude/java/rules/tooling-and-review/agent-team-orchestrator.md +30 -5
- package/presets/claude/java/team/README.md +13 -2
- package/presets/claude/mcp-ts/agents/build-verifier.md +9 -2
- package/presets/claude/mcp-ts/agents/feature-developer.md +7 -1
- package/presets/claude/mcp-ts/agents/task-analyst.md +8 -1
- package/presets/claude/mcp-ts/agents/task-router.md +33 -5
- package/presets/claude/mcp-ts/commands/task.md +8 -6
- package/presets/claude/mcp-ts/hooks/chain-team-phases.sh +181 -39
- package/presets/claude/mcp-ts/rules/tooling-and-review/agent-team-orchestrator.md +30 -5
- package/presets/claude/mcp-ts/team/README.md +13 -2
- package/presets/claude/next/agents/accessibility-reviewer.md +6 -12
- package/presets/claude/next/agents/api-contract-reviewer.md +6 -12
- package/presets/claude/next/agents/build-verifier.md +8 -12
- package/presets/claude/next/agents/ci-investigator.md +7 -13
- package/presets/claude/next/agents/code-reviewer.md +9 -14
- package/presets/claude/next/agents/debugger.md +6 -12
- package/presets/claude/next/agents/feature-developer.md +8 -1
- package/presets/claude/next/agents/migration-specialist.md +6 -12
- package/presets/claude/next/agents/performance-auditor.md +5 -11
- package/presets/claude/next/agents/qa-tester.md +6 -12
- package/presets/claude/next/agents/security-reviewer.md +6 -12
- package/presets/claude/next/agents/solution-architect.md +6 -12
- package/presets/claude/next/agents/task-analyst.md +10 -16
- package/presets/claude/next/agents/task-router.md +31 -3
- package/presets/claude/next/agents/tech-writer.md +6 -13
- package/presets/claude/next/agents/unit-test-generator.md +6 -10
- package/presets/claude/next/agents/unit-test-healer.md +6 -10
- package/presets/claude/next/agents/unit-test-planner.md +5 -11
- package/presets/claude/next/commands/task.md +8 -6
- package/presets/claude/next/hooks/chain-team-phases.sh +197 -54
- package/presets/claude/next/rules/tooling-and-review/agent-team-orchestrator.md +29 -4
- package/presets/claude/next/team/README.md +15 -3
- package/presets/claude/next/team/fixtures/artifact-manifest.example.json +61 -0
- package/presets/claude/next/team/fixtures/bugfix-standard.json +10 -0
- package/presets/claude/next/team/fixtures/feature-full.json +10 -0
- package/presets/claude/next/team/fixtures/feature-light.json +10 -0
- package/presets/claude/php-hexagonal/agents/build-verifier.md +9 -2
- package/presets/claude/php-hexagonal/agents/code-reviewer.md +8 -1
- package/presets/claude/php-hexagonal/agents/feature-developer.md +8 -1
- package/presets/claude/php-hexagonal/agents/task-analyst.md +8 -1
- package/presets/claude/php-hexagonal/agents/task-router.md +31 -3
- package/presets/claude/php-hexagonal/commands/task.md +8 -6
- package/presets/claude/php-hexagonal/hooks/README.md +1 -1
- package/presets/claude/php-hexagonal/hooks/chain-team-phases.sh +195 -53
- package/presets/claude/php-hexagonal/rules/tooling-and-review/agent-team-orchestrator.md +30 -5
- package/presets/claude/php-hexagonal/team/README.md +13 -2
- package/presets/claude/php-laravel/agents/build-verifier.md +9 -2
- package/presets/claude/php-laravel/agents/code-reviewer.md +8 -1
- package/presets/claude/php-laravel/agents/feature-developer.md +8 -1
- package/presets/claude/php-laravel/agents/task-analyst.md +8 -1
- package/presets/claude/php-laravel/agents/task-router.md +31 -3
- package/presets/claude/php-laravel/commands/task.md +8 -6
- package/presets/claude/php-laravel/hooks/chain-team-phases.sh +195 -53
- package/presets/claude/php-laravel/rules/tooling-and-review/agent-team-orchestrator.md +30 -5
- package/presets/claude/php-laravel/team/README.md +13 -2
- package/presets/cursor/go/agents/build-verifier.md +8 -1
- package/presets/cursor/go/agents/code-reviewer.md +8 -1
- package/presets/cursor/go/agents/feature-developer.md +8 -1
- package/presets/cursor/go/agents/task-analyst.md +8 -1
- package/presets/cursor/go/agents/task-router.md +31 -3
- package/presets/cursor/go/commands/task.md +8 -6
- package/presets/cursor/go/hooks/chain-team-phases.sh +195 -53
- package/presets/cursor/go/rules/agent-team-orchestrator.mdc +30 -5
- package/presets/cursor/go/team/README.md +13 -2
- package/presets/cursor/ios-swift/agents/build-verifier.md +8 -1
- package/presets/cursor/ios-swift/agents/code-reviewer.md +9 -14
- package/presets/cursor/ios-swift/agents/feature-developer.md +8 -1
- package/presets/cursor/ios-swift/agents/task-analyst.md +8 -1
- package/presets/cursor/ios-swift/agents/task-router.md +31 -3
- package/presets/cursor/ios-swift/commands/task.md +8 -6
- package/presets/cursor/ios-swift/hooks/chain-team-phases.sh +195 -54
- package/presets/cursor/ios-swift/rules/agent-team-orchestrator.mdc +23 -2
- package/presets/cursor/ios-swift/team/README.md +13 -2
- package/presets/cursor/java/agents/build-verifier.md +8 -1
- package/presets/cursor/java/agents/code-reviewer.md +8 -1
- package/presets/cursor/java/agents/feature-developer.md +8 -1
- package/presets/cursor/java/agents/task-analyst.md +8 -1
- package/presets/cursor/java/agents/task-router.md +31 -3
- package/presets/cursor/java/commands/task.md +8 -6
- package/presets/cursor/java/hooks/chain-team-phases.sh +195 -53
- package/presets/cursor/java/rules/agent-team-orchestrator.mdc +30 -5
- package/presets/cursor/java/team/README.md +13 -2
- package/presets/cursor/mcp-ts/agents/build-verifier.md +8 -1
- package/presets/cursor/mcp-ts/agents/feature-developer.md +7 -1
- package/presets/cursor/mcp-ts/agents/task-analyst.md +8 -1
- package/presets/cursor/mcp-ts/agents/task-router.md +31 -3
- package/presets/cursor/mcp-ts/commands/task.md +8 -6
- package/presets/cursor/mcp-ts/hooks/chain-team-phases.sh +181 -39
- package/presets/cursor/mcp-ts/rules/agent-team-orchestrator.mdc +30 -5
- package/presets/cursor/mcp-ts/team/README.md +13 -2
- package/presets/cursor/next/agents/accessibility-reviewer.md +7 -14
- package/presets/cursor/next/agents/api-contract-reviewer.md +7 -14
- package/presets/cursor/next/agents/build-verifier.md +8 -13
- package/presets/cursor/next/agents/ci-investigator.md +7 -14
- package/presets/cursor/next/agents/code-reviewer.md +9 -14
- package/presets/cursor/next/agents/debugger.md +6 -12
- package/presets/cursor/next/agents/feature-developer.md +8 -1
- package/presets/cursor/next/agents/migration-specialist.md +7 -14
- package/presets/cursor/next/agents/performance-auditor.md +6 -13
- package/presets/cursor/next/agents/qa-tester.md +6 -13
- package/presets/cursor/next/agents/security-reviewer.md +7 -14
- package/presets/cursor/next/agents/solution-architect.md +6 -12
- package/presets/cursor/next/agents/task-analyst.md +10 -16
- package/presets/cursor/next/agents/task-router.md +31 -3
- package/presets/cursor/next/agents/tech-writer.md +6 -13
- package/presets/cursor/next/agents/unit-test-generator.md +6 -11
- package/presets/cursor/next/agents/unit-test-healer.md +6 -11
- package/presets/cursor/next/agents/unit-test-planner.md +6 -13
- package/presets/cursor/next/commands/task.md +8 -6
- package/presets/cursor/next/hooks/chain-team-phases.sh +197 -54
- package/presets/cursor/next/rules/agent-team-orchestrator.mdc +30 -5
- package/presets/cursor/next/team/README.md +16 -4
- package/presets/cursor/next/team/fixtures/artifact-manifest.example.json +61 -0
- package/presets/cursor/next/team/fixtures/bugfix-standard.json +10 -0
- package/presets/cursor/next/team/fixtures/feature-full.json +10 -0
- package/presets/cursor/next/team/fixtures/feature-light.json +10 -0
- package/presets/cursor/php-hexagonal/agents/build-verifier.md +8 -1
- package/presets/cursor/php-hexagonal/agents/code-reviewer.md +8 -1
- package/presets/cursor/php-hexagonal/agents/feature-developer.md +8 -1
- package/presets/cursor/php-hexagonal/agents/task-analyst.md +8 -1
- package/presets/cursor/php-hexagonal/agents/task-router.md +31 -3
- package/presets/cursor/php-hexagonal/commands/task.md +8 -6
- package/presets/cursor/php-hexagonal/hooks/chain-team-phases.sh +195 -53
- package/presets/cursor/php-hexagonal/rules/agent-team-orchestrator.mdc +30 -5
- package/presets/cursor/php-hexagonal/team/README.md +13 -2
- package/presets/cursor/php-laravel/agents/build-verifier.md +8 -1
- package/presets/cursor/php-laravel/agents/code-reviewer.md +8 -1
- package/presets/cursor/php-laravel/agents/feature-developer.md +8 -1
- package/presets/cursor/php-laravel/agents/task-analyst.md +8 -1
- package/presets/cursor/php-laravel/agents/task-router.md +31 -3
- package/presets/cursor/php-laravel/commands/task.md +8 -6
- package/presets/cursor/php-laravel/hooks/chain-team-phases.sh +195 -53
- package/presets/cursor/php-laravel/rules/agent-team-orchestrator.mdc +30 -5
- package/presets/cursor/php-laravel/team/README.md +13 -2
|
@@ -122,6 +122,27 @@ function roleFor(agent) {
|
|
|
122
122
|
if (/writer/.test(agent || '')) return 'docs';
|
|
123
123
|
return 'implementation';
|
|
124
124
|
}
|
|
125
|
+
const DEFAULT_ARTIFACT_OUTPUT_IDS = {
|
|
126
|
+
'task-router': 'routing',
|
|
127
|
+
'task-analyst': 'analysis',
|
|
128
|
+
'solution-architect': 'architecture',
|
|
129
|
+
'migration-specialist': 'migration-plan',
|
|
130
|
+
debugger: 'debug',
|
|
131
|
+
'ci-investigator': 'ci',
|
|
132
|
+
'feature-developer': 'implementation',
|
|
133
|
+
'build-verifier': 'validation',
|
|
134
|
+
'code-reviewer': 'review',
|
|
135
|
+
'security-reviewer': 'security-review',
|
|
136
|
+
'accessibility-reviewer': 'a11y-review',
|
|
137
|
+
'performance-auditor': 'perf-report',
|
|
138
|
+
'api-contract-reviewer': 'api-contract-review',
|
|
139
|
+
'tech-writer': 'documentation',
|
|
140
|
+
};
|
|
141
|
+
function artifactOutputIdFor(step, agent) {
|
|
142
|
+
if (step && typeof step.artifactOutputId === 'string') return step.artifactOutputId;
|
|
143
|
+
if (step?.artifactOutputIds && typeof step.artifactOutputIds[agent] === 'string') return step.artifactOutputIds[agent];
|
|
144
|
+
return DEFAULT_ARTIFACT_OUTPUT_IDS[agent] || agent;
|
|
145
|
+
}
|
|
125
146
|
function activateAttempts(step, stepIndex) {
|
|
126
147
|
const active = { ...(status.activeAttempts || {}) }; const agents = getStepAgents(step);
|
|
127
148
|
const parallelGroupId = step.parallel && agents.length > 1 ? `${status.metricsRunId || slug}:step-${stepIndex}` : null;
|
|
@@ -146,11 +167,61 @@ function resolvedAttempt() {
|
|
|
146
167
|
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 {}
|
|
147
168
|
return null;
|
|
148
169
|
}
|
|
170
|
+
function readBoundReceiptOutcome(agent, step, resolved) {
|
|
171
|
+
if (!agent || !resolved) return { outcome: null, receiptRequired: false };
|
|
172
|
+
const manifestPath = path.join(taskDir, 'artifact-manifest.json');
|
|
173
|
+
if (!fs.existsSync(manifestPath)) return { outcome: null, receiptRequired: false };
|
|
174
|
+
try {
|
|
175
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
176
|
+
const artifacts = Array.isArray(manifest.artifacts) ? manifest.artifacts : [];
|
|
177
|
+
const expectedOutputId = artifactOutputIdFor(step, agent);
|
|
178
|
+
const attemptStartedAt = Date.parse(resolved.entry.startedAt);
|
|
179
|
+
const owned = artifacts
|
|
180
|
+
.filter((entry) => {
|
|
181
|
+
if (!entry || entry.producer !== agent || !entry.receipt || typeof entry.receipt.outcome !== 'string') return false;
|
|
182
|
+
const boundByAttempt = typeof entry.receipt.attemptId === 'string' && entry.receipt.attemptId === resolved.attemptId;
|
|
183
|
+
const boundByOutput = entry.receipt.attemptId == null && entry.id === expectedOutputId;
|
|
184
|
+
if (!boundByAttempt && !boundByOutput) return false;
|
|
185
|
+
const terminalAt = Date.parse(entry.timestamps?.completedAt || entry.timestamps?.updatedAt || '');
|
|
186
|
+
return Number.isFinite(terminalAt) && (!Number.isFinite(attemptStartedAt) || terminalAt >= attemptStartedAt);
|
|
187
|
+
})
|
|
188
|
+
.sort((a, b) => {
|
|
189
|
+
const ta = Date.parse(a.timestamps?.completedAt || a.timestamps?.updatedAt || 0) || 0;
|
|
190
|
+
const tb = Date.parse(b.timestamps?.completedAt || b.timestamps?.updatedAt || 0) || 0;
|
|
191
|
+
return tb - ta;
|
|
192
|
+
});
|
|
193
|
+
return { outcome: owned[0]?.receipt?.outcome || null, receiptRequired: true };
|
|
194
|
+
} catch {
|
|
195
|
+
return { outcome: null, receiptRequired: false };
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
function reconcileStatusFromManifest(agent, step, resolved) {
|
|
199
|
+
const terminal = new Set(['completed', 'blocked', 'changes_requested', 'validation_failed']);
|
|
200
|
+
const { outcome, receiptRequired } = readBoundReceiptOutcome(agent, step, resolved);
|
|
201
|
+
if (terminal.has(outcome)) {
|
|
202
|
+
status.state = outcome;
|
|
203
|
+
return outcome;
|
|
204
|
+
}
|
|
205
|
+
if (receiptRequired) {
|
|
206
|
+
status.state = 'awaiting_artifact_receipt';
|
|
207
|
+
status.phase = 'blocked';
|
|
208
|
+
return status.state;
|
|
209
|
+
}
|
|
210
|
+
if (terminal.has(status.state)) return status.state;
|
|
211
|
+
// Host reported subagent completed and no blocking receipt → treat as completed for metrics/advance.
|
|
212
|
+
if (hookInput.status === 'completed') {
|
|
213
|
+
status.state = 'completed';
|
|
214
|
+
return 'completed';
|
|
215
|
+
}
|
|
216
|
+
return status.state;
|
|
217
|
+
}
|
|
218
|
+
|
|
149
219
|
function finishActiveAttempt() {
|
|
150
220
|
const resolved = resolvedAttempt(); if (!resolved) return null;
|
|
151
221
|
const durationMs = Date.parse(now) - Date.parse(resolved.entry.startedAt);
|
|
152
|
-
|
|
153
|
-
|
|
222
|
+
const outcome = ['completed', 'blocked', 'changes_requested', 'validation_failed'].includes(status.state) ? status.state : 'unknown';
|
|
223
|
+
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 });
|
|
224
|
+
const active = { ...(status.activeAttempts || {}) }; delete active[resolved.attemptId]; status.activeAttempts = active; status.lastCompletedAttempt = { attemptId: resolved.attemptId, completedAt: now }; return { ...resolved, outcome };
|
|
154
225
|
}
|
|
155
226
|
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; }
|
|
156
227
|
function endHumanGateIfResumed() {
|
|
@@ -219,54 +290,54 @@ function findNextExecutableStep(steps, startIdx, dir) {
|
|
|
219
290
|
const AGENT_HANDOFF = {
|
|
220
291
|
'task-analyst': (s) =>
|
|
221
292
|
`Invoke task-analyst for slug "${s}". Write brief.md and decomposition.md under .cursor/team/tasks/${s}/. ` +
|
|
222
|
-
`
|
|
293
|
+
`Upsert the analysis receipt in artifact-manifest.json; do not mutate status.json lifecycle fields. Do not use legacy phase analysis.`,
|
|
223
294
|
debugger: (s) =>
|
|
224
295
|
`Invoke debugger subagent for slug "${s}". Investigate root cause; write debug-report.md with fixApplied: true/false. ` +
|
|
225
|
-
`
|
|
296
|
+
`Upsert the owned terminal receipt in artifact-manifest.json; do not mutate status.json lifecycle fields.`,
|
|
226
297
|
'solution-architect': (s) =>
|
|
227
|
-
`Invoke solution-architect for slug "${s}". Read brief.md; write architecture.md.
|
|
298
|
+
`Invoke solution-architect for slug "${s}". Read brief.md; write architecture.md. Upsert the owned terminal receipt in artifact-manifest.json; do not mutate status.json lifecycle fields.`,
|
|
228
299
|
'migration-specialist': (s) =>
|
|
229
|
-
`Invoke migration-specialist for slug "${s}". Read brief.md; write migration-plan.md.
|
|
300
|
+
`Invoke migration-specialist for slug "${s}". Read brief.md; write migration-plan.md. Upsert the owned terminal receipt in artifact-manifest.json; do not mutate status.json lifecycle fields.`,
|
|
230
301
|
'api-contract-reviewer': (s) =>
|
|
231
|
-
`Invoke api-contract-reviewer (readonly) for slug "${s}". Write api-contract-review.md.
|
|
302
|
+
`Invoke api-contract-reviewer (readonly) for slug "${s}". Write api-contract-review.md. Upsert the owned terminal receipt in artifact-manifest.json; do not mutate status.json lifecycle fields.`,
|
|
232
303
|
'ci-investigator': (s) =>
|
|
233
304
|
`Invoke ci-investigator for slug "${s}". Diagnose CI failure; write ci-report.md; apply minimal fix. ` +
|
|
234
|
-
`Use ci-investigation skill.
|
|
305
|
+
`Use ci-investigation skill. Upsert the owned terminal receipt in artifact-manifest.json; do not mutate status.json lifecycle fields.`,
|
|
235
306
|
'feature-developer': (s) =>
|
|
236
307
|
`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. ` +
|
|
237
|
-
`Follow feature-delivery-workflow.mdc and reference-features.mdc. Include unit tests for mappers/logic before handoff.
|
|
308
|
+
`Follow feature-delivery-workflow.mdc and reference-features.mdc. Include unit tests for mappers/logic 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}". Run lint:js, lint:css, type-check, and targeted unit tests from app/. ` +
|
|
240
|
-
`Write validation-report.md
|
|
311
|
+
`Write validation-report.md and emit receipt outcome completed or validation_failed. Do not edit production code.`,
|
|
241
312
|
'accessibility-reviewer': (s) =>
|
|
242
|
-
`Invoke accessibility-reviewer (readonly) for slug "${s}". Review UI diff; write a11y-review.md.
|
|
313
|
+
`Invoke accessibility-reviewer (readonly) for slug "${s}". Review UI diff; write a11y-review.md. Upsert the owned terminal receipt in artifact-manifest.json; do not mutate status.json lifecycle fields.`,
|
|
243
314
|
'performance-auditor': (s) =>
|
|
244
|
-
`Invoke performance-auditor (readonly) for slug "${s}". Write perf-report.md.
|
|
315
|
+
`Invoke performance-auditor (readonly) for slug "${s}". Write perf-report.md. Upsert the owned terminal receipt in artifact-manifest.json; do not mutate status.json lifecycle fields.`,
|
|
245
316
|
'code-reviewer': (s) =>
|
|
246
317
|
`Invoke code-reviewer subagent (readonly) for slug "${s}". Review git diff vs brief.md per code-review-mr.mdc. ` +
|
|
247
|
-
`
|
|
318
|
+
`Upsert the owned terminal receipt in artifact-manifest.json; do not mutate status.json lifecycle fields.`,
|
|
248
319
|
'security-reviewer': (s) =>
|
|
249
|
-
`Invoke security-reviewer (readonly) for slug "${s}". Write security-review.md.
|
|
320
|
+
`Invoke security-reviewer (readonly) for slug "${s}". Write security-review.md. Upsert the owned terminal receipt in artifact-manifest.json; do not mutate status.json lifecycle fields.`,
|
|
250
321
|
'qa-tester': (s, scope) =>
|
|
251
322
|
`Invoke qa-tester for slug "${s}"${scope ? ` (scope: ${scope})` : ''}. ` +
|
|
252
|
-
`Add/update tests per brief. Follow playwright-agents.mdc.
|
|
323
|
+
`Add/update tests per brief. Follow playwright-agents.mdc. Upsert the owned terminal receipt in artifact-manifest.json; do not mutate status.json lifecycle fields.`,
|
|
253
324
|
'unit-test-planner': (s) =>
|
|
254
|
-
`Invoke unit-test-planner for slug "${s}". Write unit-test-plan.md. Use unit-testing skill.
|
|
325
|
+
`Invoke unit-test-planner for slug "${s}". Write unit-test-plan.md. Use unit-testing skill. Upsert the owned terminal receipt in artifact-manifest.json; do not mutate status.json lifecycle fields.`,
|
|
255
326
|
'unit-test-generator': (s) =>
|
|
256
|
-
`Invoke unit-test-generator for slug "${s}". Implement specs from unit-test-plan.md. Use unit-testing skill.
|
|
327
|
+
`Invoke unit-test-generator for slug "${s}". Implement specs 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.`,
|
|
257
328
|
'unit-test-healer': (s) =>
|
|
258
|
-
`Invoke unit-test-healer for slug "${s}". Fix failing unit tests. Use unit-testing and debug-investigation skills.
|
|
329
|
+
`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.`,
|
|
259
330
|
'playwright-test-planner': (s) =>
|
|
260
331
|
`Invoke playwright-test-planner for slug "${s}". Create/update app/__tests__/e2e/**/*.cases.md. ` +
|
|
261
|
-
`Follow playwright-agents.mdc and the playwright-e2e skill.
|
|
332
|
+
`Follow playwright-agents.mdc and the playwright-e2e skill. Upsert the owned terminal receipt in artifact-manifest.json; do not mutate status.json lifecycle fields.`,
|
|
262
333
|
'playwright-test-generator': (s) =>
|
|
263
334
|
`Invoke playwright-test-generator for slug "${s}". Generate/update *.spec.ts from matching *.cases.md. ` +
|
|
264
|
-
`Use playwright MCP when available. Follow playwright-agents.mdc and the playwright-e2e skill.
|
|
335
|
+
`Use playwright MCP when available. Follow playwright-agents.mdc and the playwright-e2e skill. Upsert the owned terminal receipt in artifact-manifest.json; do not mutate status.json lifecycle fields.`,
|
|
265
336
|
'playwright-test-healer': (s) =>
|
|
266
337
|
`Invoke playwright-test-healer for slug "${s}". Fix failing/flaky e2e while preserving cases.md assertions. ` +
|
|
267
|
-
`Use playwright MCP when available. Follow playwright-agents.mdc and debug-investigation.
|
|
338
|
+
`Use playwright MCP when available. Follow playwright-agents.mdc and debug-investigation. Upsert the owned terminal receipt in artifact-manifest.json; do not mutate status.json lifecycle fields.`,
|
|
268
339
|
'tech-writer': (s) =>
|
|
269
|
-
`Invoke tech-writer for slug "${s}". Write documentation.md from task artifacts.
|
|
340
|
+
`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.`,
|
|
270
341
|
};
|
|
271
342
|
|
|
272
343
|
const REVIEW_AGENTS = new Set([
|
|
@@ -279,15 +350,20 @@ const REVIEW_AGENTS = new Set([
|
|
|
279
350
|
function metricsHandoff(agent) {
|
|
280
351
|
const matches = Object.entries(status.activeAttempts || {}).filter(([, entry]) => entry.agent === agent);
|
|
281
352
|
if (matches.length === 1) {
|
|
282
|
-
return ` Metrics attemptId: ${matches[0][0]};
|
|
353
|
+
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.`;
|
|
283
354
|
}
|
|
284
355
|
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.';
|
|
285
356
|
}
|
|
286
357
|
|
|
287
|
-
function
|
|
358
|
+
function artifactHandoff(step, agent) {
|
|
359
|
+
const inputIds = Array.isArray(step?.artifactInputIds) ? step.artifactInputIds.join(', ') : 'manifest defaults';
|
|
360
|
+
return ` Artifact inputs: ${inputIds}; owned output ID: ${artifactOutputIdFor(step, agent)}. Bind the terminal receipt to the supplied attemptId.`;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
function handoffForAgent(agent, scope, step = null) {
|
|
288
364
|
const fn = AGENT_HANDOFF[agent];
|
|
289
|
-
const handoff = fn ? fn(slug, scope) : `Invoke ${agent} subagent for slug "${slug}".
|
|
290
|
-
return handoff + metricsHandoff(agent);
|
|
365
|
+
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.`;
|
|
366
|
+
return handoff + artifactHandoff(step, agent) + metricsHandoff(agent);
|
|
291
367
|
}
|
|
292
368
|
|
|
293
369
|
function handoffForParallelStep(step) {
|
|
@@ -297,7 +373,8 @@ function handoffForParallelStep(step) {
|
|
|
297
373
|
.map(([attemptId, entry]) => `${entry.agent}=${attemptId}`);
|
|
298
374
|
return (
|
|
299
375
|
`Invoke IN PARALLEL (one message, multiple Task calls): ${agents.join(' AND ')} for slug "${slug}". ` +
|
|
300
|
-
`Each agent writes its artifact and
|
|
376
|
+
`Each agent writes its artifact and owned receipt; the hook records parallelCompleted from stops. Do not mutate status.json lifecycle fields. ` +
|
|
377
|
+
`Artifact inputs: ${Array.isArray(step.artifactInputIds) ? step.artifactInputIds.join(', ') : 'manifest defaults'}; owned output IDs: ${agents.map((agent) => `${agent}=${artifactOutputIdFor(step, agent)}`).join('; ')}. ` +
|
|
301
378
|
`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.`
|
|
302
379
|
);
|
|
303
380
|
}
|
|
@@ -336,8 +413,12 @@ function legacyFlow() {
|
|
|
336
413
|
appendMetric({ type: 'retry_scheduled', at: now, attemptId: predecessorAttemptId, nextAgent: agent, retryAfterFix: retryAfterFix || null, source: 'hook', attributionConfidence: 'unknown' });
|
|
337
414
|
activateAttempts({ agent }, stepIndex);
|
|
338
415
|
}
|
|
339
|
-
if (status.state === 'changes_requested'
|
|
416
|
+
if (status.state === 'changes_requested') {
|
|
340
417
|
const reviewer = status.currentAgent;
|
|
418
|
+
if (!reviewer) {
|
|
419
|
+
process.stdout.write('{}');
|
|
420
|
+
process.exit(0);
|
|
421
|
+
}
|
|
341
422
|
scheduleLegacyRetry('feature-developer', reviewer);
|
|
342
423
|
writeStatus({
|
|
343
424
|
currentAgent: 'feature-developer',
|
|
@@ -411,7 +492,7 @@ if (hookInput.action === 'end_human_gate') {
|
|
|
411
492
|
parallelCompleted: [],
|
|
412
493
|
});
|
|
413
494
|
if (resumeNext.step.parallel && resumeAgents.length > 1) out(handoffForParallelStep(resumeNext.step));
|
|
414
|
-
else out(handoffForAgent(resumeAgents[0], resumeNext.step.scope));
|
|
495
|
+
else out(handoffForAgent(resumeAgents[0], resumeNext.step.scope, resumeNext.step));
|
|
415
496
|
process.exit(0);
|
|
416
497
|
}
|
|
417
498
|
}
|
|
@@ -440,6 +521,19 @@ const steps = pipeline.steps || [];
|
|
|
440
521
|
const initialIdx = typeof status.pipelineIndex === 'number' ? status.pipelineIndex : 0;
|
|
441
522
|
const initialStep = steps[initialIdx];
|
|
442
523
|
const activeAttemptCountBeforeFinish = Object.keys(status.activeAttempts || {}).length;
|
|
524
|
+
const completingAgentHint = (() => {
|
|
525
|
+
const resolved = resolvedAttempt();
|
|
526
|
+
if (resolved?.entry?.agent) return resolved.entry.agent;
|
|
527
|
+
if (status.currentAgent) return status.currentAgent;
|
|
528
|
+
return getStepAgents(initialStep)[0];
|
|
529
|
+
})();
|
|
530
|
+
const reconcilingAttempt = resolvedAttempt();
|
|
531
|
+
reconcileStatusFromManifest(completingAgentHint, initialStep, reconcilingAttempt);
|
|
532
|
+
if (status.state === 'awaiting_artifact_receipt') {
|
|
533
|
+
writeStatus({});
|
|
534
|
+
process.stdout.write('{}');
|
|
535
|
+
process.exit(0);
|
|
536
|
+
}
|
|
443
537
|
const finishedAttempt = finishActiveAttempt();
|
|
444
538
|
if (!finishedAttempt && (hookInput.attemptId || activeAttemptCountBeforeFinish > 0)) {
|
|
445
539
|
process.stdout.write('{}');
|
|
@@ -447,6 +541,7 @@ if (!finishedAttempt && (hookInput.attemptId || activeAttemptCountBeforeFinish >
|
|
|
447
541
|
}
|
|
448
542
|
endHumanGateIfResumed();
|
|
449
543
|
if (steps.length === 0 || pipeline.autoChain === false) {
|
|
544
|
+
writeStatus({});
|
|
450
545
|
process.stdout.write('{}');
|
|
451
546
|
process.exit(0);
|
|
452
547
|
}
|
|
@@ -474,8 +569,75 @@ function scheduleRetry(agent, retryAfterFix, predecessor, explicitStepIndex = nu
|
|
|
474
569
|
return stepIndex;
|
|
475
570
|
}
|
|
476
571
|
|
|
477
|
-
|
|
478
|
-
|
|
572
|
+
const idx = typeof status.pipelineIndex === 'number' ? status.pipelineIndex : 0;
|
|
573
|
+
const currentStep = steps[idx];
|
|
574
|
+
const completedAgent = finishedAttempt?.entry?.agent || status.currentAgent || getStepAgents(currentStep)[0];
|
|
575
|
+
const stepAgents = getStepAgents(currentStep);
|
|
576
|
+
|
|
577
|
+
if (currentStep?.parallel && stepAgents.length > 1) {
|
|
578
|
+
const done = [...(status.parallelCompleted || [])];
|
|
579
|
+
const outcomes = { ...(status.parallelOutcomes || {}) };
|
|
580
|
+
if (finishedAttempt && completedAgent) {
|
|
581
|
+
if (!done.includes(completedAgent)) done.push(completedAgent);
|
|
582
|
+
outcomes[completedAgent] = {
|
|
583
|
+
outcome: finishedAttempt.outcome || status.state,
|
|
584
|
+
attemptId: finishedAttempt.attemptId,
|
|
585
|
+
confidence: finishedAttempt.confidence,
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
writeStatus({ parallelCompleted: done, parallelOutcomes: outcomes });
|
|
589
|
+
const remaining = stepAgents.filter((agent) => !done.includes(agent));
|
|
590
|
+
if (remaining.length > 0) {
|
|
591
|
+
process.stdout.write('{}');
|
|
592
|
+
process.exit(0);
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
const blockedProducer = stepAgents.find((agent) => outcomes[agent]?.outcome === 'blocked');
|
|
596
|
+
if (blockedProducer) {
|
|
597
|
+
writeStatus({ parallelCompleted: [], parallelOutcomes: {}, state: 'blocked', phase: 'blocked', currentAgent: blockedProducer });
|
|
598
|
+
process.stdout.write('{}');
|
|
599
|
+
process.exit(0);
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
const retryReviewer = stepAgents.find((agent) => outcomes[agent]?.outcome === 'changes_requested');
|
|
603
|
+
if (retryReviewer) {
|
|
604
|
+
const retryAfterFixStepIndex = idx;
|
|
605
|
+
const predecessor = outcomes[retryReviewer]
|
|
606
|
+
? {
|
|
607
|
+
attemptId: outcomes[retryReviewer].attemptId,
|
|
608
|
+
confidence: outcomes[retryReviewer].confidence,
|
|
609
|
+
}
|
|
610
|
+
: finishedAttempt;
|
|
611
|
+
const developerIndex = scheduleRetry('feature-developer', retryReviewer, predecessor);
|
|
612
|
+
writeStatus({
|
|
613
|
+
pipelineIndex: developerIndex,
|
|
614
|
+
currentAgent: 'feature-developer',
|
|
615
|
+
state: 'in_progress',
|
|
616
|
+
phase: 'executing',
|
|
617
|
+
retryAfterFix: retryReviewer,
|
|
618
|
+
retryAfterFixStepIndex,
|
|
619
|
+
parallelCompleted: [],
|
|
620
|
+
parallelOutcomes: {},
|
|
621
|
+
});
|
|
622
|
+
out(handoffForAgent('feature-developer', steps[developerIndex]?.scope, steps[developerIndex]) + ` After fixes, re-run ${retryReviewer}.`);
|
|
623
|
+
process.exit(0);
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
writeStatus({ parallelCompleted: [], parallelOutcomes: {}, state: 'completed' });
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
if (status.state === 'blocked') {
|
|
630
|
+
writeStatus({ state: 'blocked', phase: 'blocked', currentAgent: completedAgent });
|
|
631
|
+
process.stdout.write('{}');
|
|
632
|
+
process.exit(0);
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
if (status.state === 'changes_requested') {
|
|
636
|
+
const reviewer = status.currentAgent || finishedAttempt?.entry?.agent;
|
|
637
|
+
if (!reviewer) {
|
|
638
|
+
process.stdout.write('{}');
|
|
639
|
+
process.exit(0);
|
|
640
|
+
}
|
|
479
641
|
const retryAfterFixStepIndex = typeof status.pipelineIndex === 'number' ? status.pipelineIndex : 0;
|
|
480
642
|
const developerIndex = scheduleRetry('feature-developer', reviewer, finishedAttempt);
|
|
481
643
|
writeStatus({
|
|
@@ -486,7 +648,7 @@ if (status.state === 'changes_requested' && REVIEW_AGENTS.has(status.currentAgen
|
|
|
486
648
|
retryAfterFix: reviewer,
|
|
487
649
|
retryAfterFixStepIndex,
|
|
488
650
|
});
|
|
489
|
-
out(handoffForAgent('feature-developer') + ` After fixes, re-run ${reviewer}.`);
|
|
651
|
+
out(handoffForAgent('feature-developer', steps[developerIndex]?.scope, steps[developerIndex]) + ` After fixes, re-run ${reviewer}.`);
|
|
490
652
|
process.exit(0);
|
|
491
653
|
}
|
|
492
654
|
|
|
@@ -501,7 +663,7 @@ if (status.state === 'validation_failed' && status.currentAgent === 'build-verif
|
|
|
501
663
|
retryAfterFix: 'build-verifier',
|
|
502
664
|
retryAfterFixStepIndex,
|
|
503
665
|
});
|
|
504
|
-
out(handoffForAgent('feature-developer') + ' Read validation-report.md. After fixes, re-run build-verifier.');
|
|
666
|
+
out(handoffForAgent('feature-developer', steps[developerIndex]?.scope, steps[developerIndex]) + ' Read validation-report.md. After fixes, re-run build-verifier.');
|
|
505
667
|
process.exit(0);
|
|
506
668
|
}
|
|
507
669
|
|
|
@@ -509,35 +671,16 @@ if (status.state === 'completed' && status.retryAfterFix) {
|
|
|
509
671
|
const retry = status.retryAfterFix;
|
|
510
672
|
const retryIndex = scheduleRetry(retry, null, finishedAttempt, status.retryAfterFixStepIndex);
|
|
511
673
|
writeStatus({ pipelineIndex: retryIndex, currentAgent: retry, state: 'in_progress', retryAfterFix: null, retryAfterFixStepIndex: null });
|
|
512
|
-
out(handoffForAgent(retry));
|
|
674
|
+
out(handoffForAgent(retry, steps[retryIndex]?.scope, steps[retryIndex]));
|
|
513
675
|
process.exit(0);
|
|
514
676
|
}
|
|
515
677
|
|
|
516
|
-
const idx = typeof status.pipelineIndex === 'number' ? status.pipelineIndex : 0;
|
|
517
|
-
const currentStep = steps[idx];
|
|
518
678
|
if (!currentStep) {
|
|
519
679
|
writeStatus({ phase: 'done', state: 'completed', currentAgent: null });
|
|
520
680
|
out(`Pipeline complete for "${slug}". Run /technical-retro ${slug}.`);
|
|
521
681
|
process.exit(0);
|
|
522
682
|
}
|
|
523
683
|
|
|
524
|
-
const completedAgent = finishedAttempt?.entry?.agent || status.currentAgent || getStepAgents(currentStep)[0];
|
|
525
|
-
const stepAgents = getStepAgents(currentStep);
|
|
526
|
-
|
|
527
|
-
if (currentStep.parallel && stepAgents.length > 1) {
|
|
528
|
-
const done = [...(status.parallelCompleted || [])];
|
|
529
|
-
if (completedAgent && !done.includes(completedAgent)) {
|
|
530
|
-
done.push(completedAgent);
|
|
531
|
-
}
|
|
532
|
-
writeStatus({ parallelCompleted: done });
|
|
533
|
-
const remaining = stepAgents.filter((a) => !done.includes(a));
|
|
534
|
-
if (remaining.length > 0) {
|
|
535
|
-
process.stdout.write('{}');
|
|
536
|
-
process.exit(0);
|
|
537
|
-
}
|
|
538
|
-
writeStatus({ parallelCompleted: [] });
|
|
539
|
-
}
|
|
540
|
-
|
|
541
684
|
if (hasGateAfterStep(currentStep, pipeline.humanGates)) {
|
|
542
685
|
beginHumanGate();
|
|
543
686
|
writeStatus({
|
|
@@ -579,6 +722,6 @@ writeStatus({
|
|
|
579
722
|
if (nextStep.parallel && nextAgents.length > 1) {
|
|
580
723
|
out(handoffForParallelStep(nextStep));
|
|
581
724
|
} else {
|
|
582
|
-
out(handoffForAgent(nextAgents[0], nextStep.scope));
|
|
725
|
+
out(handoffForAgent(nextAgents[0], nextStep.scope, nextStep));
|
|
583
726
|
}
|
|
584
727
|
NODE
|
|
@@ -51,7 +51,7 @@ Work request без `/task` — см. **`agent-team-intake.mdc`** (когда п
|
|
|
51
51
|
|
|
52
52
|
**Cursor frontmatter:** all team pipeline agents use `readonly: false` so they can write `.cursor/team/**`. Production/app restrictions live in the agent prompt body — do **not** set `readonly: true` (Cursor blocks *all* file writes, including team artifacts).
|
|
53
53
|
|
|
54
|
-
Full prompts: `.cursor/agents/*.md`. Artifact conventions: `.cursor/team/README.md
|
|
54
|
+
Full prompts: `.cursor/agents/*.md`. Artifact conventions: `.cursor/team/README.md`; schema SoT: **agent-artifact-contracts**.
|
|
55
55
|
|
|
56
56
|
When editing `packages/ai-rules/presets/**`, follow **`preset-pr-checklist.mdc`** (+ twin-sync / layering / token-budget / leakage meta-rules).
|
|
57
57
|
|
|
@@ -79,9 +79,21 @@ flowchart TD
|
|
|
79
79
|
|
|
80
80
|
**Source of truth for order:** `pipeline.json` → `steps[]`. Never hardcode analyst → dev → review → QA when `pipeline.json` exists.
|
|
81
81
|
|
|
82
|
+
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.
|
|
83
|
+
|
|
82
84
|
## Metrics lifecycle
|
|
83
85
|
|
|
84
|
-
Use the local `metrics.json` ledger described by **agent-task-metrics**:
|
|
86
|
+
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`.
|
|
87
|
+
|
|
88
|
+
## Artifact context index and receipts
|
|
89
|
+
|
|
90
|
+
Each slug has one `artifact-manifest.json`: `.cursor/team/tasks/<slug>/` or `.claude/team/tasks/<slug>/`. Schema SoT: **agent-artifact-contracts**.
|
|
91
|
+
|
|
92
|
+
- 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.
|
|
93
|
+
- Markdown remains detailed evidence; the manifest records producer, status, authoritative paths, timestamps, outcome, and evidence paths.
|
|
94
|
+
- Agents upsert only entries they own and preserve foreign entries. Parallel agents use distinct IDs; the parent/orchestrator serializes manifest merges.
|
|
95
|
+
- 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.
|
|
96
|
+
- Missing/stale manifests are reported as a handoff gap; fall back to `brief.md`, `decomposition.md`, and `pipeline.json` rather than inventing authority.
|
|
85
97
|
|
|
86
98
|
## status.json (pipeline mode)
|
|
87
99
|
|
|
@@ -93,10 +105,23 @@ Use the local `metrics.json` ledger described by **agent-task-metrics**: initial
|
|
|
93
105
|
"currentAgent": "task-analyst",
|
|
94
106
|
"phase": "executing",
|
|
95
107
|
"state": "in_progress",
|
|
96
|
-
"awaitingHumanGate": false
|
|
108
|
+
"awaitingHumanGate": false,
|
|
109
|
+
"activeAttempts": {},
|
|
110
|
+
"lastCompletedAttempt": null,
|
|
111
|
+
"parallelCompleted": [],
|
|
112
|
+
"parallelOutcomes": {}
|
|
97
113
|
}
|
|
98
114
|
```
|
|
99
115
|
|
|
116
|
+
| Field | Meaning |
|
|
117
|
+
|-------|---------|
|
|
118
|
+
| `activeAttempts` | Transient timing bridge keyed by `attemptId` |
|
|
119
|
+
| `lastCompletedAttempt` | Diagnostic pointer to the latest finished attempt |
|
|
120
|
+
| `parallelCompleted` | Agents that have stopped in the current parallel step |
|
|
121
|
+
| `parallelOutcomes` | Per-agent `{ outcome, attemptId, confidence }` for the current parallel join; cleared when the step advances or retries |
|
|
122
|
+
|
|
123
|
+
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.
|
|
124
|
+
|
|
100
125
|
| state | Meaning |
|
|
101
126
|
|-------|---------|
|
|
102
127
|
| `in_progress` | Current step running |
|
|
@@ -120,7 +145,7 @@ Use the local `metrics.json` ledger described by **agent-task-metrics**: initial
|
|
|
120
145
|
2. Read `pipeline.json` before every subagent invocation.
|
|
121
146
|
3. One role per Task call — **except** parallel steps: multiple Task calls in one message.
|
|
122
147
|
4. **Never** skip `humanGates` without `/task-continue` or explicit user approval.
|
|
123
|
-
5. Persist handoffs to disk
|
|
148
|
+
5. Persist handoffs to disk and update the producer-owned terminal receipt in `artifact-manifest.json`; Markdown remains detailed evidence.
|
|
124
149
|
6. On `changes_requested` from reviewers: hook sets `retryAfterFix` → developer → same reviewer.
|
|
125
150
|
7. On `validation_failed` from `build-verifier`: hook sets `retryAfterFix: build-verifier` → developer → build-verifier.
|
|
126
151
|
8. When all steps complete, suggest `/technical-retro <slug>`.
|
|
@@ -145,7 +170,7 @@ Use Task tool or `/agent-name`. Pass: slug, artifact paths, step `scope` if set.
|
|
|
145
170
|
- **`strong`** → **never** pass `fast`; rely on agent pin (`claude-opus-4-8[effort=high]`) or parent Max model. If Task allows an explicit model ID from the allowed list, prefer the strong agent’s frontmatter ID.
|
|
146
171
|
3. Parallel steps: apply the step-level `model` to **each** agent in the array (or omit → each agent’s own frontmatter).
|
|
147
172
|
|
|
148
|
-
After each agent completes,
|
|
173
|
+
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`.
|
|
149
174
|
|
|
150
175
|
## Legacy mode
|
|
151
176
|
|
|
@@ -70,10 +70,11 @@
|
|
|
70
70
|
"phase": "executing",
|
|
71
71
|
"state": "in_progress",
|
|
72
72
|
"awaitingHumanGate": false,
|
|
73
|
-
"parallelCompleted": [],
|
|
74
73
|
"retryAfterFix": null,
|
|
75
74
|
"activeAttempts": {},
|
|
76
75
|
"lastCompletedAttempt": null,
|
|
76
|
+
"parallelCompleted": [],
|
|
77
|
+
"parallelOutcomes": {},
|
|
77
78
|
"updatedAt": "2026-05-29T12:00:00.000Z"
|
|
78
79
|
}
|
|
79
80
|
```
|
|
@@ -105,17 +106,28 @@ Legacy: `/feature-start`, `/feature-continue`.
|
|
|
105
106
|
|
|
106
107
|
## Creating task dirs
|
|
107
108
|
|
|
108
|
-
Bootstrap `<slug>/` by **writing** `pipeline.json` / `status.json`
|
|
109
|
+
Bootstrap `<slug>/` by **writing** `pipeline.json` (Write tool). Runtime/hook owns `status.json` lifecycle; parent may only create a minimal bootstrap if missing. Do **not** use Shell `mkdir -p` — Cursor’s default sandbox returns `Operation not permitted` (especially for repos on macOS Desktop/Documents).
|
|
109
110
|
|
|
110
111
|
## Git
|
|
111
112
|
|
|
112
113
|
Commit task artifacts if you want them to survive `ai-rules clean cursor`. Or copy finished tasks to `docs/agent-workflow/tasks/<slug>/`.
|
|
113
114
|
|
|
115
|
+
## Artifact manifest and receipts
|
|
116
|
+
|
|
117
|
+
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.
|
|
118
|
+
|
|
119
|
+
Producer/consumer rules:
|
|
120
|
+
|
|
121
|
+
- 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.
|
|
122
|
+
- Producers upsert only their stable entry IDs, preserve foreign entries, and finish with `completed`, `blocked`, `changes_requested`, or `validation_failed` receipt evidence.
|
|
123
|
+
- 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.
|
|
124
|
+
- 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.
|
|
125
|
+
|
|
114
126
|
## Local metrics ledger
|
|
115
127
|
|
|
116
|
-
`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. Policy SoT: `presets/_shared/core/agent-team/agent-task-metrics.md`. The
|
|
128
|
+
`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. Policy SoT: `presets/_shared/core/agent-team/agent-task-metrics.md`. 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.
|
|
117
129
|
|
|
118
|
-
`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`.
|
|
130
|
+
`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`.
|
|
119
131
|
|
|
120
132
|
After a human gate, `/task-continue` closes wait via the installed hook (do not hand-edit `pipelineIndex` first):
|
|
121
133
|
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"slug": "order-history-date-filter",
|
|
4
|
+
"updatedAt": "2026-07-31T12:20:00.000Z",
|
|
5
|
+
"artifacts": [
|
|
6
|
+
{
|
|
7
|
+
"id": "routing",
|
|
8
|
+
"producer": "task-router",
|
|
9
|
+
"status": "completed",
|
|
10
|
+
"authoritativePaths": ["pipeline.json"],
|
|
11
|
+
"acceptanceCriteriaIds": [],
|
|
12
|
+
"taskIds": [],
|
|
13
|
+
"timestamps": {
|
|
14
|
+
"createdAt": "2026-07-31T12:00:00.000Z",
|
|
15
|
+
"updatedAt": "2026-07-31T12:00:00.000Z",
|
|
16
|
+
"completedAt": "2026-07-31T12:00:00.000Z"
|
|
17
|
+
},
|
|
18
|
+
"receipt": {
|
|
19
|
+
"outcome": "completed",
|
|
20
|
+
"summary": "The feature pipeline and routing evidence are authoritative.",
|
|
21
|
+
"evidencePaths": ["pipeline.json"]
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"id": "analysis",
|
|
26
|
+
"producer": "task-analyst",
|
|
27
|
+
"status": "completed",
|
|
28
|
+
"authoritativePaths": ["brief.md", "decomposition.md"],
|
|
29
|
+
"acceptanceCriteriaIds": ["AC-1", "AC-2"],
|
|
30
|
+
"taskIds": ["T1", "T2"],
|
|
31
|
+
"timestamps": {
|
|
32
|
+
"createdAt": "2026-07-31T12:01:00.000Z",
|
|
33
|
+
"updatedAt": "2026-07-31T12:05:00.000Z",
|
|
34
|
+
"completedAt": "2026-07-31T12:05:00.000Z"
|
|
35
|
+
},
|
|
36
|
+
"receipt": {
|
|
37
|
+
"outcome": "completed",
|
|
38
|
+
"summary": "The acceptance criteria and implementation tasks are ready.",
|
|
39
|
+
"evidencePaths": ["brief.md", "decomposition.md"]
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"id": "implementation",
|
|
44
|
+
"producer": "feature-developer",
|
|
45
|
+
"status": "completed",
|
|
46
|
+
"authoritativePaths": ["implementation.md"],
|
|
47
|
+
"acceptanceCriteriaIds": ["AC-1", "AC-2"],
|
|
48
|
+
"taskIds": ["T1", "T2"],
|
|
49
|
+
"timestamps": {
|
|
50
|
+
"createdAt": "2026-07-31T12:06:00.000Z",
|
|
51
|
+
"updatedAt": "2026-07-31T12:20:00.000Z",
|
|
52
|
+
"completedAt": "2026-07-31T12:20:00.000Z"
|
|
53
|
+
},
|
|
54
|
+
"receipt": {
|
|
55
|
+
"outcome": "completed",
|
|
56
|
+
"summary": "Implementation and focused unit coverage are complete.",
|
|
57
|
+
"evidencePaths": ["implementation.md"]
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
]
|
|
61
|
+
}
|
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
"slug": "example-bugfix-standard",
|
|
3
3
|
"intent": "bugfix",
|
|
4
4
|
"profile": "standard",
|
|
5
|
+
"routingDecision": {
|
|
6
|
+
"complexity": "moderate",
|
|
7
|
+
"riskFlags": ["behavior-regression"],
|
|
8
|
+
"routingReasons": [
|
|
9
|
+
"The regression needs root-cause evidence before deciding whether a separate fix pass is necessary.",
|
|
10
|
+
"The bounded behavior change still requires validation and independent review."
|
|
11
|
+
],
|
|
12
|
+
"estimatedWorkPackages": 1,
|
|
13
|
+
"openDecisionCount": 0
|
|
14
|
+
},
|
|
5
15
|
"summary": "Regression fix with review",
|
|
6
16
|
"steps": [
|
|
7
17
|
{ "agent": "debugger", "label": "Root cause" },
|
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
"slug": "example-feature-full",
|
|
3
3
|
"intent": "feature",
|
|
4
4
|
"profile": "full",
|
|
5
|
+
"routingDecision": {
|
|
6
|
+
"complexity": "high",
|
|
7
|
+
"riskFlags": ["cross-layer", "public-contract", "broad-test-surface"],
|
|
8
|
+
"routingReasons": [
|
|
9
|
+
"The feature spans UI, state, and API boundaries, so architecture and implementation checkpoints are required.",
|
|
10
|
+
"The public contract and e2e acceptance surface require independent validation and review."
|
|
11
|
+
],
|
|
12
|
+
"estimatedWorkPackages": 2,
|
|
13
|
+
"openDecisionCount": 1
|
|
14
|
+
},
|
|
5
15
|
"summary": "Multi-layer feature with e2e coverage",
|
|
6
16
|
"steps": [
|
|
7
17
|
{ "agent": "task-analyst", "label": "Clarify and decompose" },
|
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
"slug": "example-feature-light",
|
|
3
3
|
"intent": "feature",
|
|
4
4
|
"profile": "light",
|
|
5
|
+
"routingDecision": {
|
|
6
|
+
"complexity": "low",
|
|
7
|
+
"riskFlags": [],
|
|
8
|
+
"routingReasons": [
|
|
9
|
+
"The request has explicit acceptance criteria and a reversible single-file scope.",
|
|
10
|
+
"No public contract, cross-layer coordination, or open decision requires analysis or architecture."
|
|
11
|
+
],
|
|
12
|
+
"estimatedWorkPackages": 1,
|
|
13
|
+
"openDecisionCount": 0
|
|
14
|
+
},
|
|
5
15
|
"summary": "Trivial single-file change with explicit AC",
|
|
6
16
|
"steps": [
|
|
7
17
|
{ "agent": "feature-developer", "label": "Implement", "model": "cheap" },
|