Problem (follow-up to #626)
adcp.testing.build_asgi_app (PR #626) is the public test-harness builder we adopted in salesagent (replacing private _build_mcp_and_a2a_app / _apply_asgi_middleware imports). It accepts every production serve() kwarg we needed — auth, asgi_middleware, context_factory, streaming_responses, allowed_hosts/origins, validation, etc. — but not pre_validation_hooks.
Effect on adopters: in-process HTTP-shaped tests driven via httpx.ASGITransport(app=build_asgi_app(...)) get a different validation surface than production. Pre-v3 buyer payloads (buying_mode omitted, bare-string format_id, missing asset_type) that production auto-defaults via serve(pre_validation_hooks=...) are rejected by build_asgi_app-driven tests. Tests pass on payloads that would have been coerced in prod; tests fail on payloads that prod accepts.
We hit this during salesagent's 5.0 migration. Our code reviewer flagged it as IMPORTANT: "Pre-v3 buyer shapes that production would auto-default to will not be auto-defaulted in tests. This means in-process HTTP-shaped tests can pass while production fails on the same payload, or vice versa."
Concrete divergence
# Production:
serve(
router,
pre_validation_hooks={"get_products": apply_buying_mode_default},
...
)
# Buyer payload {"brand": ...} (no buying_mode) → coerced to {"brand": ..., "buying_mode": "brief"} → validates.
# Test:
app = build_asgi_app(router, ...)
# Same payload → ValidationError: "buying_mode" field required.
Tests can't reach the production code path without re-implementing the coercion in test setup — which defeats the point of testing the production wiring.
Proposed fix
Add pre_validation_hooks to build_asgi_app's signature, forwarded the same way serve() forwards it to create_adcp_server_from_platform or the underlying MCP server builder:
def build_asgi_app(
platform: DecisioningPlatform,
*,
name: str = ...,
# ... existing kwargs ...
pre_validation_hooks: dict[str, Callable[[str, dict[str, Any]], dict[str, Any]]] | None = None,
factory_kwargs: ...,
) -> Any: ...
Symmetric with serve(). Forwards to whatever internal path serve() uses to install the hooks pre-dispatcher.
Why this matters
This is the second adopter friction point on build_asgi_app since 5.0 (the first was the #618 pre-existing gap that the PR addressed). Every kwarg serve() accepts that build_asgi_app doesn't accept is a place adopters either (a) lose production fidelity in tests, or (b) re-implement plumbing on the test side.
For salesagent, the workaround for now is documenting in build_app()'s docstring that pre-v3 wire fidelity is not preserved in in-process tests — adopters who need pre-v3-shape testing must use the production serve() path or pre-shape payloads themselves. Worth fixing properly upstream.
Files
- Our
build_app(): bokelley/salesagent core/main.py — search for build_asgi_app. Currently drops pre_validation_hooks with a comment justifying the divergence; that comment can be removed once this lands.
Related
Problem (follow-up to #626)
adcp.testing.build_asgi_app(PR #626) is the public test-harness builder we adopted in salesagent (replacing private_build_mcp_and_a2a_app/_apply_asgi_middlewareimports). It accepts every productionserve()kwarg we needed — auth, asgi_middleware, context_factory, streaming_responses, allowed_hosts/origins, validation, etc. — but notpre_validation_hooks.Effect on adopters: in-process HTTP-shaped tests driven via
httpx.ASGITransport(app=build_asgi_app(...))get a different validation surface than production. Pre-v3 buyer payloads (buying_modeomitted, bare-stringformat_id, missingasset_type) that production auto-defaults viaserve(pre_validation_hooks=...)are rejected bybuild_asgi_app-driven tests. Tests pass on payloads that would have been coerced in prod; tests fail on payloads that prod accepts.We hit this during salesagent's 5.0 migration. Our code reviewer flagged it as IMPORTANT: "Pre-v3 buyer shapes that production would auto-default to will not be auto-defaulted in tests. This means in-process HTTP-shaped tests can pass while production fails on the same payload, or vice versa."
Concrete divergence
Tests can't reach the production code path without re-implementing the coercion in test setup — which defeats the point of testing the production wiring.
Proposed fix
Add
pre_validation_hookstobuild_asgi_app's signature, forwarded the same wayserve()forwards it tocreate_adcp_server_from_platformor the underlying MCP server builder:Symmetric with
serve(). Forwards to whatever internal pathserve()uses to install the hooks pre-dispatcher.Why this matters
This is the second adopter friction point on
build_asgi_appsince 5.0 (the first was the #618 pre-existing gap that the PR addressed). Every kwargserve()accepts thatbuild_asgi_appdoesn't accept is a place adopters either (a) lose production fidelity in tests, or (b) re-implement plumbing on the test side.For salesagent, the workaround for now is documenting in
build_app()'s docstring that pre-v3 wire fidelity is not preserved in in-process tests — adopters who need pre-v3-shape testing must use the productionserve()path or pre-shape payloads themselves. Worth fixing properly upstream.Files
build_app(): bokelley/salesagent core/main.py — search forbuild_asgi_app. Currently dropspre_validation_hookswith a comment justifying the divergence; that comment can be removed once this lands.Related
build_asgi_app. This is a per-kwarg gap, not a regression.pre_validation_hooksmechanism this would forward.