Skip to content

feat(adcp): force_create_media_buy_arm + force_task_completion controller scenarios#98

Draft
bokelley wants to merge 2 commits intomainfrom
claude/issue-97-force-create-media-buy-arm-and-task-completion
Draft

feat(adcp): force_create_media_buy_arm + force_task_completion controller scenarios#98
bokelley wants to merge 2 commits intomainfrom
claude/issue-97-force-create-media-buy-arm-and-task-completion

Conversation

@bokelley
Copy link
Copy Markdown
Contributor

Closes #97

Adds force_create_media_buy_arm (adcp#3104) and force_task_completion (adcp#3138) to adcp/testcontroller.go, giving the Go reference seller storyboard parity with the training-agent implementations at adcp#3115 and adcp#3194.

What changed

force_create_media_buy_arm — registers a single-shot per-principal directive that drives the next create_media_buy call into submitted or input-required arm. Validates arm enum using TaskStatusSubmitted/TaskStatusInputRequired constants, task_id requirement (submitted arm only), and byte-length caps (task_id ≤128 B, message ≤2000 B). Returns ForcedDirectiveSuccess.

force_task_completion — resolves a submitted task to completed. Validates task_id (≤128 B), result is non-empty object, encoded size ≤256 KB. Re-marshals from map[string]any to json.RawMessage before the size check for stable opaque payload passing. Returns StateTransitionSuccess from types_gen.go (includes message, context, ext fields the storyboard harness validates against).

Sandbox bool guardRegisterTestController panics at startup if store.Sandbox is false, providing runtime enforcement of the existing "MUST NOT register in production" contract. Mirrors the buildCapabilities startup-panic pattern.

ForcedDirectiveSuccess type — hand-written and registered in both KNOWN_TYPES (generate.py) and EXEMPT (lint.py). Comment notes the pending upstream schema in adcp#3104 so the bundle-bump owner knows to check for drift when the version pin moves past 3.0.0.

Both scenarios advertised in list_scenarios when store functions are wired.

Cross-account isolation, single-shot consumed-and-cleared semantics, and idempotency are seller responsibilities (consistent with every other force_* scenario in the existing dispatch layer).

Caveats: buyer-side tasks/get polling round-trip is deferred pending adcp-client#994. This PR ships the controller-side primitive only; storyboard runners mark the buyer-side polling scenario not_applicable until that lands.

What was tested

  • go build ./...
  • go vet ./...
  • cd adcp && go test ./... ✓ — all packages including 10 new tests:
    • TestForceCreateMediaBuyArm_Submitted
    • TestForceCreateMediaBuyArm_InputRequired
    • TestForceCreateMediaBuyArm_InvalidParams (5 sub-cases: missing arm, invalid arm, submitted without task_id, task_id too long, message too long)
    • TestForceTaskCompletion_Valid
    • TestForceTaskCompletion_InvalidParams (5 sub-cases: missing task_id, task_id too long, missing result, empty result, result not object)
    • TestForceTaskCompletion_NotFound
    • TestForceTaskCompletion_InvalidTransition
    • TestRegisterTestController_SandboxGuard
    • TestListScenarios_IncludesNewScenarios

Pre-PR review

  • code-reviewer: approved — no blockers; bytes-vs-chars clarification and panic guard test addressed in fixup commit
  • ad-tech-protocol-expert: approved — wire format correct (StateTransitionSuccess for force_task_completion), arm enum values match A2A/AdCP vocab, deferred buyer-side polling does not create a conformance gap

Triage-managed PR. This bot does not currently iterate on
review comments or PR conversation threads (only on the source
issue). To unblock:

  • Push fixup commits directly: gh pr checkout <num>
    fix → push.
  • Or re-trigger: comment /triage execute on the source
    issue.

See adcp#3121
for context.

Session: https://claude.ai/code/session_01Js7WwyJontwvwN6G5Um1Z3


Generated by Claude Code

claude added 2 commits April 25, 2026 20:34
…ller scenarios

Adds two new comply_test_controller scenarios for storyboard parity with the
AdCP spec additions adcp#3104 and adcp#3138, mirroring the training-agent
implementations at adcp#3115 and adcp#3194.

- force_create_media_buy_arm: registers a single-shot per-principal directive
  that drives the next create_media_buy call into submitted or input-required
  arm; validates arm enum, task_id requirement (submitted arm), and length caps.
- force_task_completion: resolves a submitted task to completed; validates
  task_id ≤128 chars, result is non-empty object, encoded size ≤256 KB.
  Uses json.RawMessage for stable opaque payload passing to store implementations.
- Both scenarios advertised in list_scenarios when store functions are wired.
- ForcedDirectiveSuccess type added; registered in KNOWN_TYPES and EXEMPT to
  keep generate.py / lint.py consistent.
- Sandbox bool guard added to TestControllerStore: RegisterTestController panics
  if store.Sandbox is false, providing startup-time enforcement of the existing
  MUST NOT register in production contract (mirrors buildCapabilities pattern).
- Nine new tests covering valid params, INVALID_PARAMS branches, NOT_FOUND
  cross-account, INVALID_TRANSITION diverging replay, and list_scenarios
  advertisement.

Closes #97

https://claude.ai/code/session_01Js7WwyJontwvwN6G5Um1Z3
- Error strings now say "≤128 bytes" / "≤2000 bytes" (len() is byte-based)
- TestRegisterTestController_SandboxGuard exercises the new panic contract
- ForcedDirectiveSuccess KNOWN_TYPES entry notes pending adcp#3104 upstream
  schema so bundle-bump owner knows to revisit

https://claude.ai/code/session_01Js7WwyJontwvwN6G5Um1Z3
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement force_create_media_buy_arm + force_task_completion controller scenarios for AdCP storyboard parity

2 participants