# 2026-04-15 · Tiered KV M0.3 + M3a local landing

## Context

After M2b, the tiered-KV project still had two structural gaps before real
T1 work could start:

- BF16 paged-KV still lived on `page_size = 1`, which makes T0↔T1 transfer
  bandwidth-hostile.
- `kv_tier` had the disk/NIXL side of the API surface, but not the local
  host-pinned transport skeleton or the tier-aware radix metadata that M3
  needs.

The 2026-04-15 local batch had to land both without pretending the CUDA host
validation was already done.

## What Worked

- Lifted BF16 paged-KV to `page_size = 16` while keeping INT8 / FP8 /
  TurboQuant at `page_size = 1` via `KVFormat::default_page_size()`.
- Reworked `TokenKVPool` around page tables (`free_pages`, `page_indices`,
  `seq_lens`, `max_total_pages`) so allocation, free-counting, and range
  lookup all stay token-accurate while storage becomes page-granular.
- Added `kv_cache_to_paged_range_hnd_cuda` so BF16 prefill→pool migration
  writes HND-correctly at `page_size > 1`.
- Updated FlashInfer decode metadata and all CUDA decode call sites to read
  runtime `page_size` instead of hardcoded `1`.
- Added local tests that pin the new page-aware bookkeeping and the
  `LocalCudaTransport` / `HostPinnedPool` structural invariants.
- Added `HostPinnedPool`, `Coordinator`, and `LocalCudaTransport` as the M3a
  structural skeleton.
- Extended `prefix_cache::Node` with the first tier-aware metadata fields:
  `hit_count`, `tier_location`, `session_id`, `fingerprint`, `byte_len`,
  and `soft_pin_until`.

## Rule

For tiered-KV work, separate **local structural landing** from **CUDA
acceptance** explicitly. Land allocator/trait/state-machine shape locally,
then require a written remote checklist before calling the milestone done.
