Skip to content

feat(decisioning): boot-time capabilities response shape validation (#422)#446

Merged
bokelley merged 1 commit intomainfrom
bokelley/dx-422-capabilities-validator
May 3, 2026
Merged

feat(decisioning): boot-time capabilities response shape validation (#422)#446
bokelley merged 1 commit intomainfrom
bokelley/dx-422-capabilities-validator

Conversation

@bokelley
Copy link
Copy Markdown
Contributor

@bokelley bokelley commented May 3, 2026

Summary

  • New validate_capabilities_response_shape(handler) — invokes handler.get_adcp_capabilities() with a synthetic request, validates the returned dict against the bundled protocol/get-adcp-capabilities-response.json schema, and asserts spec invariants the schema can't fully express on its own (account.supported_billing non-empty whenever media_buy is claimed; supported_protocols non-empty).
  • Wired into create_adcp_server_from_platform after validate_platform + the F12 webhook gate, so misconfiguration surfaces as a structured AdcpError("INVALID_REQUEST", recovery="terminal") before the server takes traffic — mirrors the existing fail-fast posture.
  • Catches the v3 reference seller's pre-feat(v3-ref-seller): enable server validation + declare account.supported_billing #402 supported_billing omission at boot. Two pre-existing fixtures (tests/test_decisioning_serve.py::_BarePlatform, _SalesPlatformWithRequiredMethods) and one example (examples/hello_seller_audience.py) were updated to declare supported_billing — the cases they exercised would have shipped a non-conformant capabilities envelope on the wire (audience-sync maps to media_buy; the projection falls through to media_buy whenever supported_protocols would otherwise be empty).

Test plan

  • pytest tests/test_capabilities_response_shape_validation.py -v — 7 tests cover: conformant pass, missing billing fail, empty protocols fail, schema-violation fail, server-boot rejects non-conformant platform, server-boot accepts conformant platform, v3-ref-seller-shape regression guard.
  • pytest tests/ -k capabilities — 99 passed, 4 skipped.
  • Full suite: 3211 passed, 21 skipped, 1 xfailed (no new failures).
  • ruff check src/ — clean.
  • mypy src/adcp/ (project venv, py3.10 target) — 748 source files, no issues.

The local uv run mypy pre-commit hook surfaces 96 pre-existing errors in webhooks.py/client.py/protocols/a2a.py (Python 3.13 + protobuf typing drift unrelated to this change; identical error count on main). CI's mypy src/adcp/ step uses the project's pinned config and is unaffected; commit was made with --no-verify and the rationale is in the commit body.

🤖 Generated with Claude Code

…422)

Adds validate_capabilities_response_shape — a server-boot fail-fast
that exercises handler.get_adcp_capabilities() against the bundled
get-adcp-capabilities-response.json schema and the spec invariants
the schema can't fully express on its own (account.supported_billing
required + non-empty whenever media_buy is claimed; supported_protocols
non-empty). Wired into create_adcp_server_from_platform after
validate_platform + the F12 webhook gate so misconfiguration surfaces
as a structured AdcpError before the server takes traffic.

Catches the v3 reference seller's pre-#402 ``supported_billing``
omission at boot. Existing test fixtures and the hello_seller_audience
example were updated to declare ``supported_billing`` — the cases they
previously exercised would have shipped a non-conformant capabilities
envelope on the wire (audience-sync maps to media_buy; the projection
falls through to media_buy whenever supported_protocols would be empty).

Note: --no-verify used because the local pre-commit mypy hook
(``uv run mypy``) provisions a Python 3.13 venv that surfaces 96
pre-existing errors in webhooks/client/protocols modules unrelated
to this change. The same errors exist on main; the project's pyproject
pins mypy to ``python_version = "3.10"`` and CI runs ``mypy src/adcp/``
directly (which passes cleanly against this change).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@bokelley bokelley merged commit 127f164 into main May 3, 2026
11 of 13 checks passed
bokelley added a commit that referenced this pull request May 3, 2026
Bring the branch up to date with main (PRs #441, #444, #445, #446, #447).
Conflicts resolved:

* ``.github/workflows/ci.yml`` — keep main's restructured v3 storyboard
  job (translator-pattern upstream mock + readiness loop) and apply
  the poll-fix pattern (``||`` outside command substitution) to both
  the upstream-mock readiness loop and main's new seller-readiness
  loop.
* ``src/adcp/server/serve.py`` — combine main's strict-by-default
  ``validation=DEFAULT_VALIDATION`` flip with the three transport-
  security kwargs added on this branch (``allowed_hosts``,
  ``allowed_origins``, ``enable_dns_rebinding_protection``).

Auto-merged: ``examples/v3_reference_seller/src/app.py`` (translator
rewrite + this branch's webhook/dispose/DNS-rebinding kwargs),
``examples/v3_reference_seller/tests/test_smoke.py`` (translator
rewrite + this branch's port-stripping regression test),
``src/adcp/server/mcp_tools.py`` (lazy schema gen + this branch's
``setdefault("type", "object")`` for union outputSchemas),
``examples/seller_agent.py`` (channels filter intact).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant