89f74d58f6
FactRetriever.search() re-encoded the query vector once per candidate, related() re-encoded both role atoms once per fact row, and probe() re-encoded the role-content atom once per row. All three encoders are deterministic (SHA-256 counter blocks), so the hoisted vectors are bit-identical to the per-iteration values they replace. Measured (300-fact store, dim=1024, median of 30 calls): search() 11.62 -> 1.46 ms/call (8.0x; encode_text 30 -> 1 per call), related() 63.08 -> 16.17 ms/call (3.9x; encode_atom 601 -> 3 per call), probe() 431.93 -> 389.36 ms/call (1.1x; dominated by per-fact content encoding, which is inherent to the algorithm and unchanged). Tests: call-count regression tests for each hoist plus a bit-exact parity test of search() against the pre-fix per-candidate loop.