feat(kvflash): pager serialize/deserialize + QK residency library

DU
3mo ago
+750-18

What

Model-agnostic KV-paging primitives in server/src/common/, already consumed by the qwen35/qwen35moe, gemma4, and laguna backends. This is the reusable foundation extracted from the kvflash snapshot-chain work — lands independently of any backend integration.

Why split it out

The full kvflash snapshot chain (~4000 LOC) couples tightly into qwen35_backend.cpp. The library (pager + QK scoring) does not — it touches only common/ headers + tests. Landing it standalone gives every backend a shared, tested KV-paging + residency layer without the integration-review burden.

Contents

  • kvflash_pager.h — serialize(max_chunks)/deserialize v1 format with ledger section (was_resident + qk_score + dtype) for pooled-prefix snapshot/restore; critical-chunk pinning + deadlock guard; floor_to_chunk; identity_prefix_covers (preserved for existing consumers).
  • kvflash_qk.h — cosine QK residency scoring with seeded-fallback; overflow-safe seeded_n (limit defaults to 0, not n_chunks, preventing OOB reads when a caller omits the length).

Verification

  • Builds against main's llama.cpp submodule (no fork dependency).
  • test_kvflash_pager PASS: serde + pinning + partial-serialize + ledger round-trip.
  • test_kvflash_qk PASS: pure-math scoring incl. seeded-sentinel fallback.
  • Full dflash_common (main's server code) recompiles — identity_prefix_covers keeps laguna/gemma4 working.
  • CPU-only; no GPU required.

Scope

5 files, +713/−18. No server-backend files, no bench/docs noise. Supersedes the library portions of #429/#445/#430/#446 (those integration slices remain tracked separately).

Follow-up PR (qwen35 pooled snapshot-chain integration) will stack on top of this.