fix(server): centralize pre-validation hook composition#882
Conversation
|
Decision: this is worth landing. #860 already shipped the core of #859 —
To land:
@sangilish — can you rebase? Happy to take it over and push the rebase if you'd prefer. Keeping #859 open as the tracker until this lands. |
|
Rebased onto current Conflict resolution summary:
Checks on the rebased branch (Python 3.11):
The storyboard CI failures match the dist-tag drift @sangilish described and are reproducible from a clean @bokelley — the rebased branch is ready. You can fast-forward Generated by Claude Code |
|
Superseded by #938, which re-applies this PR's diagnostics — the named-chain error attribution (pre_validation_hook[i] ...) and the real-path MCP/A2A tests, consolidated into a new adcp/server/_hooks.py — freshly on current main (#930/#933 had moved the server files this branch was based on, so a rebase wasn't clean). You're credited as co-author on #938, @sangilish. The core hook composition shipped earlier in #860, and #938 closes #859. Thanks for the original work. |
…ures (#938) Move the pre-validation hook types, compose_pre_validation_hooks, and the _flatten/_apply helpers into a single source-of-truth module adcp.server._hooks. spec_compat.py re-imports and re-exports the public names with __all__ so existing imports keep working; __init__.py, a2a_server.py, mcp_tools.py, and serve.py now import from _hooks. Add PreValidationHookError so INVALID_REQUEST messages name the failing chain index and callable (e.g. "pre_validation_hook[1] second_hook raised RuntimeError: ...") instead of a generic "pre_validation_hook raised ...". The dispatcher catches it on the shared create_tool_caller path, so both MCP and A2A surface the attributed message. Replace the mock/spy MCPToolSet test with a real call_tool() behavior test, add an A2A ordered-chain executor test, and add MCP + A2A chain-failure attribution tests asserting the index and callable appear in the error. Closes #859 Supersedes #882 Co-authored-by: sangilish <sangilish@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Summary
Refs #859.
This is a focused follow-up to the hook-composition work that already landed in #860. Current
mainalready supports ordered pre-validation hook chains, so this PR avoids reimplementing that behavior and only tightens the remaining pieces from the #859 brief and #860 review notes:adcp.server._hooks.adcp.server.spec_compatbackward-compatible by re-exporting the public hook types/helper.INVALID_REQUESTmessage.MCPToolSet.call_tool(...)behavior test.Review note
I opened this as a draft because #860 already shipped most of #859. The main question is whether this small cleanup/diagnostic follow-up is still useful, or whether #859 should be closed as already covered by #860.
Current CI note
The draft currently has red storyboard checks against the moving
adcp-3.1npm dist-tag. The failing PR run installed@adcp/sdk@8.1.0-beta.13; the latest passingmainCI run I checked used@adcp/sdk@8.1.0-beta.12. I also reproduced the same classes of storyboard failure from a detached current-mainworktree withADCP_SDK_VERSION=adcp-3.1, including the reference seller 3.1 storyboard and proposal-finalize recovery expectation.I am therefore keeping this as draft rather than marking it ready while that runner/schema drift is active.
Testing
uv run pytest tests/test_pre_validation_hooks.py tests/test_a2a_server.py -quv run pytest tests/test_pre_validation_hooks.py tests/test_a2a_server.py tests/test_spec_compat_hooks.py tests/test_spec_compat_hooks_deprecation.py tests/test_unknown_field_policy_server.py tests/test_schema_validation_server.py tests/test_serve_validation_passthrough.py tests/test_testing_decisioning.py tests/test_public_api.py -quv run ruff check src/adcp/server/_hooks.py src/adcp/server/spec_compat.py src/adcp/server/mcp_tools.py src/adcp/server/a2a_server.py src/adcp/server/serve.py src/adcp/server/__init__.py tests/test_pre_validation_hooks.py tests/test_a2a_server.pyuv run mypy src/adcp/serveruv run mypy src/adcpuv run pytest -qgit diff --checkFull
uv run ruff checkstill fails on unrelated existing test lint drift outside this PR's touched files, including long lines and unused imports in existing tests.