fix(protocols): normalize adcp_version to release-precision on the wire#1807
Merged
Conversation
The adcp_version envelope field (AdCP 3.1, spec PR adcontextprotocol/adcp#3493) is constrained by core/version-envelope.json to release-precision strings via pattern ^\d+\.\d+(-[a-zA-Z0-9.-]+)?$. Full-semver bundle keys like '3.1.0-beta.0' are explicitly NOT valid wire values per the envelope schema's own normalization rule. Before this fix, buildVersionEnvelope emitted the bundle key verbatim for prerelease pins. A 3.1.0-beta-pinned client would send adcp_version: "3.1.0-beta.0", which sellers AJV-reject with a pattern mismatch. The bug was latent: COMPATIBLE_ADCP_VERSIONS doesn't include any 3.1.x release today, so no public-pin path could hit it, but a caller manually overriding adcpVersion past the compat gate (or the SDK's eventual 3.1 enablement) would silently break. Introduces toReleasePrecisionWire(bundleKeyOrVersion) next to resolveBundleKey, applied at the single wire-emit site in buildVersionEnvelope. Exported publicly so storyboard fixtures and conformance harnesses produce the same wire shape the SDK emits. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
3 tasks
Round-1 multi-reviewer pass on PR #1807 surfaced two convergent JSDoc gaps and one missing test category. JSDoc additions: - "**Idempotent.** Re-applying to an already-wire-shaped value is a no-op" — DX reviewer noted the test file states this but the contract should live in the JSDoc itself (cited by the conformance-test author who'll call the function defensively). - "**Prerelease regex is stricter than the wire pattern by design.**" — protocol + code reviewers both noted that the regex `[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*` is tighter than the wire's `[a-zA-Z0-9.-]+`. The strictness is intentional (mirrors resolveBundleKey's path-traversal hardening + SemVer §9 compliance); the comment prevents a future reader from "fixing" it back to match the wire char-for-char. Test additions: - Trailing-dot rejection (`'3.1.0-beta.'`, `'3.0.'`) — confirms the stricter-than-wire-pattern note is enforced by code, not just doc. - Leading-zero round-trip (`'03.01'` -> `'03.01'`) — code reviewer's precedence-story documentation: we don't normalize numeric components, only the patch-collapse. Single-reviewer concerns not addressed in this commit (defer to follow-up issues per the run-by-experts convergence rule): - Naming asymmetry (`toReleasePrecisionWire` vs sibling `bundleSupportsAdcpVersionField`). DX preference; no convergence. - `resolveBundleKey` doesn't accept release-precision pins (`'3.1-beta'` throws). Protocol-reviewer-flagged follow-up; out of scope for the wire-emit fix. - Outgoing-envelope validation should name the helper in errors. DX follow-up. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
Author
Round 1 expert review — consensus: shipFired four reviewers in parallel per Convergent findings (2+ reviewers) — addressed in d3e6ad7:
Single-reviewer must-fix (worth doing) — also in d3e6ad7:
Single-reviewer suggestions — deferring to follow-up issues:
Security: explicit ship-it. No path coupling, no ReDoS (linear up to 512KB inputs), no log-injection vector, safe to export. Reviewer summaries:
|
5 tasks
bokelley
added a commit
that referenced
this pull request
May 17, 2026
…ce (#1814) * feat(wire-version): release-precision pins + wire validator + namespace Three follow-ups surfaced during the expert review of #1807. Each is small and additive; bundled because they share the wire-version helper surface. Issue #1: resolveBundleKey accepts release-precision pins. AdCP 3.1's supported_versions capability advertises versions in release-precision shape (["3.1-beta"]), and a buyer reading that off the wire must be able to construct a client pinned to it. Before: new AdcpClient({ adcpVersion: '3.1-beta' }) threw ConfigurationError. Now: resolveBundleKey accepts MAJOR.MINOR-PRE and returns verbatim; resolveSchemaRoot fuzzy-resolves to the highest cached prerelease dir whose own release-precision form matches (so '3.1-beta' finds schemas/cache/3.1.0-beta.0/, '3.1-beta.0' matches it exactly). Issue #2: validateAdcpVersionWire public assertion. Throws ConfigurationError with a hint pointing at toReleasePrecisionWire when given a non-spec wire value. Use when constructing request envelopes by hand (storyboard fixtures, conformance harnesses, custom transports). Also wired as a defensive postcondition in buildVersionEnvelope so future refactors can't silently emit non-spec shapes. Issue #3: wireVersion namespace. Groups the three helpers (isSupported, normalize, validate) under one barrel export. Stable target for future additions. Top-level exports kept for back-compat — not deprecated. Tests cover all three plus regression checks against the existing test suites (47 tests pass across the wire-version surface). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * test(wire-version): make fuzzy-resolution test hermetic CI failed on the release-precision-pinned-bundle test because it relied on schemas/cache/3.1.0-beta.0/ existing — that directory is gitignored and only present in dev workspaces that have synced a 3.1 prerelease cache. CI only syncs the SDK-pinned ADCP_VERSION. Fix: create a synthetic 98.0.0-test.0/ fixture directory in before(), exercise the fuzzy lookup against it (a pin of '98.0-test' should resolve via the release-precision form '98.0-test.0'), and clean up in after(). The fixture's major version (98) is far outside any plausible real AdCP cache so a stray leftover can't conflict. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Merged
bokelley
added a commit
that referenced
this pull request
May 17, 2026
Standalone design proposal for how the SDK should expose AdCP 3.1 (canonical formats, version envelope, expanded capabilities) without forcing adopters to think about v1 vs v2 shapes. The proposal lays out: auto-negotiation per agent via supported_versions; a single dual-shape Product type that always carries format_ids and populates format_options when projectable; bidirectional v1<->v2 projection with a structured Diagnostic surface (FORMAT_PROJECTION_FAILED, FORMAT_DECLARATION_DIVERGENT, CUSTOM_FORMAT_FETCH_FAILED) that's never logger-only per the spec's resolution-order amendment; a centralized fetchFormatSchema utility implementing the normative custom-format fetch contract; versioned codegen under src/lib/types/v3.0/ and v3.1/. Migration path: 7.x defensive fixes (PR #1807 already shipped); 8.0 ships canonical-formats enablement; 8.x hardens; 9.0 removes v1 paths when AdCP 4.0 lands. Explicit non-goals and open questions documented for reviewer feedback. Draft for review only — no code changes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
toReleasePrecisionWire(bundleKeyOrVersion)next toresolveBundleKeyinschema-loader.ts.buildVersionEnvelopeat the single emit site so prerelease pins emit spec-valid wire values.Why this matters
AdCP 3.1's
core/version-envelope.jsonconstrains theadcp_versionfield to release-precision strings via pattern^\d+\.\d+(-[a-zA-Z0-9.-]+)?$. The schema's own description carries the normalization rule:Before this fix,
buildVersionEnvelopeemitted the bundle key verbatim, so a 3.1.0-beta-pinned client would sendadcp_version: "3.1.0-beta.0"and sellers would AJV-reject the request body with a pattern mismatch.The bug is latent today —
COMPATIBLE_ADCP_VERSIONSdoesn't include any 3.1.x release, so no public-pin path can hit it. But a caller manually overridingadcpVersionpast the compat gate, or the SDK's eventual 3.1 enablement, would silently break. Found while staging a local 3.1.0-beta cache from spec PR adcontextprotocol/adcp#3307 and exercisingbuildVersionEnvelopeagainst the real envelope schema.How it works
toReleasePrecisionWire:'3.0','3.1') pass through unchanged.'3.1.0-beta.0'→'3.1-beta.0'.'3.0.11'→'3.0'.'v3') pass through (gated out of the wire field bybundleSupportsAdcpVersionFieldanyway).Verification
End-to-end check: emitted envelopes for
bundle = '3.0' | '3.1' | '3.1.0-beta.0' | '3.0.11'all validate againstcore/version-envelope.jsonfrom a locally-built 3.1.0-beta.0 cache.Test plan
npm run format:checkclean.npm run typecheckclean.🤖 Generated with Claude Code