Summary
The memory pack's warm-ANN freshness gate (AnnState::generations, khive-pack-memory/src/ann.rs) is a plain in-process Mutex<HashMap<AnnKey, u64>> — no persistence, no cross-process visibility. The note-mutation hook seam introduced for #750 guarantees freshness for all mutation paths within one process, but an external writer process cannot invalidate a running daemon's warm cache.
Affected paths
-
kkernel exec --ops-file --atomic (crates/kkernel/src/atomic_apply.rs): runs in its own short-lived process. Its in-process hook wiring is now correct (registry installs the hook, atomic update/delete post-commit effects fire it), but the bumped generation dies with the process. A concurrently-running daemon serving memory.recall keeps trusting its warm index.
-
kkernel reindex (crates/kkernel/src/reindex.rs): constructs a bare KhiveRuntime with no packs loaded, so it has no AnnState at all. It rewrites note/entity vectors directly against the DB, then invalidates persisted Vamana snapshot rows (invalidate_vamana_snapshots, ~line 628) — a different mechanism from the daemon's in-memory generation counter. A running daemon's already-loaded warm index is untouched and can serve pre-reindex vectors until its own next generation bump or restart.
-
Any other external process writing memory notes against the same DB while a daemon holds a warm index.
Why this was not fixed alongside #750
#750 was an in-process race (mid-write corpus snapshot installed permanently by entry().or_insert()); the generation-checked install plus the mutation-hook seam close every in-process path. Cross-process coherence is architecturally distinct: no hook fired inside a separate OS process can reach the daemon's in-memory state. It requires one of:
- a persisted generation counter (DB row bumped transactionally with corpus writes; daemon compares persisted vs in-memory generation on the recall path or on a cheap poll),
- a daemon notification channel (external writers signal the daemon to invalidate), or
- startup/periodic revalidation (daemon re-derives corpus fingerprint and discards stale warm entries).
The persisted-counter option is the natural fit — the daemon already reads the DB on every recall, and the existing persisted-snapshot invalidation in kkernel reindex shows the pattern.
Scope note
The practical exposure today is bounded: routine agent traffic goes through the daemon (single-writer), and kkernel exec non-atomic local fallback constructs the full server (hook installed) in-process. The gap matters for --atomic batch applies and admin reindex runs executed while a daemon is live.
Summary
The memory pack's warm-ANN freshness gate (
AnnState::generations,khive-pack-memory/src/ann.rs) is a plain in-processMutex<HashMap<AnnKey, u64>>— no persistence, no cross-process visibility. The note-mutation hook seam introduced for #750 guarantees freshness for all mutation paths within one process, but an external writer process cannot invalidate a running daemon's warm cache.Affected paths
kkernel exec --ops-file --atomic(crates/kkernel/src/atomic_apply.rs): runs in its own short-lived process. Its in-process hook wiring is now correct (registry installs the hook, atomic update/delete post-commit effects fire it), but the bumped generation dies with the process. A concurrently-running daemon servingmemory.recallkeeps trusting its warm index.kkernel reindex(crates/kkernel/src/reindex.rs): constructs a bareKhiveRuntimewith no packs loaded, so it has noAnnStateat all. It rewrites note/entity vectors directly against the DB, then invalidates persisted Vamana snapshot rows (invalidate_vamana_snapshots, ~line 628) — a different mechanism from the daemon's in-memory generation counter. A running daemon's already-loaded warm index is untouched and can serve pre-reindex vectors until its own next generation bump or restart.Any other external process writing memory notes against the same DB while a daemon holds a warm index.
Why this was not fixed alongside #750
#750 was an in-process race (mid-write corpus snapshot installed permanently by
entry().or_insert()); the generation-checked install plus the mutation-hook seam close every in-process path. Cross-process coherence is architecturally distinct: no hook fired inside a separate OS process can reach the daemon's in-memory state. It requires one of:The persisted-counter option is the natural fit — the daemon already reads the DB on every recall, and the existing persisted-snapshot invalidation in
kkernel reindexshows the pattern.Scope note
The practical exposure today is bounded: routine agent traffic goes through the daemon (single-writer), and
kkernel execnon-atomic local fallback constructs the full server (hook installed) in-process. The gap matters for--atomicbatch applies and admin reindex runs executed while a daemon is live.