REQ-C-009: Multi-Step Commands Report completed/failed/skipped
Tier: Command Contract | Priority: P1
Source: §13 Partial Failure & Atomicity
Addresses: Severity: Critical / Token Spend: High / Time: High / Context: Medium
Description
Any command that operates on a batch of items MUST include a summary object in its response containing total, succeeded, and failed integer counts. For each individual item that failed, the response MUST include per-item error details within results[]. This MUST allow a caller to determine exactly which items succeeded and which failed, enabling safe retry of only the failed items.
Acceptance Criteria
- A batch command response includes
summary.total,summary.succeeded,summary.failed - Each item in
results[]includesok: true/falseand, when false, anerrorobject - The item-level
errorobject follows the standard error structure (code, message, retryable) - The exit code for a partial batch success is non-zero (not
0)
Schema
Types: response-envelope.md
Batch commands include a summary object and a results array in the response data.
{
"summary": {
"type": "object",
"required": ["total", "succeeded", "failed"],
"properties": {
"total": { "type": "integer", "description": "Total number of items in the batch" },
"succeeded": { "type": "integer", "description": "Items that completed successfully" },
"failed": { "type": "integer", "description": "Items that failed" }
},
"description": "Aggregate counts for the batch operation"
},
"results": {
"type": "array",
"items": {
"type": "object",
"required": ["id", "ok"],
"properties": {
"id": { "description": "Identifier of the item" },
"ok": { "type": "boolean", "description": "Whether this item succeeded" },
"error": { "description": "Error detail when ok is false; follows standard error structure" }
}
},
"description": "Per-item results enabling safe retry of only the failed items"
}
}
Wire Format
$ tool send-notifications --users 1,2,3,4,5
{
"ok": false,
"data": {
"partial": true,
"summary": { "total": 5, "succeeded": 3, "failed": 2 },
"results": [
{ "id": 1, "ok": true, "effect": "sent" },
{ "id": 2, "ok": true, "effect": "sent" },
{ "id": 3, "ok": false, "error": { "code": "INVALID_EMAIL", "message": "Invalid email address", "retryable": false } },
{ "id": 4, "ok": true, "effect": "sent" },
{ "id": 5, "ok": false, "error": { "code": "RATE_LIMITED", "message": "Rate limit exceeded", "retryable": true } }
]
},
"error": { "code": "PARTIAL_FAILURE", "message": "2 of 5 notifications failed" },
"warnings": [],
"meta": { "duration_ms": 312 }
}
Exit code: 2 (PARTIAL_FAILURE).
Example
A batch command iterates items, captures per-item errors, and always returns the structured summary even on partial success.
register command "send-notifications":
danger_level: mutating
exit_codes:
SUCCESS (0): description: "All notifications sent", retryable: false, side_effects: complete
PARTIAL_FAILURE(2): description: "Some notifications failed", retryable: false, side_effects: partial
ARG_ERROR (3): description: "Invalid --users value", retryable: true, side_effects: none
execute(args):
results = []
for user_id in args.users:
try:
send(user_id)
results.append(id=user_id, ok=True, effect="sent")
except Exception as e:
results.append(id=user_id, ok=False, error=classify(e))
failed = [r for r in results if not r.ok]
exit_code = PARTIAL_FAILURE if failed else SUCCESS
return response(exit_code, partial=bool(failed),
summary={total: len(results), succeeded: len(results)-len(failed), failed: len(failed)},
results=results)
Related
| Requirement | Tier | Relationship |
|---|---|---|
| REQ-C-008 | C | Composes: completed_steps/failed_step for sequential multi-step commands; summary/results for batch commands |
| REQ-C-013 | C | Enforces: per-item error objects follow the same structure as top-level error responses |
| REQ-F-001 | F | Provides: PARTIAL_FAILURE (2) is the correct exit code for partial batch success |
| REQ-F-004 | F | Wraps: summary and results are carried inside ResponseEnvelope.data |