e0c075a25a
Two query-planner hotspots called out by the audit: #2 — mergeDesc used to materialise every driver's full stream into one ArrayList before sorting, so `limit = 10` against a million-event store would still load and sort the full million before emitting anything. Replace with a lazy k-way merge on a max-heap of per-stream heads. Memory is now O(num streams) instead of O(sum of stream sizes), and `limit` short-circuits naturally after the first N pops. walkDir also sorts filenames (cheap strings) instead of Candidate objects (bigger) — same DESC order because our `<padded_ts>-<id>` convention makes lex-reverse == chronological DESC. #3 — ftsDriver used to load every search token's full listing into HashMap<HexKey, Long> before intersecting, so `search = "the bitcoin"` against a popular token would spike memory proportional to that token's size. Switch to smallest-first: count each token dir, drive the walk with the smallest, and confirm each candidate in the others via a single `Files.exists()` per (candidate, token). Works because every FTS hardlink for an event shares the same `<padded_ts>-<id>` filename, so stat-check is a direct lookup. Memory is now O(smallest_token_size) — no HashMap for the big tokens. 113 fs tests green, FsSearchTest verifies the ordering, AND semantics, and limit behaviour that these touch.