1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
//! ADR-017 §A.1 — persistent block prefix cache, format + index module.
//!
//! This module ships the lowest layer of the ADR-017 persistence stack:
//!
//! * [`format`] — on-disk envelope (byte-compatible with oMLX
//! `paged_ssd_cache.py:246-297`), chain-hash identity per ADR-017
//! §D4, and the `EnvelopeHeader` JSON schema (ADR-017 §D10).
//! * [`index`] — in-memory `HashMap<BlockHash, BlockMeta>` with
//! restart-recovery scan (ADR-017 §D8) and quarantine of corrupted
//! files (ADR-017 §R-F9).
//!
//! Phase A.2 lands `block_store` + `writer` + `recovery` on top of these
//! primitives; Phase A.3 lands the `BlockPrefixCacheSpiller<E>` impl
//! that wires `KvSpiller<E>` (ADR-005 Phase 4 iter-212) into the
//! HotSwapManager.
// ADR-017 Phase E option (a) iter-1 (2026-05-05): standalone LCP
// (longest common prefix) registry. NOT yet wired into the request
// flow — that's iter-2 / iter-3 scope. This iter ships only the
// pure-data-structure substrate + 12 unit tests pinning the
// correctness invariants identified in the research dossier at
// `docs/research/adr017-phase-e-option-a-2026-05-05.md` §9 + §10.
pub use ;
pub use ;
pub use ;
pub use LoaderWrapper;
pub use ;
pub use ;
pub use KvPersistRegistry;
pub use ;
pub use ;
// ---------------------------------------------------------------------------
// EngineBindable — Phase C.1 additive trait surface for binding the live
// engine reference into per-family `KvCacheSpill` hooks BEFORE the
// `KvSpiller::post_admit` trigger fires.
//
// ## Why a separate trait (not a method on `KvCacheSpill`)
//
// `KvCacheSpill` is the per-family payload codec — its surface (`block_alignment`,
// `snapshot_block`, `restore_block`) is stable per the iter-212 ship contract +
// the Phase A.3 wiring discipline. Adding `bind_engine` directly to
// `KvCacheSpill` would force every existing impl (including the Phase A.3
// `StubGemma4Spill` and any test mocks) to handle a concept they don't need.
//
// Splitting the engine-binding seam into its own trait keeps `KvCacheSpill`
// payload-only and lets stateless hooks (e.g. `StubGemma4Spill`,
// `MockKvCacheSpill`) impl it as a no-op. Hooks that DO need engine state
// (Gemma 4 dense, future Qwen 3.5 hybrid) impl it with the real downcast.
//
// ## Why `Arc<dyn Any + Send + Sync>`
//
// Phase A.3's `BlockPrefixCacheSpiller<E>` is generic over the engine type
// `E` so the trait surface stays engine-agnostic. The Phase C.1
// `LoaderWrapper<E>` is also generic over `E`. Neither knows about
// concrete types like `MlxModelWeights` or `EngineHandle`. Type-erasing
// the engine ref through `Arc<dyn Any>` lets the wrapper deliver the
// freshly-loaded engine to the hook without the wrapper depending on the
// hook's concrete state shape — the hook performs the downcast itself
// (`Arc::downcast`) and silently no-ops on type mismatch.
// ---------------------------------------------------------------------------
/// Per-family hook extension for binding the live engine reference at
/// load time. Phase C.1 calls
/// [`Self::bind_engine`] from inside `LoaderWrapper::load` BEFORE
/// `KvSpiller::post_admit` fires, and [`Self::unbind_engine`] from
/// the manager's evict path so stale handles don't outlive the
/// engine.
///
/// **Send + Sync** because the `KvPersistRegistry` holds the hook
/// behind `Arc<dyn EngineBindable>` and the bind/unbind trigger sites
/// may run from concurrent tokio tasks (per the
/// `HotSwapManager<E>` concurrency model in `multi_model.rs:887-892`).
///
/// **Object safety**: both methods take `&self` (no generic params,
/// no Self return), so `Arc<dyn EngineBindable>` is well-formed.
///
/// **Failure handling**: hooks must NEVER panic on type mismatch. The
/// canonical impl uses `Arc::downcast::<ConcreteHandle>()` which
/// returns `Result<Arc<ConcreteHandle>, Arc<dyn Any>>`; the `Err`
/// branch must silently discard the engine ref and return without
/// further state mutation.