adcp/server/test_controller.py's sandbox-authority gate denies live-mode callers with PERMISSION_DENIED:
# adcp/server/test_controller.py — _apply_sandbox_gate
return _controller_error(
"PERMISSION_DENIED",
...
The AdCP 3.1.1 conformance storyboard that grades this exact path requires FORBIDDEN:
# dist/compliance/3.1.1/universal/comply-controller-mode-gate.yaml
# phase live_account_denial → step deny_live_caller
validations:
- check: field_value
path: 'error'
allowed_values:
- 'FORBIDDEN'
description: 'Error code is FORBIDDEN — seller detected live-mode account and denied dispatch before scenario execution'
The storyboard's own narrative is explicit that this is the graded behavior: "the seller MUST refuse with error: FORBIDDEN before dispatching any scenario," and it describes itself as "the keystone for the (Sandbox) AAO Verified tier."
The prose spec agrees on the code and scopes it precisely — dist/docs/3.1.1/building/by-layer/L3/comply-test-controller.mdx, §Sandbox gating: "FORBIDDEN is reserved for the in-sandbox case where the caller is authorized to call the controller but params reference a non-sandbox account."
Effect: a seller wiring dispatch(...) with an account_resolver and returning its result verbatim fails comply_controller_mode_gate, because the graded field is an exact-match allowlist of one value.
Note the adjacent semantic distinction, in case it affects the fix: the same spec section requires that a live-mode caller probing the tool by name receive the transport's standard unknown-tool error (indistinguishable from a seller that never implemented the controller) rather than any authorization error — FORBIDDEN is only for the in-sandbox caller referencing a non-sandbox account. If the current gate is intended to cover the probing case rather than the referenced-account case, then the divergence may be one of layering rather than a simple string.
Verified against adcp==6.6.0; a2a-sdk==1.0.1.
Related: #1007, #1008, #1009, #1010 (other A2A/server seam reports from the same audit).
adcp/server/test_controller.py's sandbox-authority gate denies live-mode callers withPERMISSION_DENIED:The AdCP 3.1.1 conformance storyboard that grades this exact path requires
FORBIDDEN:The storyboard's own narrative is explicit that this is the graded behavior: "the seller MUST refuse with
error: FORBIDDENbefore dispatching any scenario," and it describes itself as "the keystone for the (Sandbox) AAO Verified tier."The prose spec agrees on the code and scopes it precisely —
dist/docs/3.1.1/building/by-layer/L3/comply-test-controller.mdx, §Sandbox gating: "FORBIDDENis reserved for the in-sandbox case where the caller is authorized to call the controller butparamsreference a non-sandbox account."Effect: a seller wiring
dispatch(...)with anaccount_resolverand returning its result verbatim failscomply_controller_mode_gate, because the graded field is an exact-match allowlist of one value.Note the adjacent semantic distinction, in case it affects the fix: the same spec section requires that a live-mode caller probing the tool by name receive the transport's standard unknown-tool error (indistinguishable from a seller that never implemented the controller) rather than any authorization error —
FORBIDDENis only for the in-sandbox caller referencing a non-sandbox account. If the current gate is intended to cover the probing case rather than the referenced-account case, then the divergence may be one of layering rather than a simple string.Verified against
adcp==6.6.0;a2a-sdk==1.0.1.Related: #1007, #1008, #1009, #1010 (other A2A/server seam reports from the same audit).