CoreAIKit

GA day — the 0.4.x release on macOS/iOS 27 GA

The runbook for the morning Apple ships macOS/iOS 27 and the release Xcode. Written ahead of time (2026-08-31) so the day costs an hour, not a search. Everything here is mechanical; the one decision — which 0.4.x number — is step 6.

The trigger is Apple’s actual release of macOS/iOS 27 and release Xcode. Check the Apple release list; an event date or an RC does not establish GA availability. scripts/check-xcode-pin.sh also raises a tripwire when a build with release-style numbering is installed alongside the pinned beta. Treat that as a prompt to check availability: RC and GA can share a build number. Keep beta, RC and GA validation results separate.

1. OS first, then Xcode

Update the CI runner (and the dev Mac) to the release macOS before flipping the Xcode pin. The CoreAI framework is an OS library: binaries built against the release SDK on a beta OS can fail at dlopen when the SDK generation and OS build disagree — the failure looks like a broken model, not a version skew. Then install the release Xcode (it lands in /Applications/Xcode.app; the beta stays put).

2. Flip .xcode-pin

/usr/libexec/PlistBuddy -c 'Print :ProductBuildVersion' /Applications/Xcode.app/Contents/version.plist

Edit .xcode-pin: XCODE_PATH=/Applications/Xcode.app, XCODE_BUILD=<that value>. The build version is the pin, not the folder name. ./scripts/check-xcode-pin.sh must print the release build and exit 0.

3. GA wording

python3 scripts/ga-wording.py --apply

Four requirement sentences (README plus three docs) flip from “27 beta” to “27”. Keep dated beta validation evidence labelled with its original build. Then delete scripts/ga-wording.py and its two CI steps (ci.yml and nightly-gate.yml, the “GA wording” step in each) in the same commit — after --apply, --check fails by design.

4. Local gate before pushing

swift build && swift test

plus the hybrid two-turn check from AGENTS.md: two ChatSession turns on a hybrid bundle (Qwen3.5 / LFM2.5 / Granite 4) on the Mac — the second turn is where an engine-visible change shows, and an SDK generation flip is exactly the kind of change the hermetic tests cannot see. The coreai-models pin (exact: "0.2.4-zoo") does not move for GA.

5. Push and watch

One commit: .xcode-pin, the three docs files, the deleted script and CI steps, CHANGELOG (step 6). CI and the nightly gate must go green on the release toolchain before anything is tagged.

6. Tag and release notes

This is a patch release — API unchanged, the toolchain and wording moved — so the next free 0.4.x:

# CHANGELOG.md: retitle "## [Unreleased]" to "## [0.4.x] — <date>" in the same push
git tag 0.4.x && git push origin 0.4.x
gh release create 0.4.x --title "0.4.x — macOS/iOS 27 GA" --notes-file <notes>

Release notes = that CHANGELOG section, led by one line the funnel can quote: built and gated on release macOS/iOS 27 and release Xcode 27, no API changes, from: "0.4.1" resolvers pick it up automatically.

7. Devices

Re-run the on-device numbers only after the phones are on release iOS 27, and say which build produced them (AGENTS.md: no device numbers you did not measure). Nothing in the tag waits for this — device re-measurement is follow-up, not gate.