Summary
When a comply() scenario step fails, ComplianceResult.tracks[].scenarios[].steps[].error ships as {} even though the underlying SDK transport has the full structured error (adcp_error: { code, message, recovery, field, details.validation_errors }).
Repro
Run any create_media_buy-touching storyboard against a 3.0-strict seller that rejects the runner's request. Compare:
SingleAgentClient.executeTask (returns the full error):
{
"status": "failed",
"error": "INVALID_REQUEST: create_media_buy failed: Invalid value for field 'packages.0.product_id': Field required",
"data": {
"adcp_error": {
"code": "INVALID_REQUEST",
"message": "create_media_buy failed: ...",
"field": "packages.0.product_id",
"details": { "validation_errors": [...] }
}
}
}
comply() ComplianceResult for the same call:
{
"step_id": "...",
"task": "create_media_buy",
"passed": false,
"error": {} ← detail dropped
}
The comply harness is collapsing/normalizing the result and stripping the meaningful payload.
Why it matters
The comply harness output is the canonical input for AAO heartbeat grading. With error: {}, an owner staring at a failing storyboard from the dashboard cannot see what their agent actually returned wrong on the wire. Diagnosing the failure requires re-running with a different tool. This is the main reason it took several probes to discover #1676 and #1677 even though both errors were sitting on the wire all along.
Fix
Preserve the underlying transport error on every step entry. Minimum:
step.error = transportResult.error ?? {};
// or, more usefully, an object with at minimum:
step.error = {
message: transportResult.error?.message,
code: transportResult.error?.code,
adcp_error: transportResult.data?.adcp_error,
};
Add a serialization test that pins this contract — step.error must be JSON-serializable and must carry the underlying error message when the step failed for a non-skip reason.
Cross-link
Same Wonderstruck probe as #1676–#1678. Findings doc in adcontextprotocol/adcp .context/wonderstruck-findings.md.
Summary
When a comply() scenario step fails,
ComplianceResult.tracks[].scenarios[].steps[].errorships as{}even though the underlying SDK transport has the full structured error (adcp_error: { code, message, recovery, field, details.validation_errors }).Repro
Run any
create_media_buy-touching storyboard against a 3.0-strict seller that rejects the runner's request. Compare:SingleAgentClient.executeTask(returns the full error):{ "status": "failed", "error": "INVALID_REQUEST: create_media_buy failed: Invalid value for field 'packages.0.product_id': Field required", "data": { "adcp_error": { "code": "INVALID_REQUEST", "message": "create_media_buy failed: ...", "field": "packages.0.product_id", "details": { "validation_errors": [...] } } } }comply()ComplianceResult for the same call:{ "step_id": "...", "task": "create_media_buy", "passed": false, "error": {} ← detail dropped }The comply harness is collapsing/normalizing the result and stripping the meaningful payload.
Why it matters
The comply harness output is the canonical input for AAO heartbeat grading. With
error: {}, an owner staring at a failing storyboard from the dashboard cannot see what their agent actually returned wrong on the wire. Diagnosing the failure requires re-running with a different tool. This is the main reason it took several probes to discover #1676 and #1677 even though both errors were sitting on the wire all along.Fix
Preserve the underlying transport error on every step entry. Minimum:
Add a serialization test that pins this contract —
step.errormust be JSON-serializable and must carry the underlying error message when the step failed for a non-skip reason.Cross-link
Same Wonderstruck probe as #1676–#1678. Findings doc in adcontextprotocol/adcp
.context/wonderstruck-findings.md.