Skip to content

chore(contract)!: re-baseline the operation manifest to Apple API 4.5 - #31

Merged
zelentsov-dev merged 4 commits into
developfrom
chore/apple-spec-4.5
Sep 24, 2026
Merged

zelentsov-dev merged 4 commits into
developfrom
chore/apple-spec-4.5

Conversation

@zelentsov-dev

Copy link
Copy Markdown
Owner

Intent

Apple replaced the App Store Connect OpenAPI specification 4.4.1 with 4.5 between 2026-09-21 and 2026-09-23. The required check "Release & Apple Contract" downloads the live specification, so every PR, develop and main failed with 1,051 errors and no code change (CI run 35921838349). The errors were expired reviews, spec pin mismatches, one untriaged operation, and optional-input pin drift.

This PR re-baselines the operation manifest to 4.5 through a re-review backed by a 4.4.1 -> 4.5 diff. It also fixes the one public tool whose contract 4.5 breaks.

Method

  1. Recovered the pinned 4.4.1 document byte-for-byte from the Wayback Machine: snapshots 20260820205342 and 20260916204644 of Apple's zip, which are identical.

    • The zip's SHA-1 equals the Wayback CDX digest, and openapi.oas (2).json hashes to the pinned ed0202ef....
    • The develop manifest passes --strict against it with 0 errors and 0 warnings.
  2. Diffed 4.4.1 against the live 4.5 (1e8ef250...) at the level the manifest reasons about:

    • operation identity;
    • parameters and request-body inputs;
    • requiredness on every operation a public tool maps;
    • response-lineage pointers.

    A Python replica of the analyzer's optional-input enumeration reproduces the Swift identity digests for both specs, so its per-input results can be trusted.

  3. Re-stamped only items whose Apple definition is unchanged. Changed items were re-decided with a stated reason, and every new operation and input was triaged.

Changes

  • 67e95c6 chore(contract): re-baseline the operation manifest to Apple API 4.5
  • 261ef52 fix(subscriptions)!: require plan_types for adjusted equalizations
  • 7972e38 test(contract): move Apple 4.4.1 pins in tests and docs to 4.5
  • 44ec237 test(contract): count the three Apple 4.5 inputs in classification inventories

4.4.1 -> 4.5 diff summary

Item Total Re-stamped (unchanged) Re-decided (changed)
Exact optional-input classifications 644 644 0
sparseFields family inputs 1,139 1,029 110 enum-only deltas (below); the family reason still holds
Waivers 424 385 39: only referenced App/AppInfo/GameCenterDetail schemas and fields[...] enums changed; no public tool maps them, so the deferrals stand
Scope rules 4 2 2: /v1/gameCenter (+4 operations) and /v2/gameCenter (+2), still out of scope

The 110 family deltas:

  • fields[apps] +performanceOverviews
  • fields[subscriptions] +marketSettings, multiSeatStatus
  • fields[appInfos] -kidsAgeBand
  • fields[ageRatingDeclarations] +gracRatingClassificationNumber
  • fields[gameCenterDetails] +blockedPlayers

New 4.5 items:

  • apps_performanceOverviews_getToManyRelated (GET /v1/apps/{id}/performanceOverviews) is deferred with a new waiver.
  • The six new Game Center operations fall under the existing Game Center scope rules.
  • Three new optional inputs are classified as intentionally omitted: gracRatingClassificationNumber on app_versions_update_age_rating, and marketSettings and multiSeatStatus on subscriptions_update.

Requiredness: I compared all 476 operations that public tools map, in every role. The only transition is filter[planType] on subscriptionPricePoints_adjustedEqualizations_getToManyRelated, which went from optional to required. No parameter was added or removed, and no raw required array changed anywhere in a request-body schema closure. The detector was canary-tested with injected changes.

Contract fix (261ef52, Sources change). subscriptions_list_price_point_adjusted_equalizations published plan_types as optional and sent filter[planType] only when it was supplied, but 4.5 requires the filter.

  • plan_types is now required in the input schema.
  • A missing or empty value fails before any network request.
  • This is breaking for MCP clients and is recorded under ### Breaking Changes.
  • The sibling subscriptions_list_price_point_equalizations keeps plan_types optional because Apple keeps that filter optional.

Reason texts. 17 reviewed reasons (14 classifications, 3 waivers) cited the pinned Apple version and now cite 4.5. Each cited fact is identical in 4.4.1 and 4.5. The build encryption-declaration PATCH deprecation was also re-checked against current Apple DocC.

ASCCoverageInventory.appleAPIVersionBaseline moves to 4.5 (snapshot 2026-09-24). It is tied to the contract pin, not independent:

  • it was set to 4.4.1 together with the 4.4.1 contract hardening in 4ae6293;
  • it drives ASC-OPENAPI-COVERAGE-GENERATED.md, which CI diffs against the live specification;
  • its rules classify all 973 Apple 4.5 paths.

Pins and counts:

  • specPin: 4.5, 973 paths, 1,270 operations.
  • optionalInputCoveragePin: 2,907 total, 1,121 bound, 40 internally controlled, 1,746 intentionally omitted, 0 unclassified; identity 0ce3ff14568f841643559c359bc91827290535925c3665172fda81d499f81ce1.
  • Operations: 476 mapped, 425 deferred, 369 scope-rule out of scope.

Risk and authority

  • Change class: Apple contract re-baseline plus one breaking tool-input fix
  • Deployment authority: no-deploy. The target is develop; release, tag, and Mint publishing stay with the owner.

Evidence

  • Source. An identity-aligned manifest diff against fcfa12f shows exactly the intended changes and nothing else: 1,073 stamps, 17 reason version updates, 4 specPin fields, 1 waiver, 3 classifications, and the coverage pin. The diff scripts and data are replayable from the review handoff.
  • Local, head 44ec237:
    • swift test --disable-automatic-resolution: 1,868 tests in 138 suites passed.
    • The ci.yml "Release & Apple Contract" steps on a clean git archive of the head all passed:
      • release build with 0 warnings;
      • asc-mcp --version returns asc-mcp 4.1.6;
      • coverage smoke;
      • live spec zip 4dc5dc79... / document 1e8ef250...;
      • openapi-contract-check --strict with 0 errors, 0 warnings, 0 drift, and the relocated-binary --strict step;
      • generated coverage diff is clean.
  • CI: runs on this PR.

Follow-ups (not in this PR)

  • Gate gap. openapi-contract-check checks only that a required Apple input is bound, not that the tool always sends it. Evidence: a scratch manifest with every stamp re-baselined passed --strict on 4.5 (0 errors, 0 warnings, 0 drift) while plan_types was still optional. Suggested rule: a required Apple parameter or body pointer must bind to a required MCP input or to a fixed or derived value.
  • app_versions_update_age_rating rejects the Apple 4.5 koreaAgeRatingOverride values ALL and TWELVE_PLUS (AppLifecycleWorker+Handlers.swift:1493, AppLifecycleWorker+ToolDefinitions.swift:611).
  • Apple 4.5 removed AppInfo.kidsAgeBand. The app_info projections still emit it, so it will read as null (AppInfoWorker+Handlers.swift:738).
  • Tool descriptions and implementation notes that cite Apple 4.4.1 as the as-built reference are unchanged. One example is the promoted image tools' "absent from pinned executable OpenAPI 4.4.1", which is still true for 4.5.
  • The README says "The Apple 4.5 pin". Relabel it with the release version at release time. The Breaking Changes entry implies a major version under this repository's SemVer history; that is the owner's decision.

Not checked

  • Live App Store Connect behavior without filter[planType]. Live ASC calls were out of scope, so the fix follows Apple's published 4.5 contract.
  • Intermediate commits are not individually green: 67e95c6 and 261ef52 fail the tests that pin 4.4.1 values until 7972e38 and 44ec237. The head is green.
  • GitHub Actions results for this PR at the time of writing.

Apple replaced App Store Connect OpenAPI 4.4.1 with 4.5 between 2026-09-21
and 2026-09-23, so the strict contract gate failed on every branch with
1,051 expired-review and pin errors and no code change.

The pinned 4.4.1 document was recovered byte-for-byte from the Wayback
Machine (snapshots 20260820205342 and 20260916204644 of Apple's zip: the
zip SHA-1 equals the CDX digest and openapi.oas (2).json hashes to the
pinned ed0202ef...). The previous manifest passes --strict against it with
0 errors and 0 warnings. Every stamp below is backed by a 4.4.1 -> 4.5 diff
of operations, parameters, request-body inputs, requiredness, and response
lineage:

- specPin: 4.5, 973 paths, 1,270 operations, SHA-256 1e8ef250...
- 644 exact optional-input classifications re-stamped: every classified
  Apple input is unchanged, including its referenced schemas and text.
- sparseFields family rule re-decided: 110 of its 1,139 inputs changed only
  in enum values (fields[apps] +performanceOverviews, fields[subscriptions]
  +marketSettings/multiSeatStatus, fields[ageRatingDeclarations]
  +gracRatingClassificationNumber, fields[gameCenterDetails]
  +blockedPlayers, fields[appInfos] -kidsAgeBand). No new fields[...] input
  reached a mapped operation, and public tools still expose documented
  projections instead of caller-controlled sparse fieldsets.
- 424 waivers: 385 unchanged; 39 re-decided because only referenced App,
  AppInfo, or GameCenterDetail schemas and fields[...] enums changed. No
  public tool maps these operations, so each deferral stands.
- Scope rules: /v1/alternativeDistribution and /v1/marketplace unchanged;
  /v1/gameCenter (+4 operations) and /v2/gameCenter (+2 operations)
  re-decided as still out of scope.
- New operation apps_performanceOverviews_getToManyRelated is deferred.
- New optional inputs are classified as intentionally omitted:
  gracRatingClassificationNumber on app_versions_update_age_rating, and
  marketSettings and multiSeatStatus on subscriptions_update.
- 17 reviewed reasons that cite the pinned Apple version now cite 4.5; each
  claim was verified against 4.5, and the build encryption-declaration
  PATCH deprecation against current Apple DocC.
- optionalInputCoveragePin: 2,907 total, 1,121 bound, 40 internally
  controlled, 1,746 intentionally omitted, 0 unclassified.

The only requiredness change on a mapped operation, filter[planType] on
adjusted equalizations, is handled in the next commit.
App Store Connect API 4.5 marks filter[planType] as required on
GET /v1/subscriptionPricePoints/{id}/adjustedEqualizations; 4.4.1 left it
optional. subscriptions_list_price_point_adjusted_equalizations published
plan_types as optional and sent filter[planType] only when it was supplied,
so every call without it produced a request that Apple's current contract
rejects. The contract gate did not flag this because the input is bound
and the checker does not compare Apple requiredness with the MCP schema.

plan_types is now required in the input schema, and a missing or empty
value fails before any network request. The sibling
subscriptions_list_price_point_equalizations keeps plan_types optional
because Apple 4.5 keeps that filter optional.

BREAKING CHANGE: MCP clients must pass plan_types (MONTHLY, UPFRONT, or
both) to subscriptions_list_price_point_adjusted_equalizations.
- Tests that assert manifest pins now expect specPin 4.5 (SHA-256
  1e8ef250..., 1,270 operations), 425 waivers and 369 scope-rule
  operations, the optional-input pin 2,907 / 1,121 / 40 / 1,746 / 0 with
  identity 0ce3ff14..., and reviewAtSpec 4.5. The two export-compliance
  waiver reason checks follow the verified 4.5 wording.
- ASCCoverageInventory moves to appleAPIVersionBaseline 4.5 and snapshot
  date 2026-09-24. The baseline is not independent of the contract pin: it
  was set to 4.4.1 together with the 4.4.1 contract hardening (4ae6293),
  the inventory drives ASC-OPENAPI-COVERAGE-GENERATED.md, which CI compares
  with the live Apple specification, and its rules classify all 973
  Apple 4.5 paths.
- ASC-OPENAPI-COVERAGE-GENERATED.md is regenerated from Apple 4.5: 973
  paths, 1,270 operations, 0 unclassified.
- README and CHANGELOG record the 4.5 pin and counts, the coverage matrix
  points at the 4.5 generated matrix, and the README promoted-image rows
  cite pinned OpenAPI 4.5, where promotedPurchaseImages is still absent.
- Historical statements stay unchanged: deprecations since 4.4.1, features
  introduced in 4.4.1, earlier CHANGELOG entries, and test titles that
  describe 4.4.1 fixtures.
…ventories

The Apps and AppLifecycle and the commerce inventory tests pin the exact
number of optional-input classifications, and the subscription write test
pins the omitted body inputs of subscriptions_update. They now include
gracRatingClassificationNumber, marketSettings, and multiSeatStatus, which
the 4.5 re-baseline classifies as intentionally omitted: 120 Apps and
AppLifecycle classifications (119 omitted) and 171 commerce
classifications (140 omitted).
@zelentsov-dev
zelentsov-dev merged commit 5e04643 into develop Sep 24, 2026
4 checks passed
@zelentsov-dev

Copy link
Copy Markdown
Owner Author
Protocol: DX-REVIEW/1
Message: FINAL_RECEIPT
Round: 1
Repository: zelentsov-dev/asc-mcp
PR: 31
Base-SHA: fcfa12f03c4225c19b0e92838cb75fef29b97657
Head-SHA: 44ec237b1fe0e28cee97675a3c2f75dac7bb8897
Previous-Reviewed-Head: N/A
Delta: fcfa12f03c4225c19b0e92838cb75fef29b97657...44ec237b1fe0e28cee97675a3c2f75dac7bb8897
Change-Class: Apple OpenAPI contract re-baseline 4.4.1 -> 4.5 (manifest, tests, docs) plus one breaking MCP tool-input fix
Deployment-Authority: no-deploy

## Intent and user effect
Independent exact-head review of the Apple App Store Connect OpenAPI 4.5 re-baseline: pinned spec provenance, re-stamp justification, requiredness completeness, the breaking plan_types fix, test integrity and the contract-gate gap. No live App Store Connect calls were made.

## Scope and exclusions
- 67e95c6 manifest re-baseline, 261ef52 plan_types fix (2 Swift files, manifest note, 2 test files, CHANGELOG), 7972e38 tests and docs, 44ec237 inventory counts: 51 files, +1230/-1155, merge-base fcfa12f
- Out-of-repo evidence: own fetch of the live Apple spec zip, Apple DocC JSON for the build encryption PATCH, scratch git-archive builds of the head
- Excluded: .github/workflows and the contract checker (Sources/asc-mcp/Tooling) are untouched; koreaAgeRatingOverride and kidsAgeBand support are follow-ups; no version bump, tag, release or Mint publishing

## Preserved contracts and invariants
- 502 public tools; the only input-contract change is the required plan_types on subscriptions_list_price_point_adjusted_equalizations: VERIFIED against the tools/list of a head build
- Implementation drift 0 and --strict clean on the live 4.5 document: VERIFIED locally and in CI
- 476 mapped + 425 deferred + 369 scope-rule = 1270 operations: VERIFIED by the strict run and the pinned test arithmetic
- subscriptions_list_price_point_equalizations keeps plan_types optional because Apple 4.5 keeps filter[planType] optional there: VERIFIED in both specs

## Risk focus
1. Pinned 4.5 document is Apple's real spec and re-stamping hid no contract change: verified independently, no defect
2. plan_types required matches Apple 4.5 and is enforced before network, including the next_url path: verified; breaking for MCP clients, release version is the owner's decision
3. No test weakened: verified, every changed assertion is a value move
4. Contract-gate gap: real and reproduced on this head, not widened by this PR (PR31-F001)
5. Release notes omit two user-visible Apple 4.5 effects (PR31-F002)

## Evidence ledger
| Layer | Status | Evidence |
|---|---|---|
| source | VERIFIED | Own identity-aligned manifest diff fcfa12f to 44ec237: 644 classification, 424 waiver, 4 scope-rule and 1 family stamps, 17 reason edits (all pure 4.4.1 to 4.5 substitutions), 3 new classifications, 1 new waiver, specPin, coverage pin and 1 tool note, 0 other changes. All 644 classified inputs are byte-identical across the two specs after full $ref expansion. Own requiredness detector over all 1263 common operations (canary 2 of 2 detected): the only transition is filter[planType]; 7 operations added, 0 removed. All 534 fixed values sit inside the 4.5 enums. Behind 50 changed container pointers the only leaf changes are AppInfo.kidsAgeBand removed, koreaAgeRatingOverride enum grown (never decoded into a closed enum) and additive fields. Tests: 34 assertion lines removed and 43 added, all value moves, no skips or inequalities. The 3 waiver facts hold in 4.5, and the DocC deprecation was re-fetched Post-merge read-back by the TeamLead: merge commit 5e04643ab99e89be5908bf3879b270408c6992c0 on develop at 2026-09-24T09:14:03Z, parents = base fcfa12f0 + approved head 44ec237b, tree identical to the approved head; merged with a merge commit per the repo convention, so the red intermediate commits 67e95c6 and 261ef52 sit off the first-parent line (bisect with --first-parent). |
| local | VERIFIED | Scratch git-archive build of 44ec237 (swift build, 0 warnings, asc-mcp 4.1.6): openapi-contract-check --strict on my own fetch of the live 4.5 document gives 0 unclassified, 0 structural errors, 0 drift and 0 warnings; the regenerated coverage document is identical; tools/list returned 502 tools (throwaway config, no network). swift test was not run locally |
| CI | VERIFIED | Run 35975296231 on 44ec237: Promotion Lineage, Release & Apple Contract (live 4.5 strict, relocated binary and coverage diff) and Tests all success, all required; the test log reads 1868 tests in 138 suites passed; Mint MCP Smoke skipped (tag-only). mergeable_state clean |
| device | N/A | Pure SwiftPM MCP server |
| staging | N/A | No staging environment for asc-mcp |
| provider | NOT CHECKED | No live App Store Connect calls (forbidden). Only the spec document was checked: developer.apple.com zip 4dc5dc79 and openapi.oas.json 1e8ef250, info.version 4.5, fetched 2026-09-24T08:49:48Z; this matches the head specPin |
| production | N/A | No deploy; the target is develop; release, tag and Mint publishing stay owner-only |

## Findings
### PR31-F001
- Severity: P2
- Confidence: 0.95
- Blocking: no
- State: OPEN
- Location: Sources/asc-mcp/Tooling/ASCOperationToolAnalyzer.swift:803-813,860-866,1058 (unchanged by this PR)
- Impact: The strict gate checks only that a required Apple parameter or body pointer is bound; it never compares Apple requiredness with the MCP input schema (validateFieldCatalog compares field names only). The 4.5 filter[planType] flip passed the gate and was caught only by the manual diff. At 44ec237, 31 Apple-required inputs bind only to optional MCP fields (10 primary, 13 supporting, 8 conditional). Today each is backed by handler behavior, so no live request is invalid, but nothing mechanical keeps it that way. Seven of the 10 primary ones were verified in code: the vendor_number config fallback in 3 analytics tools, the IOS default in ppoPlatform and reviews_summarizations, enabled defaulting to true in webhooks_create, and the always-encoded eligibility range in winback. The rest are alternative-parent selectors.
- Original-Counterexample: In a scratch git-archive copy of 44ec237, removing plan_types from the required list at SubscriptionsWorker+PlanAvailabilityToolDefinitions.swift:113 and running openapi-contract-check --strict against the live 4.5 document (1e8ef250) prints '0 unclassified, 0 structural errors, 0 implementation drift, 0 warnings' and exits 0. Only the new unit tests catch that regression.
- Required-Correction: Add an analyzer rule: an Apple-required parameter or body pointer must bind to a required MCP input, a fixed or derived value, or an explicitly declared handler default or alternative-parent selector in the manifest. Add a regression test in which reverting plan_types fails --strict.
- Follow-Up: Non-blocking: the checker gap predates this PR and the PR does not widen it (Tooling is untouched, no binding changed). The PR closes the only live instance and protects it with unit tests (the SubscriptionPlanAvailabilityContractTests required-set assertion and SubscriptionsV3WorkerTests.adjustedEqualizationsRequirePlanTypes). Separate task: the analyzer rule plus manifest annotations for the 31 handler-backed bindings.
### PR31-F002
- Severity: P3
- Confidence: 0.7
- Blocking: no
- State: OPEN
- Location: CHANGELOG.md:21-23 (head 44ec237, [Unreleased] Compatibility)
- Impact: The Compatibility note says that apart from plan_types existing calls are unchanged, but two user-visible Apple 4.5 effects ship with this baseline and appear only in the PR body. First, app_info projections will return kidsAgeBand as null (AppInfoWorker+Handlers.swift:738) because 4.5 removed AppInfo.kidsAgeBand. Second, app_versions_update_age_rating cannot set korea_age_rating_override to the new 4.5 values ALL and TWELVE_PLUS.
- Original-Counterexample: 4.5 AgeRatingDeclaration.koreaAgeRatingOverride enum is [NONE, ALL, TWELVE_PLUS, FIFTEEN_PLUS, NINETEEN_PLUS], while the tool allows only NONE, FIFTEEN_PLUS and NINETEEN_PLUS (AppLifecycleWorker+Handlers.swift:1490-1496, AppLifecycleWorker+ToolDefinitions.swift:608-612). AppInfo attributes in 4.5 have no kidsAgeBand (4.4.1 did), yet the projection still emits it.
- Required-Correction: Add a Known limitations line to [Unreleased] (or the release notes) naming both effects until the code follow-ups land.
- Follow-Up: Non-blocking: docs only, both effects come from Apple, not from this diff, and the owner writes the release notes. Separate task: fold the line in with the README pin relabel at release time; the code follow-ups are already listed in the PR body.

## Verdict
- Patch: APPROVE
- Merge: READY
- Merge-Actor: TEAMLEAD
- Release-Deploy: NOT_ASSESSED
- Production-Authority: OWNER_ONLY
- Approved-Head: 44ec237b1fe0e28cee97675a3c2f75dac7bb8897
- CI: Run 35975296231 on exact head 44ec237: Promotion Lineage success, Release & Apple Contract success (live 4.5 strict, relocated binary, coverage diff), Tests success (1868 tests, 138 suites); Mint MCP Smoke skipped (tag-only). Intermediate commits 67e95c6 and 261ef52 are red on their own: a squash merge keeps develop bisectable, while the repo's usual merge commit keeps only first-parent history green. The Breaking Changes entry makes plan_types required for callers; in this CHANGELOG, Breaking Changes entries have so far shipped only in major releases (2.0.0, 3.0.0), and the release version is the owner's decision.
- Remaining-Not-Checked: Live App Store Connect behavior without filter[planType] and for kidsAgeBand; local swift test (CI-verified instead); the Wayback CDX digest (4.4.1 provenance rests on SHA-256 equality with the base pin); the family 110 and waiver 39 per-category counts (SUPPLIED); code-level handler backing of 3 primary and 21 supporting or conditional optional-only bindings

## Not checked and residual risk
- Not checked: Live App Store Connect calls of any kind (forbidden by the brief)
- Not checked: Intermediate commits 67e95c6 and 261ef52 on their own (the author reports them red; only the head was built)
- Not checked: Wayback CDX digest and snapshot identity: 4.4.1 provenance was verified through SHA-256 equality of the recovered document with the base specPin ed0202ef instead
- Not checked: Per-category counts of the 110 family deltas and 39 waiver deltas: SUPPLIED; the substantive checks (fixed values inside the 4.5 enums, no public tool mapping a deferred operation) were verified
- Residual risk: Apple can publish another revision at any time; the live-spec gate will fail every branch again until the next re-baseline
- Residual risk: Contract-gate gap (PR31-F001): a future requiredness flip on another optionally bound input would pass CI
- Residual risk: kidsAgeBand reads null and korea_age_rating_override cannot set ALL or TWELVE_PLUS until the follow-ups land (PR31-F002)
- Residual risk: MCP clients that omit plan_types now get a local error; the version bump is the owner's call

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.

1 participant