Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions crates/trusty-common/changelog.d/5005-hnsw-id-aliasing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Fixed

- `HnswStore` no longer aliases vector ids across two live stores over one palace file, which silently overwrote one drawer's embedding with another's (closes [#5005](https://github.com/bobmatnyc/trusty-tools/issues/5005))
- the vector-id counter now lives in redb (`vector_id_seq`) and is reserved inside the same write transaction as the insert, so every writer on the file serialises against it; an existing palace has its counter seeded to the file's high-water mark on open, and re-raised on every subsequent open so a rolling upgrade cannot leave it behind
- `upsert` refuses an id that already has a `VECTORS` row: it allocates past it, or fails with `IdAllocationFailed` — it never overwrites
- `PalaceHandle::embed_health` and `palace_reembed` now carry an `AliasAudit`: key presence alone reported a false all-clear for this class, and `is_healthy()` is now false when any drawer is aliased
- an alias audit that could not run is `AliasAudit::Unavailable`, not zeros; `is_healthy()` is false for it, so a failed scan can never be read as a clean palace
- new `PalaceHandle::repair_aliases`: the operator surface for the repair, which had no caller at all. Dry-run by default; a real run frees the whole collision group and then re-audits, and reports `Repaired` only when that verification ran and came back clean. `Partial` and `Unavailable` are distinct outcomes and neither is a success
- `UsearchStore::unalias` now returns `UnaliasOutcome`, carrying the keys it freed but could not parse back into a drawer id instead of dropping them — those drawers would otherwise be missing from the operator's re-embed worklist inside a reported success
- `alias_audit` no longer drops collision-group keys that are not uuids, and `AliasAudit::is_clean` now consults `key_rows` vs `distinct_vector_ids` rather than the id list alone. Two `VECTOR_KEYS` rows on one `vector_id` with non-uuid keys previously reported `is_clean() == true`, `is_healthy() == true`, and a `clean` repair while leaving the collision in place — the counts come straight off the table and no parse can shrink them, so they are the signal that cannot be fooled
431 changes: 430 additions & 1 deletion crates/trusty-common/src/memory_core/retrieval/embed_repair.rs

Large diffs are not rendered by default.

787 changes: 786 additions & 1 deletion crates/trusty-common/src/memory_core/retrieval/embed_repair_tests.rs

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion crates/trusty-common/src/memory_core/retrieval/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,10 @@ pub use handle::PalaceHandle;
// public because a caller choosing to run a longer policy than the write-path
// default is a legitimate operator decision.
pub use deferred_embed::RetryPolicy;
pub use embed_repair::{EmbedHealth, VectorBackfillOptions, VectorBackfillReport};
pub use embed_repair::{
AliasAudit, AliasRepairOptions, AliasRepairOutcome, AliasRepairReport, EmbedHealth,
VectorBackfillOptions, VectorBackfillReport,
};

// Recall scoping (ADR-0027 T9)
pub use scope::{RecallScope, list_drawers_in_wing, scope_admits};
Expand Down
493 changes: 300 additions & 193 deletions crates/trusty-common/src/memory_core/store/hnsw_store.rs

Large diffs are not rendered by default.

Loading
Loading