Skip to content

fix(verify): allow duplicate content-hash passage ids (#5) - #6

Merged
ww2283 merged 3 commits into
mainfrom
bugfix/issue-5-verify-duplicate-ids
Aug 19, 2026
Merged

ww2283 merged 3 commits into
mainfrom
bugfix/issue-5-verify-duplicate-ids

Conversation

@ww2283

@ww2283 ww2283 commented Aug 19, 2026

Copy link
Copy Markdown
Owner

Fixes #5.

Problem

leann verify assumed passage ids are unique (a bijection across passages.jsonl, passages.idx, and the IVF id maps). With --id-scheme=content-hash, ids are sha256(text)[:16], so byte-identical chunks (empty __init__.py, repeated headers) legitimately share one id — the builder writes every chunk to jsonl, the offset map is last-wins per id, and the IVF passage_to_id is the last-wins inverse of a many-to-one id_to_passage. verify flagged all of this as corruption on healthy indexes while search worked fine.

Change (packages/leann-core/src/leann/cli.py)

  • jsonl duplicate-id check → duplicate ids allowed only when all lines sharing an id carry identical text (per-id text-hash comparison); same id with different text is still a finding.
  • idx cardinality → compared against unique jsonl ids instead of line count.
  • IVF size check → len(passage_to_id) vs len(set(id_to_passage.values())).
  • IVF inversion → each passage_to_id[pid]=fid must map back via id_to_passage[fid] == pid, and set(passage_to_id) == set(id_to_passage.values()); the offsets set check is unchanged.

No build-side changes; the storage contract is correct as-is.

Tests

tests/test_cli_verify.py: new _make_ivf_index_with_entries fixture helper taking explicit (pid, text) pairs (last-wins idx/passage_to_id, one id_to_passage entry per faiss label) + 4 tests: healthy duplicate-content index passes; duplicate id with different text, wrong passage_to_id inversion, and pid missing from passage_to_id all still fail. TDD: reproduction test written first and confirmed red on the old code.

E2E / smoke checklist (all verified)

  • uv run pytest tests/test_cli_verify.py — 16 passed (4 new + 12 existing).
  • Live E2E: built a real IVF index with --id-scheme=content-hash over a 129-chunk corpus containing 7 byte-identical files → leann verify exits 0 (previously false-positived); jsonl confirmed to contain 129 lines / 123 unique ids with one id ×7.
  • Same index: leann search returns correct results, including the deduplicated passage.
  • Negative smoke: hand-corrupted ivf_id_map.json (passage_to_id[pid]=999999) → verify exits 1 with does not map back in id_to_passage; restored map → exits 0 again.
  • ruff format --check / ruff check clean on both files.

ww2283 added 3 commits August 19, 2026 17:23
Content-hash ids are legitimately many-to-one for byte-identical
chunks; verify's bijection invariants flagged healthy indexes.
- jsonl dup ids allowed only when text is identical per id
- idx cardinality compared against unique jsonl ids
- IVF passage_to_id checked as last-wins partial inverse over
  deduped id_to_passage values
Codex review of PR #6: type-prefixed text hash so 1 vs "1" counts as
different text, and type-independent sort of conflicting ids so mixed
int/str ids report findings instead of raising TypeError.
@ww2283
ww2283 merged commit 444a5b4 into main Aug 19, 2026
26 of 30 checks passed
@ww2283
ww2283 deleted the bugfix/issue-5-verify-duplicate-ids branch August 19, 2026 21:40
ww2283 added a commit that referenced this pull request Aug 19, 2026
* fix(verify): allow duplicate content-hash passage ids (#5)

Content-hash ids are legitimately many-to-one for byte-identical
chunks; verify's bijection invariants flagged healthy indexes.
- jsonl dup ids allowed only when text is identical per id
- idx cardinality compared against unique jsonl ids
- IVF passage_to_id checked as last-wins partial inverse over
  deduped id_to_passage values

* docs: changelog entry for verify duplicate-id fix (#5)

* fix(verify): harden duplicate-id conflict check against mixed types

Codex review of PR #6: type-prefixed text hash so 1 vs "1" counts as
different text, and type-independent sort of conflicting ids so mixed
int/str ids report findings instead of raising TypeError.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

verify: bijection invariant false-positives on corpora with duplicate-content chunks (id_to_passage is legitimately many-to-one)

1 participant