Skip to content

Chore: Add API breakage gate, platform CI matrix and changelog (Phase 0) - #440

Merged
nezhyborets merged 12 commits into
mainfrom
chore/phase-0-ci-safety-net
Sep 14, 2026
Merged

nezhyborets merged 12 commits into
mainfrom
chore/phase-0-ci-safety-net

Conversation

@Krivoblotsky

@Krivoblotsky Krivoblotsky commented Sep 13, 2026

Copy link
Copy Markdown
Contributor

What

Phase 0 of the maintenance plan: a safety net that makes "no breaking changes" and "builds on every declared platform" checkable in CI, plus the paperwork that goes with it.

  • API Breakage workflow (.github/workflows/api-breakage.yml): runs swift package diagnose-api-breaking-changes against the latest semver tag on every pull request and push to main, writes the report to the job summary, and fails on every break not listed in .github/api-breakage-allowlist.txt. Manual runs can pass a different baseline.
  • Swift Build workflow now covers what Package.swift declares: Linux in swift:5.10, swift:6.0 and swift:6.3 containers (build + test), macOS and iOS Simulator (test), tvOS, watchOS and visionOS (build) on macos-latest.
  • CHANGELOG.md in Keep a Changelog format, seeded from the 0.4.9, 0.5.0 and 0.5.1 release notes, with the 0.5.1 API changes spelled out.
  • CONTRIBUTING.md: an API stability section describing the additive-only policy, how the gate works, how to accept an unavoidable break, and how regenerated Components.Schemas types are treated.
  • Test target compiles for iOS 13 again: testModerationsIterable used Regex, which needs iOS 16.
  • One build warning fixed in ModelResponseEventsStreamInterpreter (optional interpolated into a debug log line).
  • Linux with Swift 5.10 builds again: swift-corelibs-foundation only gained the async URLSession APIs in Swift 6, so URLSession did not satisfy our URLSessionProtocol there and the package did not compile on the toolchain it declares as its minimum. A Linux-only, pre-Swift-6 extension bridges dataTask(with:completionHandler:) to async/await, with cancellation.

Why

swift package diagnose-api-breaking-changes 0.5.0 reports 77 public API breaks shipped in 0.5.1: 71 inside the generated Components.Schemas (for example transcription timestamps changed from Float to Double, ImageGenTool.SizePayload lost its cases, responseId was removed from the response audio events) and 6 in hand-written types (two ChatCompletionMessageParam initializers and ChatResult.Choice.Message.Annotation removed). Nothing in CI could have caught them, and CI only ever ran one unpinned Linux toolchain even though the package declares Swift 5.10 and five Apple platforms.

Affected Areas

CI configuration and documentation, one debug-only log line, one test using replacingOccurrences(of:with:options:) instead of Regex, and one new internal Linux-only file (URLSession+AsyncCompatibility.swift) that compiles only with compiler(<6.0). No public API changes; the new gate confirms this against 0.5.1.

More Info

  • The allowlist is intentionally empty. Every accepted break needs an entry there and a CHANGELOG line, and the file is emptied when the release containing the break is tagged.
  • Components.Schemas stays public. The policy in CONTRIBUTING.md asks for shims on regeneration instead of silently accepting breaks; moving generated types out of the public surface is a 1.0 topic.
  • swift-openapi-runtime 1.10.0 requires Swift 6.0 and 1.11.0+ requires Swift 6.1. Our Package.resolved pins 1.12.0, which a Swift 5.10 or 6.0 toolchain refuses outright, so the Linux 5.10 and 6.0 lanes delete the pin and resolve the way a consumer of this library does (they get 1.9.0 and 1.10.0). Consumers never use our Package.resolved, so this is the realistic check.
  • Affected areas now include one Linux-only source file. Its code was type-checked on macOS with the platform condition removed, and the Linux 6.0 and 6.3 lanes, which use Foundation's own implementation, are unaffected.

🤖 Generated with Claude Code

Krivoblotsky and others added 10 commits September 13, 2026 23:12
Runs swift package diagnose-api-breaking-changes against the newest semver tag on every pull request and push to main, publishes the report in the job summary, and fails on any break that is not listed in .github/api-breakage-allowlist.txt. A manual run can pass a different baseline.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…latform

Package.swift declares Swift 5.10 and macOS, iOS, tvOS, watchOS and visionOS, but CI only ran one unpinned Linux toolchain. Linux now builds and tests in swift:5.10, 6.0 and 6.3 containers; macOS and the iOS Simulator run the tests; tvOS, watchOS and visionOS are built with xcodebuild.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
testModerationsIterable used Regex and String.replacing(_:with:), which require iOS 16 while the package declares iOS 13, so the test bundle could not be built for iOS. Foundation's replacingOccurrences(of:with:options:) does the same job on every platform.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
CHANGELOG.md follows Keep a Changelog and is seeded from the 0.4.9, 0.5.0 and 0.5.1 release notes, including the public API changes 0.5.1 shipped. CONTRIBUTING.md gains an API stability section: additive-only public API, how the breakage gate works, how to accept an unavoidable break, and how regenerated Components.Schemas types are treated.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…lator by UDID

Package.resolved pins swift-openapi-runtime 1.12.0, whose manifest needs Swift tools 6.1, so the 5.10 and 6.0 lanes failed before compiling anything. A consumer of this library never uses our Package.resolved, so those lanes now delete it and resolve fresh, which is what such a consumer gets. The iOS Simulator lane picked a device name that only exists in an older runtime; it now takes an iPhone from the newest installed iOS runtime and addresses it by UDID.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
swift-corelibs-foundation gained data(for:delegate:) in Swift 6.0, so on Linux toolchains before that URLSession did not satisfy URLSessionProtocol and the package failed to compile, contradicting the declared Swift 5.10 minimum. A Linux-only, pre-Swift-6 extension now bridges dataTask(with:completionHandler:) to async/await, including cancellation.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… shim

The call was ambiguous between Foundation's method and the URLSessionProtocol overload that returns URLSessionDataTaskProtocol. Coerce to URLSessionDataTask, as URLSessionProtocol.swift already does.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Swift Testing ships with Swift 6 toolchains. The package declares Swift 5.10, and swift test on that toolchain failed with 'no such module Testing'. The eight Swift Testing files are now wrapped in #if canImport(Testing); the XCTest suite still runs on every toolchain.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Adding methods to OpenAIProtocol, OpenAIAsync or OpenAICombine is reported as a break by the API gate because external conformers stop compiling. New endpoint groups follow the Responses API pattern instead: one namespace property on OpenAIProtocol and the methods on the endpoint protocol in all three flavours.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
swift package diagnose-api-breaking-changes matches allowlist entries literally and matches none of them when the file contains a comment or blank line. The file now starts empty and the format is documented in CONTRIBUTING.md and in the workflow summary.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@nezhyborets
nezhyborets merged commit e39bf9d into main Sep 14, 2026
11 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.

2 participants