# `aborist.evict`

Hot ↔ cold tier transitions. The corpus is large (3.47M Wikipedia
docs); not every chunk fits in working memory. `evict.py` is the
mechanism that moves rarely-touched chunks to a cold tier (still
indexed, just stored separately) and rehydrates them on demand
from the original source.

## API surface

```python
from aborist.evict import evict_to_cold, rehydrate

# Move chunks unused for >threshold days to cold tier
evict_to_cold(conn, max_age_days=90, max_evictions=10000)

# Pull a cold chunk back to hot from its original source
rehydrate(conn, document_root="abc123...")
```

## Invariant: cores never evict

`evict_to_cold` filters `WHERE kind='surface'`. Cores are always
hot — they're the long-tail-friendly compression layer that justifies
evicting their underlying surfaces. Evicting cores would defeat the
purpose.

## v9.8 falsification on drift

When `rehydrate()` re-fetches a document and the recomputed
`document_root` differs from the stored one, the source has
changed since ingest (Wikipedia article was edited, HTML page was
republished, etc.). The cache record's `falsification_state` flips
from `live` to `stale` — every providence record keyed on that
`source_root` is no longer admissible to lookups.

This is the **drift-detection-as-falsification** discipline: cache
hits don't blindly trust historical answers; they trust answers
that the SAME source still grounds.

## Tier values

`chunks.tier ∈ {'hot', 'cold'}`. Hot chunks live in `chunks.content`;
cold chunks live with NULL `content` and a `cold_uri` pointing at
the source. The QA pipeline's chunk-fetch path checks `tier`; on
'cold', it triggers `rehydrate` before continuing.

## Source

[`aborist/evict.py`](../../aborist/evict.py)
