Skip to content

feat(server): default validation=strict in serve() — wire-conformance by default#439

Merged
bokelley merged 2 commits intomainfrom
bokelley/dx-8-validation-default
May 3, 2026
Merged

feat(server): default validation=strict in serve() — wire-conformance by default#439
bokelley merged 2 commits intomainfrom
bokelley/dx-8-validation-default

Conversation

@bokelley
Copy link
Copy Markdown
Contributor

@bokelley bokelley commented May 3, 2026

Summary

  • Promote strict-by-default schema validation across serve(), create_mcp_server(), create_a2a_server(), and ADCPAgentExecutor.__init__. Adopters who omit validation= now get strict requests + strict responses; the pricing_options class of bug (extra=\"allow\" Pydantic silently swallowing an unknown shape) fails on first call rather than during a buyer's storyboard run.
  • Canonical default lives in adcp.validation.SERVER_DEFAULT_VALIDATION and is re-aliased as adcp.server.serve.DEFAULT_VALIDATION. Adopters opt out via validation=ValidationHookConfig(responses=\"warn\") (warn-only on response drift) or validation=None (off entirely).
  • Test fixtures focused on transport plumbing — middleware composition, context-factory propagation, parser dispatch, idempotency scoping — opt out via validation=None rather than grow full spec-conformant Product / adcp.idempotency payloads on minimal stubs.

Test fixtures touched

Count: 4 test files (19 transport-plumbing tests originally surfaced as failures under strict-by-default; all now pass with explicit validation=None):

  • tests/test_a2a_server.py — module-level ADCPAgentExecutor wrapper sets validation=None once for all 17 dispatch sites
  • tests/test_handler_typevar.py — 2 sites add validation=None
  • tests/test_mcp_middleware_composition.py — 2 create_mcp_server(...) fixtures add validation=None
  • tests/test_server_caller_identity.py — 4 ADCPAgentExecutor(...) constructions add validation=None

New tests

  • tests/test_serve_validation_default.py — pins the strict default in serve / create_mcp_server / create_a2a_server signatures via inspect.signature.
  • Updated tests/test_serve_validation_passthrough.py — replaced test_serve_validation_default_is_none with test_serve_validation_default_is_strict and added test_serve_validation_explicit_none_disables.

Test plan

  • Full unit suite: 3166 passed, 25 skipped (was 2777 passed before this change; new tests + retained behavior).
  • Conformance suite: 383 passed (no regressions).
  • ruff check, mypy, pre-commit hooks pass.
  • Smoke-test v3 reference seller boot (already opt-in strict; explicit validation= kwarg now redundant with framework default but kept self-documenting).
  • Adopters running serve(handler) with no args now get strict validation; logs/errors should reflect VALIDATION_ERROR on the first malformed call.

🤖 Generated with Claude Code

@bokelley bokelley closed this May 3, 2026
@bokelley bokelley reopened this May 3, 2026
… by default

Promote strict-by-default schema validation across `serve()`,
`create_mcp_server()`, `create_a2a_server()`, and the `ADCPAgentExecutor`
constructor. Adopters who omitted the `validation=` kwarg previously got
no enforcement; the recent `pricing_options` regression
(`{"type", "rate"}` vs spec `{"pricing_model", "fixed_price"}`) shipped
because Pydantic models with `extra="allow"` silently swallowed the
unknown shape past type-validation. Strict-by-default puts the
schema check on every adopter's serve path so the next bug of this
class fails on first call rather than during a buyer's storyboard run.

Default values resolve to a shared canonical
`SERVER_DEFAULT_VALIDATION = ValidationHookConfig(requests="strict",
responses="strict")` exposed from `adcp.validation` and re-aliased as
`adcp.server.serve.DEFAULT_VALIDATION`. Adopters opt out via
`validation=ValidationHookConfig(responses="warn")` (warn-only) or
`validation=None` (off entirely) — both paths are documented and
exercised by tests.

Test fixtures focused on transport plumbing (middleware composition,
context-factory propagation, parser dispatch, idempotency scoping) opt
out via `validation=None` rather than grow full spec-conformant
`Product` / `adcp.idempotency` payloads on minimal stubs. Affected
files: `tests/test_a2a_server.py` (module-level `ADCPAgentExecutor`
wrapper sets `validation=None` once), `tests/test_handler_typevar.py`,
`tests/test_mcp_middleware_composition.py`,
`tests/test_server_caller_identity.py`. New
`tests/test_serve_validation_default.py` pins the strict default in
`serve` / `create_mcp_server` / `create_a2a_server` signatures.

The v3 reference seller already opted into strict; its `validation=`
arg is now redundant with the framework default but kept explicit so
the seller's posture remains self-documenting.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@bokelley bokelley force-pushed the bokelley/dx-8-validation-default branch from 1583258 to 68c91b3 Compare May 3, 2026 03:26
…ation

Three integration tests under tests/integration/ use stub handlers
that send synthetic non-spec payloads (e.g. {'brief': 'x'}) to
exercise A2A context-id passthrough and v0.3 wire compat — not
request shape conformance. The new strict default in serve() correctly
caught their non-spec payloads (catch is a feature) but these tests
shouldn't validate at all. Pass validation=None on the create_a2a_server
sites, matching the same pattern used for unit-test fixtures.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@bokelley bokelley merged commit a29b61f into main May 3, 2026
11 of 13 checks passed
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