Context
After PRs #282, #296, #310, and #313, `examples/seller_agent.py`'s `DemoStore` overrides all 7 new `TestControllerStore` methods (force_create_media_buy_arm, force_task_completion, seed_product, seed_pricing_option, seed_creative, seed_plan, seed_media_buy) plus the original 5 (force_, simulate_). The dynamic `comply_test_controller(scenario="list_scenarios")` call returns all 12 implemented scenarios:
```json
{"success": true, "scenarios": [
"force_creative_status", "force_account_status", "force_media_buy_status",
"force_create_media_buy_arm", "force_task_completion",
"simulate_delivery", "simulate_budget_spend",
"seed_product", "seed_pricing_option", "seed_creative",
"seed_plan", "seed_media_buy"
]}
```
But the JS storyboard runner (`@adcp/client@latest`, currently 5.23) still reports `"controller_detected": false` and skips/fails the 5 fixture-dependent storyboards (`outdoor_display_q2`, `sports_preroll_q2` products; `acme_outdoor_allowlist_v1`, `acme_outdoor_no_match_v1` property lists; aggressive measurement_terms).
Hypothesis
The runner has a detection heuristic beyond `list_scenarios`. Possibilities:
-
Reads the static `compliance_testing.scenarios` field on `get_adcp_capabilities`. AdCP 3.0.1's response schema constrains this enum to the original 6 names — the new `force_create_media_buy_arm` / `force_task_completion` / `seed_*` cannot be advertised there without failing schema validation. If the runner requires them in this static field, we have a deadlock between schema and runner expectations.
-
Probes a specific scenario (e.g., `force_task_completion`) and checks for a particular response shape. If the response doesn't match, controller is graded "absent."
-
Requires a specific subset of scenarios (e.g., all `seed_*` to be present? `force_session_status`? — DemoStore doesn't override `force_session_status`, even though it's schema-allowed).
-
A2A-only detection path — the runner's compliance-fleet flag `controller_detected` may only flip `true` for A2A-protocol agents.
Verification path
Run `@adcp/client@latest adcp storyboard run http://127.0.0.1:3001/mcp media_buy_seller --json --allow-http` against `examples/seller_agent.py` on `main`. Inspect the runner's source (`@adcp/client/src/storyboard/...`) for the controller-detection logic. Report findings.
Impact
Until `controller_detected: true`, the storyboard CI job (#305 / merged via #309) cannot pass — it's stuck at 36/47 with the 5 fixture-dependent storyboards failing. Once detection works, those storyboards should pass via the `seed_*` overrides this team just landed.
Workaround
None on the SDK side. The Python SDK provides every scenario the AdCP 3.0.1 spec exposes; the gap is downstream in the JS runner's heuristic OR in the AdCP 3.0.1 capabilities-response schema (item 1 above).
References
Context
After PRs #282, #296, #310, and #313, `examples/seller_agent.py`'s `DemoStore` overrides all 7 new `TestControllerStore` methods (force_create_media_buy_arm, force_task_completion, seed_product, seed_pricing_option, seed_creative, seed_plan, seed_media_buy) plus the original 5 (force_, simulate_). The dynamic `comply_test_controller(scenario="list_scenarios")` call returns all 12 implemented scenarios:
```json
{"success": true, "scenarios": [
"force_creative_status", "force_account_status", "force_media_buy_status",
"force_create_media_buy_arm", "force_task_completion",
"simulate_delivery", "simulate_budget_spend",
"seed_product", "seed_pricing_option", "seed_creative",
"seed_plan", "seed_media_buy"
]}
```
But the JS storyboard runner (`@adcp/client@latest`, currently 5.23) still reports `"controller_detected": false` and skips/fails the 5 fixture-dependent storyboards (`outdoor_display_q2`, `sports_preroll_q2` products; `acme_outdoor_allowlist_v1`, `acme_outdoor_no_match_v1` property lists; aggressive measurement_terms).
Hypothesis
The runner has a detection heuristic beyond `list_scenarios`. Possibilities:
Reads the static `compliance_testing.scenarios` field on `get_adcp_capabilities`. AdCP 3.0.1's response schema constrains this enum to the original 6 names — the new `force_create_media_buy_arm` / `force_task_completion` / `seed_*` cannot be advertised there without failing schema validation. If the runner requires them in this static field, we have a deadlock between schema and runner expectations.
Probes a specific scenario (e.g., `force_task_completion`) and checks for a particular response shape. If the response doesn't match, controller is graded "absent."
Requires a specific subset of scenarios (e.g., all `seed_*` to be present? `force_session_status`? — DemoStore doesn't override `force_session_status`, even though it's schema-allowed).
A2A-only detection path — the runner's compliance-fleet flag `controller_detected` may only flip `true` for A2A-protocol agents.
Verification path
Run `@adcp/client@latest adcp storyboard run http://127.0.0.1:3001/mcp media_buy_seller --json --allow-http` against `examples/seller_agent.py` on `main`. Inspect the runner's source (`@adcp/client/src/storyboard/...`) for the controller-detection logic. Report findings.
Impact
Until `controller_detected: true`, the storyboard CI job (#305 / merged via #309) cannot pass — it's stuck at 36/47 with the 5 fixture-dependent storyboards failing. Once detection works, those storyboards should pass via the `seed_*` overrides this team just landed.
Workaround
None on the SDK side. The Python SDK provides every scenario the AdCP 3.0.1 spec exposes; the gap is downstream in the JS runner's heuristic OR in the AdCP 3.0.1 capabilities-response schema (item 1 above).
References