From 5f114f48007bfb7dd7d0e172a36e7dc2e6318014 Mon Sep 17 00:00:00 2001 From: Ilya Grigorik Date: Thu, 21 May 2026 09:03:41 -0700 Subject: [PATCH 1/2] remove T3 platform_schema endpoint workaround MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The upstream spec PR fixing platform_schema's over-strict `endpoint` requirement on rest/mcp anyOf branches has landed. Apply the script's own removal checklist: delete the transform function and self-destruct error, remove the call site, simplify the resolve.http resolver to fetch service.json directly, drop the T3 entries from the transformsApplied[] list and banner block, and regenerate. The regenerated src/core/generated/*.zod.ts schemas have a banner-only diff — the Zod expressions are byte-identical to what shipped in 0.5.0, because the upstream fix is shape-equivalent to what T3 was patching locally. No runtime behavior change; this is pure code hygiene. --- scripts/codegen-schemas.ts | 114 ++++----------------- src/core/generated/business_profile.zod.ts | 3 - src/core/generated/platform_profile.zod.ts | 3 - 3 files changed, 19 insertions(+), 101 deletions(-) diff --git a/scripts/codegen-schemas.ts b/scripts/codegen-schemas.ts index 60b5f0f..1f854c5 100644 --- a/scripts/codegen-schemas.ts +++ b/scripts/codegen-schemas.ts @@ -6,11 +6,10 @@ // Pipeline: // 1. Read package.json#ucp.{specVersion, specBaseUrl}; UCP_SPEC_BASE_URL // env override wins over package.json -// 2. Pre-fetch service.json and ucp.json; apply T3 + T5 in-memory (both -// TEMPORARY — see below) +// 2. Pre-fetch ucp.json; apply T5 in-memory (TEMPORARY — see below) // 3. $RefParser.dereference against entry URL, with a custom resolve.http -// resolver that serves the mutated service.json + ucp.json from memory -// and passes every other URL through to fetch() +// resolver that serves the mutated ucp.json from memory and passes every +// other URL through to fetch() // 4. Apply T2 injectObjectType() — bundle-level, permanent // 5. Apply T4 openAdditionalProperties() — bundle-level, permanent // 6. For each of [platform_profile, business_profile]: @@ -23,15 +22,15 @@ // (Earlier file-form pipeline needed T1 stripIds() to paper over a source- // form `$id` vs filesystem-relative `$ref` mismatch; that's gone now.) // -// Why T3/T5 run against their source documents before dereference: after -// deref, named $defs markers are lost and look-alike branches become hard -// to disambiguate. Mutating the source document by name beforehand is -// robust and obvious. +// Why T5 runs against its source document before dereference: after deref, +// named $defs markers are lost and look-alike branches become hard to +// disambiguate. Mutating the source document by name beforehand is robust +// and obvious. (T3 was the sibling pattern for platform_schema; removed +// once the upstream spec fix landed at this specVersion.) // // Version paths under // are frozen-by-convention: BC and -// non-BC fixes both land at a new version path. T3/T5 self-destruct -// triggers are therefore engineer-driven (bump specVersion), never -// spontaneous. +// non-BC fixes both land at a new version path. T5's self-destruct trigger +// is therefore engineer-driven (bump specVersion), never spontaneous. // // Run via: pnpm gen:schemas // CI drift gate: `pnpm gen:schemas && git diff --exit-code src/core/generated/` @@ -73,24 +72,21 @@ async function main() { const specBaseUrl = (process.env.UCP_SPEC_BASE_URL ?? pkg.ucp.specBaseUrl).replace(/\/$/, '') const entryUrl = `${specBaseUrl}/${specVersion}/schemas/discovery/profile.json` - const serviceUrl = `${specBaseUrl}/${specVersion}/schemas/service.json` const ucpUrl = `${specBaseUrl}/${specVersion}/schemas/ucp.json` console.log(`▸ spec version: ${specVersion}`) console.log(`▸ spec base: ${specBaseUrl}`) console.log(`▸ entry: ${entryUrl}`) - // T3 + T5 (TEMPORARY): pre-fetch source documents and mutate in memory. - // The custom resolver below serves these mutated copies to ref-parser - // when it asks for serviceUrl / ucpUrl; everything else passes through - // to fetch(). - const serviceDoc = await fetchJson(serviceUrl) - const t3Touched = relaxPlatformEndpointRequirementInPlace(serviceDoc) - // touched===0 is the self-destruct trigger: the upstream spec fix has landed - // at this specVersion, so T3 is no longer needed. The error string below is - // the engineer's removal checklist. - if (t3Touched === 0) throw t3SelfDestructError() - + // T5 (TEMPORARY): pre-fetch the ucp.json document and mutate in memory. + // The custom resolver below serves the mutated copy to ref-parser when it + // asks for ucpUrl; everything else passes through to fetch(). + // + // T3 was a sibling transform against service.json's platform_schema; the + // upstream spec fix landed at this specVersion, the drift gate caught it, + // and the transform was removed per its self-destruct checklist. If a + // future temporary transform on service.json is needed, re-introduce the + // pre-fetch + resolver short-circuit alongside it. const ucpDoc = await fetchJson(ucpUrl) const t5Touched = relaxBusinessPaymentHandlersRequirementInPlace(ucpDoc) if (t5Touched === 0) throw t5SelfDestructError() @@ -101,7 +97,6 @@ async function main() { order: 1, canRead: /^https?:/i, async read(file: { url: string }) { - if (file.url === serviceUrl) return JSON.stringify(serviceDoc) if (file.url === ucpUrl) return JSON.stringify(ucpDoc) return await fetchText(file.url) }, @@ -116,9 +111,6 @@ async function main() { injectObjectType(dereffed) // T2 openAdditionalProperties(dereffed) // T4 - console.warn( - `⚠ T3 (TEMPORARY): relaxed platform_schema endpoint requirement on ${t3Touched} branch(es). Remove this transform once the spec PR fixing the over-strict endpoint requirement on the platform_schema branch lands and specVersion is bumped to that version.`, - ) console.warn( `⚠ T5 (TEMPORARY): relaxed business_schema payment_handlers requirement on ${t5Touched} branch(es). Remove this transform once the spec PR making payment_handlers optional on business_schema lands and specVersion is bumped to that version.`, ) @@ -126,7 +118,6 @@ async function main() { await mkdir(OUT_DIR, { recursive: true }) const transformsApplied = [ 'T2 injectObjectType', - 'T3 relaxPlatformEndpointRequirement (TEMPORARY)', 'T4 openAdditionalProperties', 'T5 relaxBusinessPaymentHandlersRequirement (TEMPORARY)', ] @@ -236,68 +227,6 @@ function openAdditionalProperties(node: JsonNode): void { } } -// T3 — relaxPlatformEndpointRequirementInPlace ⚠ TEMPORARY ⚠ -// -// The canonical service.json#/$defs/platform_schema requires `endpoint` on -// the `rest` and `mcp` anyOf branches. That rejects platform-side profiles -// that have no hosted endpoint to advertise (consumer-only agents, own- -// platform deployments). The spec's own prose example in overview.md -// disagrees with the schema (no endpoint there either) — i.e. this is a -// schema bug awaiting an upstream fix. -// -// This transform reaches into the in-memory service.json document, finds -// platform_schema's anyOf branches by name, and removes `endpoint` from -// `required` on the rest and mcp transports. The `schema` requirement is -// preserved (it's load-bearing for spec docs). The a2a branch keeps -// `endpoint` required (a2a is genuinely callable). Operating on the -// service.json document by name BEFORE dereference is robust: post- -// dereference, the platform_schema marker is lost and rest/mcp branches -// under platform vs business become hard to disambiguate. -// -// REMOVE THIS TRANSFORM once the spec PR fixing the platform_schema -// endpoint requirement lands at a published specVersion and we bump to it. -// The drift-gate CI step will catch the resulting regen. -function relaxPlatformEndpointRequirementInPlace(obj: Record): number { - const platformSchema = (obj?.$defs as Record | undefined)?.platform_schema - if (!isObject(platformSchema)) { - throw new Error('T3: service.json#/$defs/platform_schema not found — schema layout changed?') - } - let touched = 0 - for (const piece of asArray(platformSchema.allOf)) { - if (!isObject(piece)) continue - for (const branch of asArray(piece.anyOf)) { - if (!isObject(branch)) continue - const transport = isObject(branch.properties) - ? (branch.properties.transport as Record | undefined)?.const - : undefined - if (transport !== 'rest' && transport !== 'mcp') continue - if (!Array.isArray(branch.required)) continue - const required: unknown[] = branch.required - const before = required.length - const after = required.filter((r) => r !== 'endpoint') - branch.required = after - if (after.length < before) touched++ - } - } - return touched -} - -function t3SelfDestructError(): Error { - return new Error( - 'T3 (relaxPlatformEndpointRequirementInPlace): expected to relax 2 platform_schema ' + - 'branches (rest + mcp) but matched 0. Likely the upstream spec fix has landed at this ' + - 'specVersion. To remove this transform:\n' + - ' 1. scripts/codegen-schemas.ts: delete relaxPlatformEndpointRequirementInPlace() and\n' + - ' its call site in main() (look for `// T3 (TEMPORARY)`).\n' + - ' 2. scripts/codegen-schemas.ts: simplify the resolve.http resolver — remove the\n' + - ' `if (file.url === serviceUrl)` short-circuit; fetch every URL via fetchText.\n' + - ' 3. scripts/codegen-schemas.ts: drop the T3 entry from transformsApplied[] and the\n' + - ' t3Touched warn log; drop t3SelfDestructError().\n' + - ' 4. scripts/codegen-schemas.ts: drop the T3 line in banner()/T3 notice block.\n' + - ' 5. Re-run `pnpm gen:schemas` and commit the updated src/core/generated/.', - ) -} - // T5 — relaxBusinessPaymentHandlersRequirementInPlace ⚠ TEMPORARY ⚠ // // The canonical ucp.json#/$defs/business_schema requires `payment_handlers` @@ -364,11 +293,6 @@ function isObject(x: unknown): x is Record { function banner(entryUrl: string, specVersion: string, transforms: string[]): string { const temporary: string[] = [] - if (transforms.some((t) => t.startsWith('T3'))) { - temporary.push( - '// - T3 relaxPlatformEndpointRequirement — pending upstream fix for over-strict\n// endpoint requirement on platform_schema rest/mcp branches.', - ) - } if (transforms.some((t) => t.startsWith('T5'))) { temporary.push( '// - T5 relaxBusinessPaymentHandlersRequirement — pending upstream fix making\n// payment_handlers optional on business_schema (read-only catalogs do not have one).', diff --git a/src/core/generated/business_profile.zod.ts b/src/core/generated/business_profile.zod.ts index 3630a7f..46d9eb1 100644 --- a/src/core/generated/business_profile.zod.ts +++ b/src/core/generated/business_profile.zod.ts @@ -6,13 +6,10 @@ // // Transforms applied: // - T2 injectObjectType -// - T3 relaxPlatformEndpointRequirement (TEMPORARY) // - T4 openAdditionalProperties // - T5 relaxBusinessPaymentHandlersRequirement (TEMPORARY) // // ⚠ INCLUDES TEMPORARY TRANSFORM(S): -// - T3 relaxPlatformEndpointRequirement — pending upstream fix for over-strict -// endpoint requirement on platform_schema rest/mcp branches. // - T5 relaxBusinessPaymentHandlersRequirement — pending upstream fix making // payment_handlers optional on business_schema (read-only catalogs do not have one). // diff --git a/src/core/generated/platform_profile.zod.ts b/src/core/generated/platform_profile.zod.ts index 8f93baf..2d96f79 100644 --- a/src/core/generated/platform_profile.zod.ts +++ b/src/core/generated/platform_profile.zod.ts @@ -6,13 +6,10 @@ // // Transforms applied: // - T2 injectObjectType -// - T3 relaxPlatformEndpointRequirement (TEMPORARY) // - T4 openAdditionalProperties // - T5 relaxBusinessPaymentHandlersRequirement (TEMPORARY) // // ⚠ INCLUDES TEMPORARY TRANSFORM(S): -// - T3 relaxPlatformEndpointRequirement — pending upstream fix for over-strict -// endpoint requirement on platform_schema rest/mcp branches. // - T5 relaxBusinessPaymentHandlersRequirement — pending upstream fix making // payment_handlers optional on business_schema (read-only catalogs do not have one). // From 5f587061522695b604439ef6c76074323d844088 Mon Sep 17 00:00:00 2001 From: Ilya Grigorik Date: Thu, 21 May 2026 09:15:40 -0700 Subject: [PATCH 2/2] remove T5 business_schema payment_handlers patch Drop the support for missing payment_handler workaround; align to UCP spec 'required' semantics. If a business wants to offer read-only contract (e.g. only access to catalog), then per spec they should still advertise payment_handlers but can return empty object. --- scripts/codegen-schemas.ts | 138 ++++----------------- src/core/generated/business_profile.zod.ts | 7 +- src/core/generated/platform_profile.zod.ts | 5 - 3 files changed, 23 insertions(+), 127 deletions(-) diff --git a/scripts/codegen-schemas.ts b/scripts/codegen-schemas.ts index 1f854c5..dfbcd31 100644 --- a/scripts/codegen-schemas.ts +++ b/scripts/codegen-schemas.ts @@ -6,15 +6,18 @@ // Pipeline: // 1. Read package.json#ucp.{specVersion, specBaseUrl}; UCP_SPEC_BASE_URL // env override wins over package.json -// 2. Pre-fetch ucp.json; apply T5 in-memory (TEMPORARY — see below) -// 3. $RefParser.dereference against entry URL, with a custom resolve.http -// resolver that serves the mutated ucp.json from memory and passes every -// other URL through to fetch() -// 4. Apply T2 injectObjectType() — bundle-level, permanent -// 5. Apply T4 openAdditionalProperties() — bundle-level, permanent -// 6. For each of [platform_profile, business_profile]: +// 2. $RefParser.dereference against entry URL, with a pass-through +// resolve.http resolver (every URL goes through fetch()). +// 3. Apply T2 injectObjectType() — bundle-level, permanent +// 4. Apply T4 openAdditionalProperties() — bundle-level, permanent +// 5. For each of [platform_profile, business_profile]: // jsonSchemaToZod → write to src/core/generated/.zod.ts -// 7. Stamp each output with a header banner pointing back here +// 6. Stamp each output with a header banner pointing back here +// +// No temporary transforms are active at the current specVersion. The T3 and T5 +// patterns (pre-fetch source doc + mutate by name + serve mutated copy through +// a resolve.http short-circuit) were removed once upstream stabilized at this +// version. Reintroduce the same shape if a future temporary fix is needed. // // Published artifacts at `//schemas/...` carry absolute, // version-prefixed `$id` URLs that match the absolute fetch paths — refs @@ -22,15 +25,10 @@ // (Earlier file-form pipeline needed T1 stripIds() to paper over a source- // form `$id` vs filesystem-relative `$ref` mismatch; that's gone now.) // -// Why T5 runs against its source document before dereference: after deref, -// named $defs markers are lost and look-alike branches become hard to -// disambiguate. Mutating the source document by name beforehand is robust -// and obvious. (T3 was the sibling pattern for platform_schema; removed -// once the upstream spec fix landed at this specVersion.) -// // Version paths under // are frozen-by-convention: BC and -// non-BC fixes both land at a new version path. T5's self-destruct trigger -// is therefore engineer-driven (bump specVersion), never spontaneous. +// non-BC fixes both land at a new version path. Temporary-transform self- +// destruct triggers (when reintroduced) are therefore engineer-driven (bump +// specVersion), never spontaneous. // // Run via: pnpm gen:schemas // CI drift gate: `pnpm gen:schemas && git diff --exit-code src/core/generated/` @@ -72,32 +70,23 @@ async function main() { const specBaseUrl = (process.env.UCP_SPEC_BASE_URL ?? pkg.ucp.specBaseUrl).replace(/\/$/, '') const entryUrl = `${specBaseUrl}/${specVersion}/schemas/discovery/profile.json` - const ucpUrl = `${specBaseUrl}/${specVersion}/schemas/ucp.json` console.log(`▸ spec version: ${specVersion}`) console.log(`▸ spec base: ${specBaseUrl}`) console.log(`▸ entry: ${entryUrl}`) - // T5 (TEMPORARY): pre-fetch the ucp.json document and mutate in memory. - // The custom resolver below serves the mutated copy to ref-parser when it - // asks for ucpUrl; everything else passes through to fetch(). - // - // T3 was a sibling transform against service.json's platform_schema; the - // upstream spec fix landed at this specVersion, the drift gate caught it, - // and the transform was removed per its self-destruct checklist. If a - // future temporary transform on service.json is needed, re-introduce the - // pre-fetch + resolver short-circuit alongside it. - const ucpDoc = await fetchJson(ucpUrl) - const t5Touched = relaxBusinessPaymentHandlersRequirementInPlace(ucpDoc) - if (t5Touched === 0) throw t5SelfDestructError() - + // No temporary transforms applied at the current specVersion. Both T3 + // (platform_schema endpoint) and T5 (business_schema payment_handlers) were + // removed once the upstream picture stabilized at this version. If a future + // temporary fix is needed, re-introduce the pre-fetch + resolver short- + // circuit pattern: fetch the source document, mutate by name, and serve the + // mutated copy back through a custom resolve.http reader. const dereffed = (await $RefParser.dereference(entryUrl, { resolve: { http: { order: 1, canRead: /^https?:/i, async read(file: { url: string }) { - if (file.url === ucpUrl) return JSON.stringify(ucpDoc) return await fetchText(file.url) }, }, @@ -111,16 +100,8 @@ async function main() { injectObjectType(dereffed) // T2 openAdditionalProperties(dereffed) // T4 - console.warn( - `⚠ T5 (TEMPORARY): relaxed business_schema payment_handlers requirement on ${t5Touched} branch(es). Remove this transform once the spec PR making payment_handlers optional on business_schema lands and specVersion is bumped to that version.`, - ) - await mkdir(OUT_DIR, { recursive: true }) - const transformsApplied = [ - 'T2 injectObjectType', - 'T4 openAdditionalProperties', - 'T5 relaxBusinessPaymentHandlersRequirement (TEMPORARY)', - ] + const transformsApplied = ['T2 injectObjectType', 'T4 openAdditionalProperties'] for (const branch of BRANCHES) { const subSchema = (dereffed.$defs as Record)?.[branch.def] @@ -158,10 +139,6 @@ async function fetchText(url: string): Promise { return await r.text() } -async function fetchJson(url: string): Promise> { - return JSON.parse(await fetchText(url)) -} - // ─── transforms ─────────────────────────────────────────────────────────── // T2 — injectObjectType (permanent) @@ -227,80 +204,9 @@ function openAdditionalProperties(node: JsonNode): void { } } -// T5 — relaxBusinessPaymentHandlersRequirementInPlace ⚠ TEMPORARY ⚠ -// -// The canonical ucp.json#/$defs/business_schema requires `payment_handlers` -// on every business profile. That rejects legitimate read-only business -// profiles (browse/search-only catalogs, lookup endpoints, content-only -// integrations) that have no checkout flow to declare. Concrete example: -// catalog.shopify.com publishes a search/lookup business profile with no -// payment_handlers and is rejected by our discovery validation. -// -// This transform reaches into the in-memory ucp.json document, finds the -// business_schema allOf piece that carries the `required` array, and -// removes `payment_handlers`. The `services` requirement is preserved — -// a business with no services declared is not useful. Operating on -// ucp.json by name BEFORE dereference is robust: post-dereference, the -// business_schema/platform_schema marker is lost and per-flavor required -// arrays become hard to disambiguate. -// -// REMOVE THIS TRANSFORM once the spec PR making payment_handlers optional -// on business_schema lands at a published specVersion and we bump to it. -// The drift-gate CI step will catch the resulting regen. -function relaxBusinessPaymentHandlersRequirementInPlace(obj: Record): number { - const businessSchema = (obj?.$defs as Record | undefined)?.business_schema - if (!isObject(businessSchema)) { - throw new Error('T5: ucp.json#/$defs/business_schema not found — schema layout changed?') - } - let touched = 0 - for (const piece of asArray(businessSchema.allOf)) { - if (!isObject(piece)) continue - if (!Array.isArray(piece.required)) continue - const required: unknown[] = piece.required - const before = required.length - const after = required.filter((r) => r !== 'payment_handlers') - piece.required = after - if (after.length < before) touched++ - } - return touched -} - -function t5SelfDestructError(): Error { - return new Error( - 'T5 (relaxBusinessPaymentHandlersRequirementInPlace): expected to relax 1 business_schema ' + - 'allOf branch but matched 0. Likely the upstream spec fix has landed at this specVersion. ' + - 'To remove this transform:\n' + - ' 1. scripts/codegen-schemas.ts: delete relaxBusinessPaymentHandlersRequirementInPlace()\n' + - ' and its call site in main() (look for `// T5`).\n' + - ' 2. scripts/codegen-schemas.ts: simplify the resolve.http resolver — remove the\n' + - ' `if (file.url === ucpUrl)` short-circuit; drop the ucpDoc pre-fetch.\n' + - ' 3. scripts/codegen-schemas.ts: drop the T5 entry from transformsApplied[] and the\n' + - ' t5Touched warn log; drop t5SelfDestructError().\n' + - ' 4. scripts/codegen-schemas.ts: drop the T5 line in banner()/T5 notice block.\n' + - ' 5. Re-run `pnpm gen:schemas` and commit the updated src/core/generated/.', - ) -} - -function asArray(x: unknown): unknown[] { - return Array.isArray(x) ? x : [] -} - -function isObject(x: unknown): x is Record { - return typeof x === 'object' && x !== null && !Array.isArray(x) -} - // ─── output banner ──────────────────────────────────────────────────────── function banner(entryUrl: string, specVersion: string, transforms: string[]): string { - const temporary: string[] = [] - if (transforms.some((t) => t.startsWith('T5'))) { - temporary.push( - '// - T5 relaxBusinessPaymentHandlersRequirement — pending upstream fix making\n// payment_handlers optional on business_schema (read-only catalogs do not have one).', - ) - } - const tempNotice = temporary.length - ? `//\n// ⚠ INCLUDES TEMPORARY TRANSFORM(S):\n${temporary.join('\n')}\n` - : '' return `// AUTOGENERATED — DO NOT EDIT. // // Generated by scripts/codegen-schemas.ts from UCP spec at: @@ -309,7 +215,7 @@ function banner(entryUrl: string, specVersion: string, transforms: string[]): st // // Transforms applied: ${transforms.map((t) => `// - ${t}`).join('\n')} -${tempNotice}// +// // To regenerate: pnpm gen:schemas // CI drift gate: any uncommitted change here fails the build. ` diff --git a/src/core/generated/business_profile.zod.ts b/src/core/generated/business_profile.zod.ts index 46d9eb1..fc3d4ad 100644 --- a/src/core/generated/business_profile.zod.ts +++ b/src/core/generated/business_profile.zod.ts @@ -7,11 +7,6 @@ // Transforms applied: // - T2 injectObjectType // - T4 openAdditionalProperties -// - T5 relaxBusinessPaymentHandlersRequirement (TEMPORARY) -// -// ⚠ INCLUDES TEMPORARY TRANSFORM(S): -// - T5 relaxBusinessPaymentHandlersRequirement — pending upstream fix making -// payment_handlers optional on business_schema (read-only catalogs do not have one). // // To regenerate: pnpm gen:schemas // CI drift gate: any uncommitted change here fails the build. @@ -82,6 +77,6 @@ export const businessProfileSchema = z.intersection(z.object({ "ucp": z.object({ message: "Invalid input: Should pass single schema. Passed " + passed, }); } - }).describe("Parent capability(s) this extends. Present for extensions, absent for root capabilities. Use array for multi-parent extensions.").optional() }).catchall(z.any())))).describe("Capability registry keyed by reverse-domain name.").optional(), "payment_handlers": z.record(z.string(), z.array(z.intersection(z.object({ "version": z.string().regex(new RegExp("^\\d{4}-\\d{2}-\\d{2}$")).describe("UCP version in YYYY-MM-DD format."), "spec": z.string().url().describe("URL to human-readable specification document.").optional(), "schema": z.string().url().describe("URL to JSON Schema defining this entity's structure and payloads.").optional(), "id": z.string().describe("Unique identifier for this entity instance. Used to disambiguate when multiple instances exist.").optional(), "config": z.record(z.string(), z.any()).describe("Entity-specific configuration. Structure defined by each entity's schema.").optional() }).catchall(z.any()).describe("Shared foundation for all UCP entities."), z.intersection(z.record(z.string(), z.any()), z.object({ "available_instruments": z.array(z.object({ "type": z.string().describe("The instrument type identifier (e.g., 'card', 'gift_card'). References an instrument schema's type constant."), "constraints": z.record(z.string(), z.any()).describe("Constraints on this instrument type. Structure depends on instrument type and active capabilities.").optional() }).catchall(z.any()).describe("An instrument type available from a payment handler with optional constraints.")).min(1).describe("Instrument types this handler supports, with optional constraints. When absent, every instrument should be considered available.").optional() }).catchall(z.any()))))).describe("Payment handler registry keyed by reverse-domain name.").optional() }).catchall(z.any()).describe("Base UCP metadata with shared properties for all schema types."), z.object({ "supported_versions": z.record(z.string(), z.string().url()).describe("Previous protocol versions this business supports, mapped to profile URIs. Businesses that support older protocol versions SHOULD advertise each version and link to its profile. Each URI points to a complete, self-contained profile for that version. When omitted, only `version` is supported.").optional(), "services": z.any(), "capabilities": z.any().optional(), "payment_handlers": z.any().optional() }).catchall(z.any())).describe("UCP metadata for business/merchant-level configuration. Subset of platform schema with business-specific settings.").optional() }).catchall(z.any())).describe("Discovery profile for businesses/merchants. Subset of platform profile with business-specific configuration.") + }).describe("Parent capability(s) this extends. Present for extensions, absent for root capabilities. Use array for multi-parent extensions.").optional() }).catchall(z.any())))).describe("Capability registry keyed by reverse-domain name.").optional(), "payment_handlers": z.record(z.string(), z.array(z.intersection(z.object({ "version": z.string().regex(new RegExp("^\\d{4}-\\d{2}-\\d{2}$")).describe("UCP version in YYYY-MM-DD format."), "spec": z.string().url().describe("URL to human-readable specification document.").optional(), "schema": z.string().url().describe("URL to JSON Schema defining this entity's structure and payloads.").optional(), "id": z.string().describe("Unique identifier for this entity instance. Used to disambiguate when multiple instances exist.").optional(), "config": z.record(z.string(), z.any()).describe("Entity-specific configuration. Structure defined by each entity's schema.").optional() }).catchall(z.any()).describe("Shared foundation for all UCP entities."), z.intersection(z.record(z.string(), z.any()), z.object({ "available_instruments": z.array(z.object({ "type": z.string().describe("The instrument type identifier (e.g., 'card', 'gift_card'). References an instrument schema's type constant."), "constraints": z.record(z.string(), z.any()).describe("Constraints on this instrument type. Structure depends on instrument type and active capabilities.").optional() }).catchall(z.any()).describe("An instrument type available from a payment handler with optional constraints.")).min(1).describe("Instrument types this handler supports, with optional constraints. When absent, every instrument should be considered available.").optional() }).catchall(z.any()))))).describe("Payment handler registry keyed by reverse-domain name.").optional() }).catchall(z.any()).describe("Base UCP metadata with shared properties for all schema types."), z.object({ "supported_versions": z.record(z.string(), z.string().url()).describe("Previous protocol versions this business supports, mapped to profile URIs. Businesses that support older protocol versions SHOULD advertise each version and link to its profile. Each URI points to a complete, self-contained profile for that version. When omitted, only `version` is supported.").optional(), "services": z.any(), "capabilities": z.any().optional(), "payment_handlers": z.any() }).catchall(z.any())).describe("UCP metadata for business/merchant-level configuration. Subset of platform schema with business-specific settings.").optional() }).catchall(z.any())).describe("Discovery profile for businesses/merchants. Subset of platform profile with business-specific configuration.") export type BusinessProfile = z.infer diff --git a/src/core/generated/platform_profile.zod.ts b/src/core/generated/platform_profile.zod.ts index 2d96f79..8b9a738 100644 --- a/src/core/generated/platform_profile.zod.ts +++ b/src/core/generated/platform_profile.zod.ts @@ -7,11 +7,6 @@ // Transforms applied: // - T2 injectObjectType // - T4 openAdditionalProperties -// - T5 relaxBusinessPaymentHandlersRequirement (TEMPORARY) -// -// ⚠ INCLUDES TEMPORARY TRANSFORM(S): -// - T5 relaxBusinessPaymentHandlersRequirement — pending upstream fix making -// payment_handlers optional on business_schema (read-only catalogs do not have one). // // To regenerate: pnpm gen:schemas // CI drift gate: any uncommitted change here fails the build.