feat(types, validation): opt-in support for AdCP 3.1.0-beta.1 (catalog-sync cluster)#1879
Merged
Merged
Conversation
…g-sync cluster) Adopters pin `adcpVersion: '3.1.0-beta.1'` (or '3.1-beta') to validate against the beta schemas including the catalog-sync cluster from adcontextprotocol/adcp#4767: `if_catalog_version` / `if_pricing_version` conditional fetch on `get_products` / `get_signals`, wholesale `discovery_mode` for signals, the `catalog_change_feed` capability stanza, and the new `core/catalog-event.json` + `core/catalog-events-response.json`. The SDK's primary pin stays at the GA `ADCP_VERSION` — this is a side-bundle, not a default move. The `latest` symlink in `schemas/cache/` continues to point at the GA pin. Plumbing: - scripts/sync-3-1-beta-schemas.ts wraps syncSchemas() with `latest` symlink restoration + HEAD-restore of tracked side-effect paths (`schemas/registry/registry.yaml`, protocol-managed skills) so an opt-in beta sync never bumps GA-pinned check-in surfaces. - scripts/generate-3-1-beta-types.ts emits the parallel `src/lib/types/v3-1-beta/tools.generated.ts` surface, mirroring the v2.5 codegen pipeline with two additions: intra-schema `$ref` reseating (`#/oneOf/...` → `#/definitions/<Name>/oneOf/...`) for the schemas that self-reference inside their own tree, and index-signature widening on inline anonymous objects so optional named properties don't collide with the index type (TS2411). - COMPATIBLE_ADCP_VERSIONS extended via `COMPATIBLE_PREFIX`. The major/minor gate in `buildCompatibleVersions` stays closed for primary-pin moves — opt-in betas land in the prefix list, GA bumps still require an explicit range extension. - package.json `./types/v3-1-beta` export + typesVersions entry mirror the v2.5 pattern. CatalogSync client (the consumer-facing change-feed mirror) lands in a follow-on PR. Verified: - tsc --noEmit clean (0 errors) - format:check clean - npm run build:lib clean; dist/lib/schemas-data/3.1.0-beta.1/ ships alongside 3.0 and v2.5 - 21/21 schema-loader-per-version + v2-5-types-import tests pass; 21/21 validation tests pass - End-to-end: resolveBundleKey('3.1-beta') resolves; getValidator ('get_products', 'sync', '3.1.0-beta.1') compiles; a beta-shaped response (unchanged: true, catalog_version, cache_scope: 'public') validates against the beta schema Closes adcontextprotocol/adcp#4794 (the SDK-side schema/types prerequisite — CatalogSync client lands separately). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Five fixes from the parallel DX/protocol/code/security expert reviews of
the catalog-sync opt-in PR. Convergent blockers from three of the four
reviewers; security findings were inherited from `sync-schemas.ts` and
filed for follow-up rather than this PR.
1. Protocol-safety on CatalogChangeEvent (the most important fix).
`core/catalog-event.json` declares `payload` as root-required but lists
only the discriminator (`event_type`) in each branch's local `required`.
`json-schema-to-typescript` emitted `payload?: BranchShape` in each
union branch, so intersection with the wrapper's `payload: {}` produced
`payload: {}` — load-bearing branch-specific shape lost at the type
level.
Added `propagateRootRequiredIntoOneOfBranches` to the codegen pipeline:
for any schema with `oneOf` + root-level `required`, lift the root
required fields into every branch's required array before handing to
jsts. Each branch now emits `payload: BranchShape` (non-optional), the
intersection narrows correctly, and discriminated-union safety survives
codegen.
Ajv runtime validation already enforced the spec semantics (Ajv reads
the original schema with `required` intact); this fixes only the TS
surface adopters see.
2. WIRE-VERSION-COMPAT.md playbook now describes both compat shapes.
Added a "Two compat patterns — pick the right one" comparison table at
the top and a full "Recipe: opt-in side-bundle" section covering the
newer-than-pin pattern: tarball-not-SHA, latest-symlink restore,
RESTORE_PATHS from HEAD, COMPATIBLE_PREFIX extension, the three
preprocessing passes, and the upgrade story for beta.N to beta.N+1.
3. Changeset now recommends `'3.1-beta'` (release-precision) as the
canonical pin. Full-semver `'3.1.0-beta.1'` is now a trailing sentence
for bit-fidelity in cross-version interop tests. Adds the
namespace-import recommendation (`import * as V31Beta`) since flat-named
imports will collide with the GA surface. Includes a 6-line poll-loop
snippet so adopters can use this surface today without waiting for
CatalogSync.
4. Removed dead `readdirSync` import + `void readdirSync` footer from
`generate-3-1-beta-types.ts`. The defensive comment was factually wrong
— `loadStandaloneCoreSchemas` uses only `existsSync` + `readFileSync`.
5. Runtime-validator test added at
`test/lib/schema-loader-per-version.test.js`. Exercises
`getValidator('get_products', 'request', '3.1.0-beta.1')` against an
`if_catalog_version`-bearing payload (must validate) AND an
`if_pricing_version`-without-`if_catalog_version` payload (must reject).
The second assertion confirms Ajv still enforces the spec's
`dependencies` constraint at runtime even though codegen strips it for
TS-surface generation. Also exercises the release-precision
`'3.1-beta'` resolution path.
Verified: tsc --noEmit clean, format:check clean, 19/19
schema-loader-per-version tests pass (up from 18), build clean.
Deferred to separate PRs per security reviewer's classification as
inherited from `sync-schemas.ts`:
- Cosign fail-closed in NODE_ENV=production publish path (medium)
- `assertSafeVersion` on `sync()` version parameter (low)
- `path.resolve` + boundary check in `refToCachePath` defense-in-depth (low)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced May 19, 2026
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
Side-bundle support for AdCP 3.1.0-beta.1 — adopters can now pin
adcpVersion: '3.1.0-beta.1'(or'3.1-beta') onAdCPClientand get strict validation + typed access to the catalog-sync cluster from adcontextprotocol/adcp#4767:if_catalog_version/if_pricing_versionconditional fetch onget_products/get_signals, wholesalediscovery_modefor signals, thecatalog_change_feedcapability stanza, and the newcore/catalog-event.json+core/catalog-events-response.json.The SDK's primary pin stays at the GA
ADCP_VERSION— this is a side-bundle, not a default move. Thelatestsymlink inschemas/cache/continues to point at the GA pin.Closes adcontextprotocol/adcp#4794 for the schema/types prerequisite. CatalogSync client (the consumer-facing change-feed mirror) lands in a follow-on PR — this opt-in unblocks adopters building their own mirror loop directly against the typed surface today.
Consumer flow this unblocks
Plumbing
scripts/sync-3-1-beta-schemas.tswrapssyncSchemas()with two safety properties:latestsymlink to the primary GA pin after the beta tarball extraction.schemas/registry/registry.yaml, protocol-managed skills) fromHEADso an opt-in beta sync never bumps GA-pinned check-in surfaces.scripts/generate-3-1-beta-types.tsemits the parallelsrc/lib/types/v3-1-beta/tools.generated.tssurface, mirroring the v2.5 codegen pipeline with two additions specific to 3.1 schemas:$refreseating (#/oneOf/...→#/definitions/<Name>/oneOf/...) for tools whose schemas reference their own internal sub-trees (get_brand_identity,verify_brand_claims, etc.). Without this, the mega-schema bundling causesMissingPointerError.ForecastPoint.metrics.audience_size?: ForecastRange) don't collide with the[k: string]: ForecastRangeindex signature (TS2411).json-schema-to-typescript'sstrictIndexSignatureshandles top-level types but not inline objects.COMPATIBLE_ADCP_VERSIONSextended viaCOMPATIBLE_PREFIX. The major/minor gate inbuildCompatibleVersionsstays closed for primary-pin moves — opt-in betas land in the prefix list, GA bumps still require an explicit range extension. Pattern matches the existing3.0.0-beta.1/3.0.0-beta.3entries.package.json./types/v3-1-betaexport +typesVersionsentry mirror the v2.5 pattern.Verified
tsc --noEmitclean (0 errors)format:checkcleannpm run build:libclean;dist/lib/schemas-data/3.1.0-beta.1/ships alongside3.0andv2.5schema-loader-per-version+v2-5-types-importtests passvalidationtests passresolveBundleKey('3.1-beta')resolves;getValidator('get_products', 'sync', '3.1.0-beta.1')compiles; a beta-shaped response (unchanged: true, catalog_version, cache_scope: 'public') validates against the beta schemaTest plan
npm run sync-schemas:3.1-beta, confirms cosign verification passesnpm run generate-types:3.1-beta && npx tsc --noEmitand confirms 0 errorsimport type { CatalogEvent } from '@adcp/sdk/types/v3-1-beta') in a downstream consumer and confirms IntelliSense exposes the catalog-sync fieldsmainonce this lands🤖 Generated with Claude Code