Build Packages / Create release (push) Successful in 21s
Build Packages / build:rugnux:aarch64 (cross) (push) Successful in 7m31s
Build Packages / build:rugnux-tgz (x86_64) (push) Successful in 8m19s
Build Packages / build:viewer-tgz:cpu (push) Successful in 9m16s
Build Packages / build:viewer-tgz:cuda (push) Successful in 10m43s
Build Packages / build:rpm (rocky8_nocuda) (push) Successful in 12m42s
Build Packages / build:windows:nocuda (push) Successful in 17m8s
Build Packages / build:windows:cuda (push) Successful in 19m41s
Build Packages / HDF5 consumer tests (DIALS, XDS) (push) Successful in 21m50s
Build Packages / build:rpm (rocky9_nocuda) (push) Successful in 17m45s
Build Packages / build:rpm (ubuntu2204_nocuda) (push) Successful in 17m11s
Build Packages / build:rpm (ubuntu2404_nocuda) (push) Successful in 16m40s
Build Packages / build:rpm (rocky8_sls9) (push) Successful in 16m53s
Build Packages / Generate python client (push) Successful in 18s
Build Packages / build:rugnux:windows (push) Successful in 11m22s
Build Packages / Build documentation (push) Successful in 1m14s
Build Packages / build:rpm (rocky9_sls9) (push) Successful in 16m25s
Build Packages / build:rpm (rocky8) (push) Successful in 14m40s
Build Packages / build:rpm (ubuntu2404) (push) Successful in 13m58s
Build Packages / build:rpm (ubuntu2204) (push) Successful in 14m25s
Build Packages / build:rpm (rocky9) (push) Successful in 15m59s
Build Packages / Unit tests (push) Successful in 1h39m25s
A production broker on rc.170 segfaulted again in the FilterFFTResults length sort, in a binary that does contain the rc.170 non-finite guard (verified from its disassembly: the two isfinite tests are compiled in and skip the insert). With magnitude and length guaranteed finite, the only remaining way that sort sees a NaN key is a NaN direction vector - and the one place that writes the direction grid is SearchCap. SearchCap swaps in a temporary cap grid but restored it around ExecuteFFT only. The fill can run out of memory, the upload can fail, and the .at() that reads the peak back throws on exactly the corrupted result row this search has to survive - any of those left the cap grid installed on an indexer the pool hands the next image. If the axis was ever not a direction, axis.Normalize() is NaN and the leaked grid is 16384 NaN directions, which is that crash on every image afterwards. The restore now covers the whole body, and an axis that is not finite and non-zero is refused instead of normalized into NaN. The peak is also tested against the minimum length before the .at() rather than after, so an unusable peak with a corrupted index no longer throws at all. DirectionsChanged uploaded the grid with three unchecked cudaMemcpy calls, and the spot upload in ExecuteFFT had three more: a silently failed upload leaves the device holding the previous grid, so the directions the host reads the results against are not the ones the kernel used. Checked like the rest of the file. Tests: [Indexing] (9 cases, 115 assertions) and RotationIndexer pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>