Reproducible 202609 chain, step 7: make the search-index builder deterministic - #357
Conversation
…cible chain step 7) Two builds of identical inputs produced different indexes (915 vs 901 shard files, 138 vs 137 hot tokens, 217 shared files with different bytes). Causes, all in the hot-token path: - est (avg posting width) was sampled with LIMIT 500000 and no ORDER BY, so the hot threshold moved between runs -> now averaged over all rows; - the hot set had no ORDER BY, so collision-suffixed hot/ keys were assigned in scan order -> ORDER BY token; - the per-shard 'heaviest token' promotion used ORDER BY n DESC LIMIT 1, so ties were arbitrary -> ORDER BY n DESC, token ASC; - hot_topk's per-pid static score was a parallel float sum -> sum(c ORDER BY field). Verified: two builds byte-identical across all 1,029 files (build_stats.json excluded: timestamps). Layout changes vs the old builder (1,025 shards) because the estimate is now over the full table; the reader contract is unchanged (hot_tokens.json remains the locator). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LtTxB4jfTZgaTR7CK4zKqy
…eplaces it); top_df tie-break (Codex round 1) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LtTxB4jfTZgaTR7CK4zKqy
…arget must resolve under --outdir (Codex round 2) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LtTxB4jfTZgaTR7CK4zKqy
|
🤖 rbotyee — Codex round 3 (final) verdict, verbatim: No P1/P2 findings remain in the scope-locked changes. The tag validation, canonical parent check, and symlink rejection all occur before the sole The full pytest suite could not start because the read-only sandbox provides no writable temporary directory. Verdict: LGTM. |
|
🤖 rbotyee — confirmation build with the final code: CONFIRMED: post-patch build byte-identical to run 1 (1029 files) |
|
🤖 rbotyee — staging check before merge: fork Pages run 33211721352 built green; |
🤖 rbotyee (Claude, operated by @rdhyee) — Raymond's intent: keep going on Part B under the standing rule. What I did: found that
tools/build_search_index.pyproduced different indexes from identical inputs, traced it to four order-dependent choices in the hot-token path, fixed them, and proved two builds byte-identical. No published file changes; the live 202608 index is untouched.Finding
Two builds of the same 202609 inputs: 915 vs 901 shard files, 138 vs 137 hot tokens, 217 shared files with different bytes — only one base shard differed; everything else was under
hot/. So the hot set itself was unstable.Causes → fixes
est(avg posting width) sampled withLIMIT 500000and noORDER BY→ different rows each run → the hot threshold movedORDER BY→hot/collision-suffix keys assigned in scan orderORDER BY tokenORDER BY n DESC LIMIT 1→ ties arbitraryORDER BY n DESC, token ASChot_topkper-pidsum(c)— parallel float summation ordersum(c ORDER BY field)(fieldnow carried throughcontrib; ≤ 1 row per field per (token, pid), so the order is total)--force, which removes it;--tagmust be one filename component and the target must resolve under--outdirbefore anyrmtreetop_df_tokenstieORDER BY df DESC, token ASCEvidence
build_stats.jsonexcluded — it carries timestamps); 1,025 shards, max 4.93 MiB under the 5 MiB cap; ~15 min per build. A third build with the final code is compared against run 1 below.hash(pid) % Msub-sharding,hot_tokens.jsonas locator). Layout changes because the estimate is now over the full table: vs the live 202608 index, 0 hot tokens disappeared, 0fetchableflips among tokens present in both, 20 tokens newly hot (all fetchable). (202609 has more OC records than 202608, so the sets aren't strictly comparable; the no-flip check is the point.)Review
Codex ×3: R1 changes-required (P2 stale output dir; nit df tie) → R2 changes-required (P2
--forcecould delete outside--outdirvia a traversal tag) → R3 LGTM. Verdict posted below.🤖 Generated with Claude Code
https://claude.ai/code/session_01LtTxB4jfTZgaTR7CK4zKqy