ci(cmux-next media): rebuild a warm tour's prebuilt modules after a binary pin change

Merged
TE
18d ago
+344-0

A pull request's second tour can fail on a Mac that last built its tag before a GhosttyNextKit pin change. cmux#21686's tour (job 117892119256) failed that way: the Mac had last built ci-media-pr-21686-head at 15:16Z on Oct 18 with GhosttyNextKit e6a27a1fb, and the new head pins ced99dcde. Xcode copied the new ghostty.h into Build/Products/Debug/include but reused the explicit module it had precompiled from the old one, so every Swift module importing GhosttyNextKit stopped with:

error: file '…/Build/Products/Debug/include/ghostty.h' has been modified since the module file
'…/Build/Intermediates.noindex/SwiftExplicitPrecompiledModules/GhosttyNextKit-….pcm' was built

The stale module is the SwiftPM binary target's (GhosttyNextKit), in SwiftExplicitPrecompiledModules. It isn't the GhosttyKit.xcframework that the "Download pre-built GhosttyKit" step re-extracts: both revisions in that job used the same ghostty SHA, and the cmux scheme doesn't link it.

The build step now runs scripts/ci/media_module_cache_stamp.py around each reload.sh:

  • clear, before the build, hashes every sha256 literal in the tracked Package.swift files (binary-target checksums change with their URLs) plus every tracked Package.resolved. If the hash differs from the tag's stamp, or there's no stamp yet, it removes SwiftExplicitPrecompiledModules, ExplicitPrecompiledModules and ModuleCache.noindex. Everything else in DerivedData stays warm.
  • record, after a build that succeeded, writes the stamp.

Each existing warm dir has no stamp yet, so its first tour after this lands rebuilds its modules once.

Validation

  • tests/test_ci_media_module_cache_stamp.py (registered in ci-guards and tests/test-execution.toml) runs the real build step twice against a stand-in reload.sh that fails like Xcode did when a module from another pin is still there. Before the fix (d7aab706f6) it fails with the same "has been modified since the module file" error; with the fix (2594d7504d) all 12 checks pass. actionlint reports nothing on cmux-next-media.yml. test_ci_prune_media_derived_data.py and test_cmux_next_media_tour_gate.py still pass.
  • CI repro on a real tour: a draft proof PR builds the head tag, reverts the GhosttyNextKit pin to a19c884fa on that warm tag, and rebuilds it twice: once without the stamp (control) and once with it. Results are linked below once that run finishes.

Changelog

none

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.


Summary by cubic

Fixes cmux-next media tours failing on Macs whose warm tag DerivedData holds Swift explicit modules precompiled from an older GhosttyNextKit pin; a new pin makes every Swift module importing GhosttyNextKit fail with ghostty.h has been modified since the module file ... was built (job 117892119256).

The build step now runs scripts/ci/media_module_cache_stamp.py around each reload.sh. clear hashes the binary-target checksums in tracked Package.swift files plus every tracked Package.resolved and removes SwiftExplicitPrecompiledModules, ExplicitPrecompiledModules, and ModuleCache.noindex when the hash differs from the tag's last recorded stamp; record writes the stamp after a successful build. Everything else in DerivedData stays warm, and existing warm dirs rebuild their modules once since they have no stamp yet.