All checks were successful
CI / Unit & Component Tests (pull_request) Successful in 3m21s
CI / OCR Service Tests (pull_request) Successful in 20s
CI / Backend Unit Tests (pull_request) Successful in 3m26s
CI / fail2ban Regex (pull_request) Successful in 44s
CI / Semgrep Security Scan (pull_request) Successful in 19s
CI / Compose Bucket Idempotency (pull_request) Successful in 1m3s
Replace the long positional filter lists on the document search chain with the SearchFilters record. searchDocuments now takes (SearchFilters, DocumentSort, String dir, Pageable) and findIdsForFilter takes a single SearchFilters; the four private helpers (buildSearchSpec, runSearch, countUndatedForFilter, isPureTextRelevance) no longer carry a positional 10-field filter list. The controller builds the record after its existing tagOp/undated coercions; the density path adapts its DensityFilters into a SearchFilters at the shared buildSearchSpec call. The forced-undated count path is preserved via filters.withUndated(true), so countUndatedForFilter still ignores the user's toggle (#668) while runSearch honours it. No behaviour change. Controller binding tests swap their positional any()/eq() matchers for ArgumentCaptor<SearchFilters>, asserting captured.undated()/.status()/ .sender() — strictly stronger than the previous any()-soup. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>