You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Solid arrows = integration order into wave/wave4-search-followups. Dotted = hard dependency. One branch per ticket; this ticket highlighted.
Found via dog-fooding round 2 (2026-06-27, branch wave/wave4-search), tracked under #174 Wave 4. Priority: P2.
Problem: On-disk index is ~300% of source (skim-search 65 MB / 21 MB; devflow 697%, townhall 684%) — 10–23x over the #358<30% target. The target is unachievable for a positional trigram index; zoekt-class engines land ~200–300%. skim is ~30–50% heavier than zoekt mainly due to a 13 MB JSONL field-map sidecar and a 1-byte field_id per posting.
Best fix: (1) re-baseline the #174 size target to a realistic ~200% (or define it against a compressed/non-positional variant if <100% is a hard product need); (2) replace the JSONL field-map sidecar with a binary encoding; (3) evaluate dropping/segmenting positions for the common case. Also: --stats "index size" (42 MB) undercounts on-disk by ~23 MB (omits skfiles+temporal+ast) — report true on-disk size.
Plan + resolved decisions (dynamic-plan, 2026-06-28)
Durable record of #380 scope, phasing, and resolution (full plan/test plan local, gitignored). Comprehensive scope: four workstreams split Phase 1 (mechanical, low-risk) and Phase 2 (research-gated, higher-risk).
Resolved design decisions
SCOPE EXPANDED (Dean): positional/segment postings folded into Wave 4 / Search: re-baseline #358 <30% index-size target + binarize the JSONL field-map sidecar #380, phased (DECISIONS-NEEDED.md OD-D); now FOUR workstreams: (1) binarize sidecar, (2) re-baseline size target to measured grounded guard, (3) accurate --stats display, (4) positional/segment postings codec -> Phase 1 = (1)+(2)+(3) ships first; Phase 2 = (4) research-gated, lands behind own commit (ADR-004 no #NEW placeholders)
Phase 1 (binarize sidecar + re-baseline + accurate stats)
FUNCTIONALITY (binarize field-map, round-trip): FileManifest with field_map triples whose end byte-offset exceeds 65535 (u16::MAX) MUST survive save()->load() with every field byte-identical. NEGATIVE: on-disk index.skfiles MUST NOT contain ASCII JSON-array encoding (no literal '],[' substring) after save().
API CONTRACT (format version + self-describing header): FORMAT_VERSION bumped to 4 (u32), rebasing onto Wave 4 / Search: standalone --ast resolves matches to the WRONG files (FileId→path ordering skew) #373's v3. Binary body MUST begin with 4-byte magic (b"SKFM") + version + u32 entry count, mirroring ast_index.skcache. load() returns Ok(empty manifest) when magic absent, version != 4, count > MAX_MANIFEST_ENTRIES, or body truncated.
API CONTRACT (bounded + saturating, observable): Binary encoder MUST be SATURATING — length/count exceeding u32::MAX written as u32::MAX, never panic. Decoder MUST REJECT BEFORE allocating when declared count > MAX_MANIFEST_ENTRIES (60000) or file size > MAX_MANIFEST_FILE_BYTES (256 MiB). NEGATIVE: forged u32::MAX count + oversized declared per-entry length yields Ok(empty) without panic.
FORMAT COMPATIBILITY / SELF-HEAL (central negative): After 3->4 bump, v3 JSONL index.skfiles (immediate predecessor, post-Wave 4 / Search: standalone --ast resolves matches to the WRONG files (FileId→path ordering skew) #373) and older v2 JSONL MUST trigger full automatic rebuild on next skim search <query> or skim search index WITHOUT --rebuild and WITHOUT error, FOR BOTH git AND non-git roots. NEGATIVE: system MUST NOT silently accept below-current (< v4) manifest as Current. check_staleness MUST detect below-current FORMAT_VERSION independent of git HEAD state.
FUNCTIONALITY (FileId alignment under corruption): On corrupt/truncated binary manifest body, load() MUST reject WHOLE file (Ok(empty)) and MUST NOT return partially-recovered manifest shorter than written, so sorted_paths()[n]==path-for-FileId(n) invariant never violated. Falsifiable: valid header with count=3 but only 1.5 entries of body yields entry_count()==0.
API CONTRACT + FUNCTIONALITY (--stats reports TRUE on-disk size): skim search --stats (text and --json) MUST report total-on-disk size = sum of metadata().len() over present index artifacts in cache_dir (index.skidx, index.skpost, index.skfiles, ast_index.skidx, ast_index.skpost, ast_index.skcache, temporal.db). NEGATIVE: total MUST NOT report lexical-only when other artifacts exist.
API CONTRACT (stats fail-soft + complexity bound): --stats total MUST treat missing artifact as 0 bytes, no error. MUST iterate FIXED known filename list via metadata() (O(1)), MUST NOT recursive directory walk. NgramIndexReader::stats.index_size_bytes unchanged (skidx+skpost only). Falsifiable: with only three lexical files present, reported total equals exactly their summed sizes.
GROUNDED REGRESSION GUARD (ADR-003, replaces baseless <30% target): NEW non-#[ignore] test MUST build real index via build_index over >= 8 KiB source corpus, measure skfiles bytes / source bytes, eprintln! ratio, assert < ceiling = measured post-binarization ratio plus fixed headroom. DISCRIMINATING (PF-007): test MUST FAIL if field-map encoding reverts to JSONL (JSONL ratio exceeds ceiling).
GROUNDED REGRESSION GUARD (whole-footprint, optional): IF whole-on-disk-footprint guard added, MUST exclude temporal.db from numerator (scales with git history) while temporal.db MAY still be reported under --stats. Ceiling = measured sum(6 deterministic artifacts)/source_bytes plus headroom, recorded as real number.
KNOWLEDGE DRIFT (PF-008): .devflow/features/cmd-search/KNOWLEDGE.md MUST describe manifest as binary (v4), remove 'JSONL' descriptor (lines 76/168/451-454/686), collapse duplicate 'manifest.jsonl' listing (lines 522-523) into single index.skfiles entry. index.json keyword blob updated consistently. NEGATIVE: CLAUDE.md edit NOT required; grep for JSONL must stay empty.
NO QUERY-PATH REGRESSION (negative, performance): Same query against binary-manifest index MUST return identical ordered SearchResult set (same paths, ranking, scores within tolerance) as prior JSONL-manifest build for same source tree. NEGATIVE: field_map MUST NOT be read at query time. Falsifiable: diff ordered results from parent-commit (JSONL) vs branch (binary) build.
ADR-006 ORDERING PRESERVED (negative): Binary save() MUST remain atomic temp-file-then-rename (readers never observe partial manifest). Build pipeline MUST persist ast_index.skcache and lexical/AST indexes BEFORE new_manifest.save(), manifest saved LAST. NEGATIVE: skcache or index write failure aborts BEFORE manifest saved so old index survives. Falsifiable: existing atomic-replace and ADR-006 abort tests pass against binary writer.
AC-P2-1 (FUNCTIONALITY — positions round-trip & decode, positive PF-007): After lexical postings codec extended, posting list for known file with known token offsets MUST survive encode->decode with every position byte-identical (positions delta+varint-decoded back to exact ascending offsets, none truncated). Lexical FORMAT_VERSION MUST bump v4->v5, positional postings file MUST begin with v5 version marker. Carries // AD-380-9: rustdoc at codec call site.
AC-P2-2 (FALSIFIABLE PF-007 — positional/proximity adjacency correctness, the required negative): Corpus: file A contains adjacent token sequence alpha beta; file B contains NON-adjacent (or reversed/distant) alpha … beta. Phrase/proximity query ("alpha beta" or --near 1) MUST return file A, MUST NOT return file B. NEGATIVE (fails when feature deleted/broken): if positions ignored (codec reverted to bag-of-trigrams) same query returns BOTH A and B — test MUST FAIL. Pinned that positional/proximity queries enforce token adjacency from decoded positions.
AC-P2-3 (NO-REGRESSION — non-positional queries byte-identical, negative): Plain (non-phrase, non-proximity) trigram query against v5 positional index MUST return identical ordered SearchResult set (same paths, order, scores within tolerance) as Phase-1 (v4, non-positional) index for same source tree. NEGATIVE: v4 postings file under v5 binary MUST cold-start (not mis-parsed as positional); check_staleness MUST flag below-current v4 lexical version independent of git state.
AC-P2-4 (PERFORMANCE + RE-GROUNDED SIZE GUARD, ADR-003): Positional queries MUST stay within query budget (<50ms per 1000-line file class; phrase/proximity adds only bounded post-verify adjacency check). Phase-1 grounded size ceiling MUST RE-MEASURED and RE-SET against positional build: new index.skidx/skpost (and total) ratio recorded as real measured number + fixed headroom in test comment (NOT arbitrary, NOT 0.30x). Falsifiable: re-grounded guard FAILS if positions persisted without re-measuring (old v4 ceiling breached) AND FAILS if codec unbounded.
Build context
Wave-4 search follow-ups. Sequence position 6 of 10. Depends on: #373 (normalize_rel_path). Unblocks: #376 (manifest format locked). AD-380-N rustdoc at decision sites (ADR-004, ADR-003, ADR-006). Falsifiable ACs (PF-007).
Resolved on wave/wave4-search via PR #386 (Wave-4 dog-food round-2). Residual partial-scope was ticketed as #388–#391 + #392 and resolved. Not yet on main — ships with PR #368 (wave→main).
Activity
dean0x commentedon Jun 28, 2026
Plan + resolved decisions (dynamic-plan, 2026-06-28)
Durable record of #380 scope, phasing, and resolution (full plan/test plan local, gitignored). Comprehensive scope: four workstreams split Phase 1 (mechanical, low-risk) and Phase 2 (research-gated, higher-risk).
Resolved design decisions
index.skfiles(SKFM magic + version + entries, mirrorsast_cache.rs), u32 bounds (source files << 4GiB), REJECT-WHOLE on corruption (preserves FileId alignment, ADR-006 critical)check_stalenessindependent of git state (fixes silent-correctness bug for non-git projects; ADR-001, ADR-006)Acceptance criteria
Phase 1 (binarize sidecar + re-baseline + accurate stats)
skim search <query>orskim search indexWITHOUT --rebuild and WITHOUT error, FOR BOTH git AND non-git roots. NEGATIVE: system MUST NOT silently accept below-current (< v4) manifest as Current.check_stalenessMUST detect below-current FORMAT_VERSION independent of git HEAD state.sorted_paths()[n]==path-for-FileId(n)invariant never violated. Falsifiable: valid header with count=3 but only 1.5 entries of body yields entry_count()==0.skim search --stats(text and --json) MUST report total-on-disk size = sum of metadata().len() over present index artifacts in cache_dir (index.skidx, index.skpost, index.skfiles, ast_index.skidx, ast_index.skpost, ast_index.skcache, temporal.db). NEGATIVE: total MUST NOT report lexical-only when other artifacts exist.Phase 2 (positional / segment postings codec — research-gated)
AC-P2-1 (FUNCTIONALITY — positions round-trip & decode, positive PF-007): After lexical postings codec extended, posting list for known file with known token offsets MUST survive encode->decode with every position byte-identical (positions delta+varint-decoded back to exact ascending offsets, none truncated). Lexical FORMAT_VERSION MUST bump v4->v5, positional postings file MUST begin with v5 version marker. Carries
// AD-380-9:rustdoc at codec call site.AC-P2-2 (FALSIFIABLE PF-007 — positional/proximity adjacency correctness, the required negative): Corpus: file A contains adjacent token sequence
alpha beta; file B contains NON-adjacent (or reversed/distant)alpha…beta. Phrase/proximity query ("alpha beta"or--near 1) MUST return file A, MUST NOT return file B. NEGATIVE (fails when feature deleted/broken): if positions ignored (codec reverted to bag-of-trigrams) same query returns BOTH A and B — test MUST FAIL. Pinned that positional/proximity queries enforce token adjacency from decoded positions.AC-P2-3 (NO-REGRESSION — non-positional queries byte-identical, negative): Plain (non-phrase, non-proximity) trigram query against v5 positional index MUST return identical ordered SearchResult set (same paths, order, scores within tolerance) as Phase-1 (v4, non-positional) index for same source tree. NEGATIVE: v4 postings file under v5 binary MUST cold-start (not mis-parsed as positional);
check_stalenessMUST flag below-current v4 lexical version independent of git state.AC-P2-4 (PERFORMANCE + RE-GROUNDED SIZE GUARD, ADR-003): Positional queries MUST stay within query budget (<50ms per 1000-line file class; phrase/proximity adds only bounded post-verify adjacency check). Phase-1 grounded size ceiling MUST RE-MEASURED and RE-SET against positional build: new index.skidx/skpost (and total) ratio recorded as real measured number + fixed headroom in test comment (NOT arbitrary, NOT 0.30x). Falsifiable: re-grounded guard FAILS if positions persisted without re-measuring (old v4 ceiling breached) AND FAILS if codec unbounded.
Build context
Wave-4 search follow-ups. Sequence position 6 of 10. Depends on: #373 (normalize_rel_path). Unblocks: #376 (manifest format locked). AD-380-N rustdoc at decision sites (ADR-004, ADR-003, ADR-006). Falsifiable ACs (PF-007).
feat(rebaseline-binarize): binary v4 manifest, true on-disk --stats, …
Merge ticket/380-rebaseline-binarize into wave/wave4-phase2-staging (#…
feat(search): add token_position to postings, format v5 for positiona…
feat(search): --phrase / --near positional query over v5 token_positi…
test(search): AC-P2 phrase/near acceptance + v4→v5 self-heal + v5 siz…
merge(#386): wave-4 search follow-ups (#373-#381) + #380 Phase 2 posi…
dean0x commentedon Jul 2, 2026
Resolved on
wave/wave4-searchvia PR #386 (Wave-4 dog-food round-2). Residual partial-scope was ticketed as #388–#391 + #392 and resolved. Not yet onmain— ships with PR #368 (wave→main).Wave 4: 3-layer code search — intersection / ranking / formatting / C…