ci(nightly-next): run verify-signed's checks on the DMG Mac, beside the DMG
Summary
A pool-signed cmux NEXT nightly sent its stapled app to two office Macs: signer-dmg-image (to build the DMG) and verify-signed-nightly (a gzip that signer-dmg made on Linux, downloaded again over the office link). That second download took 2-5 min in recent runs and set the length of the verify stage whenever it outran verify-final-dmg-nightly.
signer-dmg-imagenow runsverify-signed-nightly's checks itself (scripts/ci/nightly-steps/signer-verify-signed.sh: version surfaces, the signed diff sidecar contract,check-signed-app.shwith the Computer Use helper ticket required, the bundled Ghostty theme picker regression, the SSH daemon contract), on a copy of the app it already holds.- Those checks and the existing signed-app checks start in the background after Apple's checks of the stapled app, run while the DMG is built and uploaded, and the job fails after the upload when any of them failed.
signer-dmgneedssigner-dmg-image, so it never signs a DMG whose app failed a check. verify-signed-nightlyruns for the macOS signer's app only.publish-nightlytakessigner-dmg's success in its place for a pool-signed app (itsifand itsOWN_VERIFIED).signer-dmgno longer packages or uploads the verify handoff;signer-handoff.shis removed.
No check is dropped: verify-signed-nightly's codesign --verify --deep --strict is the first of signer-apple-checks.sh's checks, which the job runs before it starts the others. No secret reaches the Mac job. Nothing changes in build-nightly-app, prepare, or the macOS fallback signer path.
Performance
Chain = build-nightly-app end to publish-nightly end. The before rows are the last five arm64 pool-signed runs with a publish, from the jobs API. The after column is filled from real nightly-next runs once this lands.
| metric | before | after | delta | harness or run link |
|---|---|---|---|---|
| verify stage, max(verify-signed, verify-final) | 3.2, 5.8, 2.8, 2.6, 2.5 min | pending | expected -0.0 to -3.9 min (verify-final alone: 3.0, 1.9, 2.8, 2.6, 1.9) | runs 40893555177, 40905688542, 40909458084, 40913618519, 40918131595 |
| verify-signed download of the signed app | 120-321 s | 0 (no second download) | -120 to -321 s of office-link traffic per run | same runs |
| signer-dmg-image, checks then DMG | 3.0-5.5 min (checks 38-73 s run before the DMG) | pending | expected about -0.6 min (checks overlap the DMG build and upload) | same runs |
| chain (build end to publish end) | 16.5, 17.9, 17.7, 15.4, 15.9 min | pending | expected about -1 to -4 min | same runs |
| Mac jobs per pool-signed run | 3 (dmg-image, verify-signed, verify-final) | 2 | -1 | nightly.yml job graph |
Testing
python3 tests/test_nightly_signer_jobs.py: 20 tests pass, including newSignerVerifySignedtests that run the script with stub checks (every check runs on a copy; a failure failsfinishafter the rest ran; bundled-assets and CLI flags matchverify-signed-nightly; nothing a check writes reaches the app the DMG is built from) and the wiring test for the new step order.python3 tests/test_nightly_step_coverage.pyandpython3 tests/test_nightly_next_recovery.pypass.- Every other
tests/test_*nightly*/ workflow test that reads nightly.yml passes here, excepttests/test_nightly_next_arm64_only.pyandtests/test_check_ghostty_zig_workflows.py, which fail the same way on base in this Linux environment (agit rev-parseoutside a repo; nobashlex).
Observed / Not run
Observed: the tests above on Linux. Not run: a real nightly-next run with this change; the first runs after it lands fill the Performance table.
Changelog
none
š¤ Generated with Claude Code
Agent session: agent session