-| **INV-13** | **Progress & cache bounds.** The stream cursor strictly advances BY CONSTRUCTION (`cursor = last + 1 ≥ cursor + 1` per batch; a 204 terminates — no runtime guard could ever fire, so none exists); the chunk cache never exceeds the in-service span + READAHEAD; **a rejected chunk promise is evicted immediately** so a later interval retries instead of replaying the cached rejection (fixes **G1**); **a FRONTIER chunk truncated at a then-lower finalized head is EXTENDED (tail-only stream + merge) before being served past its `coveredTo`** — never served stale (head-boundary bug found in review; fixed on main by PR #5/802ceed and RE-IMPLEMENTED here with behavior parity — main's own #5 regression test passes unchanged). A failed extend evicts the whole entry — never leaves the optimistic high-water in place. | A cached rejection permanently poisons a chunk; an unbounded cache OOMs; a blind idx cache hit over a head-truncated chunk marks an interval synced over a silent gap. | `readAheadPlan`/`evictionPlan` bound the cache (pure); `dataChunk` records `coveredTo` per cache entry and extends via the shared `runStreams` (append-only merge; rows accounted through the same per-fetch token); `p.catch`/`extended.catch` evict + free exactly once. | `dataChunk`'s INV-1/INV-9/INV-3 asserts bracket every fetch; `portal-client.ts` documents progress-by-construction at the cursor advance. | `portal-chunks.test.ts` — "INV-13: read-ahead plan is bounded (≤ readahead), within raEnd, and depth-1-only when saturated", "eviction plan…"; `portal-client.test.ts` — "INV-13: the cursor strictly advances and the stream terminates"; `portal-shell.test.ts` — "G1: a failed chunk is evicted (rows freed) and a later call refetches…", "regression: frontier chunk truncated at a lagging Portal head is EXTENDED…" (main's #5 test, unchanged) and "frontier extend streams ONLY the newly-finalized tail…". |
0 commit comments