Repository navigation
fix(version): keep protocol envelope fields when converting responses down - #3594
Conversation
… down The schema projection removed `status`, `message`, `context` and the other envelope fields when converting a response down to a release whose task schema didn't compose the protocol envelope (the 3.2 proposal-flow responses; DR-0036 correction b), so a 3.2 buyer could get a 3.3 server's list_products response without its status. Root-level envelope fields are now never projected away. Also runs both shared vector files (3.2-3.1 and the new 3.3-3.2 set from adcontextprotocol/adcp#8433), using each pair's client release. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| const sourceSchemas = schemasForKey(sourceIndex, key) ?? nonEmpty(sourceIndex.additional); | ||
| const targetSchemas = schemasForKey(targetIndex, key) ?? nonEmpty(targetIndex.additional); | ||
| const entryPath = joinPath(path, key); | ||
| if (protectedKeys.has(key)) { |
There was a problem hiding this comment.
Medium: The protection is unconditional — it continues before recursing, so structured envelope keys (context, governance_context, push_notification_config, payload) are kept wholesale even when the target release composes the envelope and could project their contents. For a 3.3 → 3.2.8 down-conversion (DR-0036 correction (b) backports the envelope into 3.2.8, so 3.2.8 declares these keys), a newer-only sub-field of context/governance_context now leaks unprojected and, because this path skips dropped.push, unreported. The witness-not-translator contract requires every removed-or-undeliverable field to surface as drift; this short-circuit silently keeps them instead. The surgical shape protects a key only when the target does not declare it — when the target declares the envelope, normal projection already keeps the key and still reports nested drift. Same set is also applied to request projection (translate.ts:179), so envelope-keyed request fields bypass the mutating-refusal gate; that's benign for the current field set but widens the blast radius. No test covers nested envelope-field drift.
There was a problem hiding this comment.
Ladon verdict: Approve
Approve — schema projection now preserves protocol-envelope fields (status/message/context) during version down-conversion instead of stripping them, a correct witness-not-translator fix grounded in DR-0036 correction (b), with a changeset and top-level test coverage.
Medium findings
src/lib/adapters/version/schema-projection.ts:327— Unconditional envelope-key protection skips nested projection and drift reporting: newer sub-fields of structured envelope objects (context, governance_context, payload) may leak unprojected/unreported when the target composes the envelope; same set applied to request projection bypasses the mutating-refusal gate (benign for current fields).
Only 1 medium finding, not in {data-loss, schema, infra}; no critical/high; high_risk false; gated_paths false; no author team gate; no prior decision. Rows 1–8 do not fire — row 9 applies. Note: review_decision is REVIEW_REQUIRED but gated_paths is false, so row 2 does not apply.
Medium findings
- src/lib/adapters/version/schema-projection.ts:327 — Unconditional envelope-key protection skips nested projection and drift reporting
Generating the 3.3 ↔ 3.2 conformance vectors (adcontextprotocol/adcp#8433) turned up a bug in version translation: converting a response down to an older release stripped protocol envelope fields (
status,message,contextand the rest) when that release's task schema didn't compose the protocol envelope.That's the case for the 3.2 proposal-flow responses, which DR-0036 correction (b) fixes in 3.3 and a 3.2.x patch. A 3.3 server's
list_productsresponse reached a 3.2 buyer withoutstatus. The envelope applies to every task payload regardless of the task schema, so the projection now keeps every root-level property the release's protocol and version envelopes declare.The released 16.x line (3.2 → 3.1) isn't affected on the common tasks: their 3.1 schemas compose the envelope.
Tests
test/server-version-translation.test.jscovers the stripped-envelope case.test/lib/version-translation-vectors.test.jsnow runs both shared vector files,3.2-3.1.jsonand the new3.3-3.2.json, each with the pair's own client release (40 cases).Separate gap (not fixed here): SDK 17 (
main) still bundles 3.2.7 for its maintained 3.2 line, while 16.x moved to 3.2.8 (#3484). 3.2.8 carries the DR-0036 proposal-flow reopening. A 17.x client validating against 3.2 therefore uses the pre-correction schemas.🤖 Generated with Claude Code
Open workspace in Conductor