From 33ae4d92ef4f30767ade6b595736012ed12f971f Mon Sep 17 00:00:00 2001 From: Elizabeth Maher Date: Fri, 5 Jun 2026 16:06:01 -0700 Subject: [PATCH 1/4] fix: strip schemaId/typeName from operation PATCH during reconciliation After spec import, APIM assigns its own auto-generated schema IDs to operation request/response representations. The reconciliation PATCH was sending the source instance's schema IDs, which don't exist on the target. APIM silently drops schemaId/typeName when the referenced schema doesn't exist, causing the compare phase to report missing fields. By stripping these fields from the PATCH body, APIM retains the schema references it assigned during its own spec import. The compare script already normalizes auto-generated hex IDs, so both sides match. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/services/api-publisher.ts | 48 +++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/services/api-publisher.ts b/src/services/api-publisher.ts index e7a23c3..01a663e 100644 --- a/src/services/api-publisher.ts +++ b/src/services/api-publisher.ts @@ -434,6 +434,13 @@ async function reconcileOperationsAfterSpecImport( } } + // Strip schemaId/typeName from representations in request/responses. + // These reference source-specific auto-generated schema IDs that won't exist + // on the target after a fresh spec import. APIM re-links representations to + // its own schema IDs during import, so sending stale source IDs causes APIM + // to silently drop the fields. + stripRepresentationSchemaRefs(patchProps); + if (Object.keys(patchProps).length === 0) return; const patchBody: Record = { properties: patchProps }; @@ -456,6 +463,47 @@ async function reconcileOperationsAfterSpecImport( } } +/** + * Strip schemaId and typeName from all representations in request/responses. + * After a spec import, APIM assigns its own auto-generated schema IDs to + * operation representations. The persisted JSON references the *source* instance's + * schema IDs, which don't exist on the target. Sending them in a PATCH causes APIM + * to silently drop the fields. By stripping them, we let APIM keep its own + * freshly-assigned schema references intact while still reconciling other metadata + * (displayName, description, templateParameters, etc.). + */ +function stripRepresentationSchemaRefs(patchProps: Record): void { + const SCHEMA_REF_FIELDS = ['schemaId', 'typeName']; + + function stripFromRepresentations(representations: unknown): void { + if (!Array.isArray(representations)) return; + for (const rep of representations) { + if (rep && typeof rep === 'object') { + for (const field of SCHEMA_REF_FIELDS) { + delete (rep as Record)[field]; + } + } + } + } + + // Strip from request.representations + const request = patchProps.request; + if (request && typeof request === 'object') { + const req = request as Record; + stripFromRepresentations(req.representations); + } + + // Strip from responses[].representations + const responses = patchProps.responses; + if (Array.isArray(responses)) { + for (const response of responses) { + if (response && typeof response === 'object') { + stripFromRepresentations((response as Record).representations); + } + } + } +} + /** * Extract revision number from API name (e.g., "my-api;rev=2" -> 2) */ From 40f213136f2d37e2a345f9b65d298f8531ae97fc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 5 Jun 2026 23:13:40 +0000 Subject: [PATCH 2/4] chore: make reconciliation comments terse --- src/services/api-publisher.ts | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/src/services/api-publisher.ts b/src/services/api-publisher.ts index 01a663e..bab8de3 100644 --- a/src/services/api-publisher.ts +++ b/src/services/api-publisher.ts @@ -434,11 +434,7 @@ async function reconcileOperationsAfterSpecImport( } } - // Strip schemaId/typeName from representations in request/responses. - // These reference source-specific auto-generated schema IDs that won't exist - // on the target after a fresh spec import. APIM re-links representations to - // its own schema IDs during import, so sending stale source IDs causes APIM - // to silently drop the fields. + // Drop source schema refs; APIM rebinds these on spec import. stripRepresentationSchemaRefs(patchProps); if (Object.keys(patchProps).length === 0) return; @@ -463,15 +459,7 @@ async function reconcileOperationsAfterSpecImport( } } -/** - * Strip schemaId and typeName from all representations in request/responses. - * After a spec import, APIM assigns its own auto-generated schema IDs to - * operation representations. The persisted JSON references the *source* instance's - * schema IDs, which don't exist on the target. Sending them in a PATCH causes APIM - * to silently drop the fields. By stripping them, we let APIM keep its own - * freshly-assigned schema references intact while still reconciling other metadata - * (displayName, description, templateParameters, etc.). - */ +/** Remove schema refs from request/response representations before PATCH. */ function stripRepresentationSchemaRefs(patchProps: Record): void { const SCHEMA_REF_FIELDS = ['schemaId', 'typeName']; @@ -486,14 +474,14 @@ function stripRepresentationSchemaRefs(patchProps: Record): voi } } - // Strip from request.representations + // request.representations const request = patchProps.request; if (request && typeof request === 'object') { const req = request as Record; stripFromRepresentations(req.representations); } - // Strip from responses[].representations + // responses[].representations const responses = patchProps.responses; if (Array.isArray(responses)) { for (const response of responses) { From 37babf0375bda10fffe1f4fa07cc8751d389e336 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 5 Jun 2026 23:14:31 +0000 Subject: [PATCH 3/4] docs: tighten reconciliation comments --- src/services/api-publisher.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/services/api-publisher.ts b/src/services/api-publisher.ts index bab8de3..72bb736 100644 --- a/src/services/api-publisher.ts +++ b/src/services/api-publisher.ts @@ -434,7 +434,7 @@ async function reconcileOperationsAfterSpecImport( } } - // Drop source schema refs; APIM rebinds these on spec import. + // Strip source schema refs; APIM rebinds on import and drops stale IDs. stripRepresentationSchemaRefs(patchProps); if (Object.keys(patchProps).length === 0) return; @@ -459,7 +459,7 @@ async function reconcileOperationsAfterSpecImport( } } -/** Remove schema refs from request/response representations before PATCH. */ +/** Strip source schema refs from request/response representations before PATCH. */ function stripRepresentationSchemaRefs(patchProps: Record): void { const SCHEMA_REF_FIELDS = ['schemaId', 'typeName']; @@ -474,14 +474,14 @@ function stripRepresentationSchemaRefs(patchProps: Record): voi } } - // request.representations + // Strip schema refs from request.representations. const request = patchProps.request; if (request && typeof request === 'object') { const req = request as Record; stripFromRepresentations(req.representations); } - // responses[].representations + // Strip schema refs from responses[].representations. const responses = patchProps.responses; if (Array.isArray(responses)) { for (const response of responses) { From 20c8eb92f2d901af51be5b7829672402734e1b9b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 5 Jun 2026 23:37:14 +0000 Subject: [PATCH 4/4] docs(squad): record schema-ref reconciliation decision --- .squad/decisions.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.squad/decisions.md b/.squad/decisions.md index 6e575ee..bb6c42b 100644 --- a/.squad/decisions.md +++ b/.squad/decisions.md @@ -2,6 +2,12 @@ ## Active Decisions +### 2026-06-05T23:40:00Z: Operation reconciliation PATCH strips schema refs +**By:** ApimExpert + TypeScriptDev +**Status:** Implemented +**What:** `reconcileOperationsAfterSpecImport` removes `schemaId` and `typeName` from operation `request.representations` and `responses[].representations` before PATCH. +**Why:** Those IDs are source-instance specific. After target spec import, APIM assigns its own schema IDs; sending stale source IDs causes APIM to drop schema refs. + ### 2026-05-28T23:06:01Z: Team-Wide Evidence Standard **By:** User directive (anonymized) **Status:** Active directive