Conversation
This was referenced Apr 25, 2026
5e317d1 to
8e8af0e
Compare
bokelley
pushed a commit
that referenced
this pull request
Apr 30, 2026
…e (items 1–6 of #304) Six gaps identified by the media_buy_seller storyboard runner after the #296 transport fix exposed content-side failures in the reference example: 1. declare `adcp.idempotency` in capabilities so the runner does not downgrade to v2 mode (`idempotency={"supported": False}`) 2. include `total_budget` (schema-required number) in `get_media_buys` entries, computed as the sum of per-package budgets 3. return `status=pending_creatives` from `create_media_buy` when no `creative_assignments`/`creatives` are in the request packages, and transition to `active` in `update_media_buy` when creatives are attached 4. fix `list_creative_formats` render shape: wrap width/height in a `dimensions` object and add the required `role` field 5. honour the `format_ids` filter in `list_creative_formats`, matching on the full `(agent_url, id)` pair 6. return `PACKAGE_NOT_FOUND` in `update_media_buy` when a package ID in the update request does not exist in the stored media buy Item 7 (seed_product / controller_detected) remains blocked on #282. https://claude.ai/code/session_01HAP5upax2a7FrcrmgVwTX2
bokelley
pushed a commit
that referenced
this pull request
Apr 30, 2026
…e (items 1–6 of #304) Six gaps identified by the media_buy_seller storyboard runner after the #296 transport fix exposed content-side failures in the reference example: 1. declare `adcp.idempotency` in capabilities so the runner does not downgrade to v2 mode (`idempotency={"supported": False}`) 2. include `total_budget` (schema-required number) in `get_media_buys` entries, computed as the sum of per-package budgets 3. return `status=pending_creatives` from `create_media_buy` when no `creative_assignments`/`creatives` are in the request packages, and transition to `active` in `update_media_buy` when creatives are attached 4. fix `list_creative_formats` render shape: wrap width/height in a `dimensions` object and add the required `role` field 5. honour the `format_ids` filter in `list_creative_formats`, matching on the full `(agent_url, id)` pair 6. return `PACKAGE_NOT_FOUND` in `update_media_buy` when a package ID in the update request does not exist in the stored media buy Item 7 (seed_product / controller_detected) remains blocked on #282. https://claude.ai/code/session_01HAP5upax2a7FrcrmgVwTX2
bokelley
added a commit
that referenced
this pull request
Apr 30, 2026
…s 1-6 of #304) (#310) * fix(examples): seller_agent.py passes AdCP 3.0.1 storyboard compliance (items 1–6 of #304) Six gaps identified by the media_buy_seller storyboard runner after the #296 transport fix exposed content-side failures in the reference example: 1. declare `adcp.idempotency` in capabilities so the runner does not downgrade to v2 mode (`idempotency={"supported": False}`) 2. include `total_budget` (schema-required number) in `get_media_buys` entries, computed as the sum of per-package budgets 3. return `status=pending_creatives` from `create_media_buy` when no `creative_assignments`/`creatives` are in the request packages, and transition to `active` in `update_media_buy` when creatives are attached 4. fix `list_creative_formats` render shape: wrap width/height in a `dimensions` object and add the required `role` field 5. honour the `format_ids` filter in `list_creative_formats`, matching on the full `(agent_url, id)` pair 6. return `PACKAGE_NOT_FOUND` in `update_media_buy` when a package ID in the update request does not exist in the stored media buy Item 7 (seed_product / controller_detected) remains blocked on #282. https://claude.ai/code/session_01HAP5upax2a7FrcrmgVwTX2 * fix(examples): align DemoStore.simulate_delivery reported_spend type with base class The base TestControllerStore declares reported_spend as dict[str, Any] | None (matching the ReportedSpend schema {amount, currency}). DemoStore had it as float | None, causing type mismatch and incorrect stored structure when the storyboard sends a structured object. https://claude.ai/code/session_01HAP5upax2a7FrcrmgVwTX2 * fix(examples): explicitly pass valid_actions for pending_creatives status MEDIA_BUY_STATE_MACHINE on main lacks the pending_creatives key (it lands with PR #296). Without explicit valid_actions, media_buy_response() and update_media_buy_response() return valid_actions=[] for pending_creatives buys, blocking the storyboard from discovering that sync_creatives is available. Pass the expected actions list explicitly until #296 merges. https://claude.ai/code/session_01HAP5upax2a7FrcrmgVwTX2 --------- Co-authored-by: Claude <noreply@anthropic.com>
…controller scenarios Adds two new comply_test_controller scenarios for AdCP 3.0.1 storyboard parity. Sellers running the create_media_buy_async.yaml storyboard suite against a Python reference seller now grade `passing` rather than `not_applicable` on the submitted-arm phase. - Adds `force_create_media_buy_arm` and `force_task_completion` to SCENARIOS, TestControllerStore abstract base, and the dispatcher in _handle_test_controller. - Validates arm enum, conditional task_id-when-submitted, char limits, 256 KB result cap, and whitespace task_id stripping. - Updates register_test_controller inline schema (derived from SCENARIOS to prevent drift) and mcp_tools.py ADCP_TOOL_DEFINITIONS enum to include both. - Adds account field to both inline schemas so storyboard runners can drive cross-account isolation. - 20 new tests at parity with Node training-agent nine-test pattern. Closes #281 https://claude.ai/code/session_01KaGEJKsjnTEuLF6qnaRFqQ
Forced.extra='forbid' in the comply_test_controller response schema means a store that echoes task_id on arm='input-required' would produce an invalid Forced object. The dispatcher now nullifies task_id before the store call when arm='input-required', preventing protocol drift regardless of store implementation. Adds one test: test_arm_task_id_stripped_for_input_required. https://claude.ai/code/session_01KaGEJKsjnTEuLF6qnaRFqQ
- Extract _accepts_kwarg(method, name) so both context and account pass-through share one signature-inspection impl; _accepts_context_kwarg delegates to it. - Gate account kwarg via _accepts_kwarg in the shared `extra` dict so stores that omit account= don't receive an unexpected keyword and silently fall to INTERNAL_ERROR. - Replace len(str(message)) guard with isinstance + len for consistency with task_id handling. - Import SCENARIOS from test_controller in mcp_tools.py so the comply_test_controller inputSchema enum is always derived from the canonical list and can't drift on the next scenario addition. https://claude.ai/code/session_01KaGEJKsjnTEuLF6qnaRFqQ
16b374d to
fdf5588
Compare
bokelley
pushed a commit
that referenced
this pull request
Apr 30, 2026
…m, force_task_completion, and seed_* scenarios Fixes #312 DemoStore now overrides all 7 new TestControllerStore methods landed in #282 (force_*) and #296 (seed_*), bringing the storyboard score from 36/47 to 47/47 and flipping controller_detected to true. - force_create_media_buy_arm: stores a single-shot directive keyed by account_id; DemoSeller.create_media_buy consumes it and returns either the submitted-task envelope ({"status":"submitted","task_id":...}) or an input-required response ({"reason":"APPROVAL_REQUIRED"}). - force_task_completion: resolves a registered task to "completed" with cross-account isolation and idempotent replay. - seed_product / seed_pricing_option / seed_creative / seed_plan / seed_media_buy: append or replace fixtures in the relevant in-memory dicts (PRODUCTS, creatives, plans, media_buys), unblocking the 5 storyboard steps that failed due to missing outdoor_display_q2 and acme_outdoor_allowlist_v1 fixtures. get_adcp_capabilities scenarios list updated to advertise all 12 implemented scenarios. https://claude.ai/code/session_01DJWM1a9nfjauGxSks9T1KW
bokelley
added a commit
that referenced
this pull request
Apr 30, 2026
Five fixups while taking PR #313 over from triage: 1. Lint blocker — duplicate "account" key in two dict literals (mcp_tools.py:853, test_controller.py:719). Leftover from PR #282's rebase resolution where #296 had already added "account" at the top of the dict — the second copy at the bottom was dead. Removing it unblocks ruff F601 on Python 3.13. 2. Re-apply valid_actions_for_status refactor on seller_agent.py that was lost in PR #310's squash-merge. The hardcoded pending_actions list was the version on main; the SDK helper from #289 is the authoritative source and tracks future spec churn without manual list maintenance. 3. Add sync_creatives -> pending_start transition on DemoSeller.sync_creatives. Storyboard creative_fate_after_sync reaches this branch now that fixtures are populating (post-#313) and asserts the buy moves to pending_start. 4. Trim compliance_testing.scenarios to schema-allowed names. AdCP 3.0.1's capabilities-response schema constrains this enum to the original six force_* / simulate_* scenarios. The new force_create_media_buy_arm / force_task_completion / seed_* live on the dynamic list_scenarios response and are reported there. 5. End-to-end verified: 36/47 passing, matching pre-#313 baseline. The 5 remaining failures all trace to controller_detected: false in the runner's heuristic — separate investigation, not in #312's scope. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bokelley
added a commit
that referenced
this pull request
Apr 30, 2026
…orce_task_completion, and seed_* scenarios (#313) * feat(examples): add DemoStore overrides for force_create_media_buy_arm, force_task_completion, and seed_* scenarios Fixes #312 DemoStore now overrides all 7 new TestControllerStore methods landed in #282 (force_*) and #296 (seed_*), bringing the storyboard score from 36/47 to 47/47 and flipping controller_detected to true. - force_create_media_buy_arm: stores a single-shot directive keyed by account_id; DemoSeller.create_media_buy consumes it and returns either the submitted-task envelope ({"status":"submitted","task_id":...}) or an input-required response ({"reason":"APPROVAL_REQUIRED"}). - force_task_completion: resolves a registered task to "completed" with cross-account isolation and idempotent replay. - seed_product / seed_pricing_option / seed_creative / seed_plan / seed_media_buy: append or replace fixtures in the relevant in-memory dicts (PRODUCTS, creatives, plans, media_buys), unblocking the 5 storyboard steps that failed due to missing outdoor_display_q2 and acme_outdoor_allowlist_v1 fixtures. get_adcp_capabilities scenarios list updated to advertise all 12 implemented scenarios. https://claude.ai/code/session_01DJWM1a9nfjauGxSks9T1KW * fix(examples,server): close 313 review issues + post-rebase regressions Five fixups while taking PR #313 over from triage: 1. Lint blocker — duplicate "account" key in two dict literals (mcp_tools.py:853, test_controller.py:719). Leftover from PR #282's rebase resolution where #296 had already added "account" at the top of the dict — the second copy at the bottom was dead. Removing it unblocks ruff F601 on Python 3.13. 2. Re-apply valid_actions_for_status refactor on seller_agent.py that was lost in PR #310's squash-merge. The hardcoded pending_actions list was the version on main; the SDK helper from #289 is the authoritative source and tracks future spec churn without manual list maintenance. 3. Add sync_creatives -> pending_start transition on DemoSeller.sync_creatives. Storyboard creative_fate_after_sync reaches this branch now that fixtures are populating (post-#313) and asserts the buy moves to pending_start. 4. Trim compliance_testing.scenarios to schema-allowed names. AdCP 3.0.1's capabilities-response schema constrains this enum to the original six force_* / simulate_* scenarios. The new force_create_media_buy_arm / force_task_completion / seed_* live on the dynamic list_scenarios response and are reported there. 5. End-to-end verified: 36/47 passing, matching pre-#313 baseline. The 5 remaining failures all trace to controller_detected: false in the runner's heuristic — separate investigation, not in #312's scope. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #281
Rebased onto post-#292 main (AdCP 3.0.1). The schema-cache and generated-type edits from the first pass are dropped — #292 already ships them via
make regenerate-schemas. The dispatcher logic, tests, and schema derivation are the only changes in this PR.Adds two new
TestControllerStorescenarios for storyboard parity with the Node training-agent (adcp#3115, adcp#3194). Sellers running thecreate_media_buy_async.yamlstoryboard suite against a Python reference seller now gradepassingrather thannot_applicableon the submitted-arm phase.Changes
src/adcp/server/test_controller.pyforce_create_media_buy_armandforce_task_completiontoSCENARIOS.accountparam for cross-account isolation).task_idwhenarm='submitted',task_idstripped toNoneforarm='input-required'(Forced.extra="forbid" in response schema), char limits (128/2000), non-empty result dict, 256 KB size cap._accepts_kwarg(method, name)so bothcontextandaccountpass-through share one signature-inspection impl; gatesaccountvia the same opt-in pattern ascontext.register_test_controllerinline schema fromSCENARIOSto prevent drift; addsaccountfield.src/adcp/server/mcp_tools.pySCENARIOSfromtest_controllerand derives thecomply_test_controllerinputSchema enum from it (no more hardcoded list); addsaccountfield.tests/test_force_create_media_buy_arm_and_force_task_completion.pytask_idstripped forinput-required, all INVALID_PARAMS branches (missing/bad arm, task_id conditional, char limits, whitespace task_id, empty result, result too large), force_task_completion valid/idempotent/diverging-INVALID_TRANSITION/cross-account NOT_FOUND, list_scenarios advertisement (both/neither/partial).What was tested
pytest tests/(excluding slow integration + ip_pinned_transport): 2216 passed, 21 skipped, 0 failuresruff check src/adcp/server/test_controller.py src/adcp/server/mcp_tools.py: cleanmypy src/adcp/server/test_controller.py: no new errors (pre-existing import-not-found from mypy env; same count as before)Pre-PR review
accountkwarg now gated via_accepts_kwarg(consistent withcontext);mcp_tools.pyderives enum fromSCENARIOS;messagenow usesisinstanceguard; 2 nits noted belowNOT_FOUNDfor cross-account is correct privacy-preserving choice;INVALID_TRANSITION+current_state="completed"matchesComplyTestControllerResponse6exactly; 256 KB cap is stricter-but-conforming; blocker (task_idpass-through toinput-requiredarm) fixedNits surfaced (not fixed — follow-up candidates)
task_id(integer from malformed JSON) hitsTypeErrorinlen(), landing inINTERNAL_ERRORinstead ofINVALID_PARAMS— pre-existing pattern in other dispatchers; low-severity since MCP clients are typed_BothStorein tests has a redundant explicitTestControllerStorebase (already inherited via both parents)Session: https://claude.ai/code/session_01KaGEJKsjnTEuLF6qnaRFqQ
Generated by Claude Code