Commit d2ffac7
feat(server): route validation by wire adcp_version (stage 3) (#664)
* feat(validation): per-version validator loader (stage 2)
``get_validator``, ``validate_request``, and ``validate_response`` now
accept an optional ``version=`` kwarg. ``None`` defaults to the SDK's
compile-time pin (existing behaviour, byte-identical for current call
sites). A wire-version string (``"3.0.7"``, ``"2.5"``,
``"3.1.0-beta.1"``) routes to the per-bundle-key cache laid down by
Stage 1 — each version's file index, compiled validators, and core
registry live in their own ``_LoaderState`` so cross-version traffic
doesn't share compilation state.
The dispatcher call sites are unchanged today; Stage 3 will detect the
buyer's ``adcp_major_version`` off the wire and thread it through.
Also widens ``resolve_bundle_key`` to accept bare ``MAJOR.MINOR``
(already a bundle key — matches the wire envelope's release-precision
``adcp_version`` field, so the dispatcher can pass it straight through).
Tests:
* ``test_schema_loader_per_version`` lays down a synthetic
``schemas/cache/2.5/`` fixture, asserts validators for the synthetic
legacy tool and the real SDK-pin tools coexist independently, and
pins ``version=None`` behaviour to the SDK pin.
* ``test_validation_version`` extended with the new pass-through and
rejection cases.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test(validation): isolate per-version loader fixture from repo working tree
Review feedback on PR #659 (Stage 2). Two nits:
* Fixture used to write into the repo's real ``schemas/cache/2.5/``,
which means CI runs against an installed wheel would silently miss
the synthetic bundle (packaged ``_schemas/`` wins) and the assertions
would be vacuous. Move to ``tmp_path`` + ``monkeypatch.setattr`` on
the loader's resolver instead.
* Teardown used a sequence of ``rmdir`` calls that fail if anything else
lands in the directories mid-run. ``shutil.rmtree(..., ignore_errors=
True)`` is robust.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(server): route validation by wire ``adcp_version`` (stage 3)
Stage 3 of the versioned-schema-validation port. ``create_tool_caller``
now detects the buyer's claimed version off the request envelope and
threads it through to the per-version validator added in Stage 2.
Detection order (mirrors the TS SDK's ``applyVersionEnvelope`` and the
spec text on every ``*-request`` schema):
1. ``adcp_version`` (3.1+ release-precision string) — normalized to
release precision (``"3.0.7"`` → ``"3.0"``); must be in
``COMPATIBLE_ADCP_VERSIONS`` or ``VERSION_UNSUPPORTED`` is raised.
2. ``adcp_major_version`` (legacy integer) — maps to the highest
supported minor for that major. Unknown major raises
``VERSION_UNSUPPORTED`` with structured details.
3. Neither field set — falls through to the SDK pin (existing behaviour).
New module ``adcp.validation.envelope`` owns the detection logic;
``UnsupportedVersionError`` carries the wire value and supported set so
the dispatcher can echo both into the error envelope's ``details``.
Existing ``test_spec_compat_hooks`` payloads used
``adcp_major_version=4`` as a wire-shape placeholder — updated to ``3``
now that the dispatcher actually validates the field.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(envelope): reject string adcp_major_version, preserve int type in echo
Review feedback on PR #660.
* String ``adcp_major_version`` (e.g. ``"3"``) used to fall through to
``None`` and silently get SDK-pin validation. A buyer that
JSON-stringified the field deserves a loud
``VERSION_UNSUPPORTED``, not a quiet behaviour swap. Same for ``< 1``
values (below the spec's ``minimum: 1`` bound).
* ``claimed_version`` in error details used to be ``str(exc.wire_value)``,
which converted the int field to ``"4"``. Pass through verbatim so
buyer telemetry sees the same type they sent.
* Rename ``test_no_version_field_validator_uses_sdk_pin`` to actually
test that claim — pass a ``ValidationHookConfig`` so the validator
fires, and assert ``version=None`` is threaded. Split the original
test's "no validation config means no validator runs" assertion into
its own regression test.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(server): default-permissive VERSION_UNSUPPORTED gate (ADCP_STRICT_VERSION_ENVELOPE)
Downstream-impact audit on PR #660 flagged that switching from "wire
schema accepts adcp_major_version 1-99" to "dispatcher rejects unknown
versions" in one release would break test fixtures using ``4`` as a
sentinel and any buyer claiming an unsupported version that happened to
pass schema validation.
Decouple legacy adapter routing (additive, ships now) from
version-envelope strictness (subtractive, ships under a gate).
* Default (``ADCP_STRICT_VERSION_ENVELOPE`` unset or ``"0"``): an
unsupported wire version is logged at WARNING with a migration hint
pointing at the env var. The dispatcher falls through to SDK-pin
validation — same as 5.1.0 behaviour for that payload.
* Strict (``ADCP_STRICT_VERSION_ENVELOPE=1``): raise the
``VERSION_UNSUPPORTED`` envelope the spec prescribes, before any
handler dispatch. This becomes the default in 5.3.
Existing strict-mode tests gain a ``strict_version_envelope`` fixture
so the spec-prescribed behaviour stays covered. New
``test_unsupported_version_permissive_falls_through`` exercises the
default-permissive path and asserts the migration hint shows up in
logs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>1 parent 6311a9a commit d2ffac7
5 files changed
Lines changed: 517 additions & 5 deletions
File tree
- src/adcp
- server
- validation
- tests
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
21 | 21 | | |
22 | 22 | | |
23 | 23 | | |
| 24 | + | |
24 | 25 | | |
25 | 26 | | |
26 | 27 | | |
| |||
1945 | 1946 | | |
1946 | 1947 | | |
1947 | 1948 | | |
| 1949 | + | |
1948 | 1950 | | |
1949 | 1951 | | |
1950 | 1952 | | |
| |||
1980 | 1982 | | |
1981 | 1983 | | |
1982 | 1984 | | |
| 1985 | + | |
| 1986 | + | |
| 1987 | + | |
| 1988 | + | |
| 1989 | + | |
| 1990 | + | |
| 1991 | + | |
| 1992 | + | |
| 1993 | + | |
| 1994 | + | |
| 1995 | + | |
| 1996 | + | |
| 1997 | + | |
| 1998 | + | |
| 1999 | + | |
| 2000 | + | |
| 2001 | + | |
| 2002 | + | |
| 2003 | + | |
| 2004 | + | |
| 2005 | + | |
| 2006 | + | |
| 2007 | + | |
| 2008 | + | |
| 2009 | + | |
| 2010 | + | |
| 2011 | + | |
| 2012 | + | |
| 2013 | + | |
| 2014 | + | |
| 2015 | + | |
| 2016 | + | |
| 2017 | + | |
| 2018 | + | |
| 2019 | + | |
| 2020 | + | |
| 2021 | + | |
| 2022 | + | |
| 2023 | + | |
| 2024 | + | |
| 2025 | + | |
| 2026 | + | |
| 2027 | + | |
| 2028 | + | |
1983 | 2029 | | |
1984 | | - | |
| 2030 | + | |
1985 | 2031 | | |
1986 | 2032 | | |
1987 | 2033 | | |
| |||
2076 | 2122 | | |
2077 | 2123 | | |
2078 | 2124 | | |
2079 | | - | |
| 2125 | + | |
2080 | 2126 | | |
2081 | 2127 | | |
2082 | 2128 | | |
| |||
2109 | 2155 | | |
2110 | 2156 | | |
2111 | 2157 | | |
2112 | | - | |
| 2158 | + | |
| 2159 | + | |
| 2160 | + | |
2113 | 2161 | | |
2114 | 2162 | | |
2115 | 2163 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
0 commit comments