Skip to content

feat(types, validation): opt-in support for AdCP 3.1.0-beta.1 (catalog-sync cluster)#1879

Merged
bokelley merged 2 commits into
mainfrom
bokelley/3-1-beta-schemas-and-types
May 19, 2026
Merged

feat(types, validation): opt-in support for AdCP 3.1.0-beta.1 (catalog-sync cluster)#1879
bokelley merged 2 commits into
mainfrom
bokelley/3-1-beta-schemas-and-types

Conversation

@bokelley
Copy link
Copy Markdown
Contributor

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') on AdCPClient and get strict validation + typed access to 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.

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

import { AdCPClient } from '@adcp/sdk';
import type {
  GetProductsRequest,
  GetProductsResponse,
  CatalogEvent,
  CatalogEventsResponse,
} from '@adcp/sdk/types/v3-1-beta';

const client = new AdCPClient({
  agentUrl: 'https://salesagent.example.com',
  adcpVersion: '3.1-beta', // or '3.1.0-beta.1' for full-semver pinning
});

const req: GetProductsRequest = {
  buying_mode: 'wholesale',
  if_catalog_version: 'v2026-05-18T08:00:00Z-acme-rev412',
};

Plumbing

  • scripts/sync-3-1-beta-schemas.ts wraps syncSchemas() with two safety properties:
    • Restores the latest symlink to the primary GA pin after the beta tarball extraction.
    • Restores tracked side-effect paths (schemas/registry/registry.yaml, protocol-managed skills) from HEAD 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 specific to 3.1 schemas:
    • Intra-schema $ref reseating (#/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 causes MissingPointerError.
    • Index-signature widening on inline anonymous objects so optional named properties (e.g. ForecastPoint.metrics.audience_size?: ForecastRange) don't collide with the [k: string]: ForecastRange index signature (TS2411). json-schema-to-typescript's strictIndexSignatures handles top-level types but not inline objects.
  • 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. Pattern matches the existing 3.0.0-beta.1 / 3.0.0-beta.3 entries.
  • package.json ./types/v3-1-beta export + typesVersions entry mirror the v2.5 pattern.

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

Test plan

  • Reviewer pulls the branch, runs npm run sync-schemas:3.1-beta, confirms cosign verification passes
  • Reviewer runs npm run generate-types:3.1-beta && npx tsc --noEmit and confirms 0 errors
  • Reviewer imports a beta type (import type { CatalogEvent } from '@adcp/sdk/types/v3-1-beta') in a downstream consumer and confirms IntelliSense exposes the catalog-sync fields
  • CatalogSync follow-on PR (issue #4794 client implementation) opens against main once this lands

🤖 Generated with Claude Code

bokelley and others added 2 commits May 19, 2026 15:11
…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>
@bokelley bokelley merged commit e4ba8c9 into main May 19, 2026
8 of 9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Proposal: CatalogSync client in @adcp/client for v3.1 beta (mirrors RegistrySync)

1 participant