Core concepts

The gate ladder

Every read walks a fixed ladder of gates. v0.6 has two ladders — the default unit path (unchanged from v0.5) and the opt-in pool path — plus default-OFF behavioral triggers around the read.

Every get(query) walks a fixed ladder of gates, top to bottom. Since v0.6 there are two ladders, selected by one constructor knob: the default unit path (read_path="unit" — byte-identical v0.5 behavior, its ladder unchanged) and the opt-in pool path (read_path="pool" — pooled cross-unit claim ranking, budgeted packing, an adaptive serve gate). Each rung is also a knob — but you rarely touch one. The defaults are pure cosine: both ladders run with no extra model and no extra dependency, and only reach for raw when the cache genuinely under-covers.

TIP

The default path costs nothing extra. Every gate decision on both ladders is cosine over embeddings you already computed — no cross-encoder, no NLI, no second LLM call on the read path. You turn a rung up only when a specific workload needs it (see the table below), and a v0.5 setup that touches nothing behaves identically.

New in v0.6 get() now dispatches on read_path first: "unit" (default) walks the eight-gate ladder exactly as in v0.5; "pool" (opt-in) runs the pool sequence instead. On the pool path the unit-path read knobs (hit_threshold, hit_margin, route_by_claim, recall_*, select_floor, ...) are inert — but coverage_floor (the RAG floor) and the S2 band (coverage_scorer) keep their jobs on both paths.

The two ladders, in firing order

A read flows down its spine — matched or pooled, fresh, covered, served. Each gate can branch off: a miss (unit) or a below-gate read (pool) builds, a stale unit re-materializes, an under-covered serve escalates to attributed raw (the RAG floor), and — on the pool path with the behavioral stack armed — a downstream refusal re-enters through report_refusal. It never fabricates.

Unit path ladder (default, unchanged from v0.5)

read_path="unit" is the default and is byte-identical v0.5 — the same eight gates, same defaults, same firing order. (New in v0.4 the matched unit is a list of query-independent extractive claims — see The Synthesizer — and cross-unit recall is on by default, gate 5, so multi-hop questions resolve for free on the read path.)

#GateDefaultFires when → what happens
1hit_thresholdauto (~0.33 OpenAI)best unit's blended score 0.7·topic + 0.3·seed is below the floor → MISS: retrieve + synthesize a new unit
2hit_margin0.0 (off)top unit beats the runner-up by less than the margin → ambiguous: build the query's own unit instead of guessing
3freshnessalways onmatched unit is dirty or expiredre-materialize it before serving
4coveragemax per-claim cosinealways computed — how well the matched unit covers the query; gates 5–7 read this value
5cross_unit_recalloncoverage < recall_thresholdsweep the best claims across all fresh units (MaxSim) and surface bridge facts; lifts coverage, no LLM call. A unit-path mechanism — the pool path has no recall step (its scan is already cross-unit)
6coverage_scorer (S2)None (off)coverage lands in [coverage_floor, coverage_ceiling) → a cross-encoder / NLI / one-token-LLM check overrides cosine
7coverage_floorauto (~0.28 OpenAI)coverage still below the floor → ESCALATE: append fresh raw retrieval (no LLM call) — the RAG floor
8select_floorNone (lexical trim)serve the atoms whose per-claim cosine ≥ floor — the query-relevant claims by meaning, fewer tokens. Deprecated in v0.5 — superseded by the pool read path (read_path="pool")

A hit that survives gate 1–2, is fresh (3), covers the query (4–7), and is trimmed (8) is served straight from the cache. A miss at gate 1 or an ambiguous tie at gate 2 builds a new unit; an under-covered hit at gate 7 escalates to fresh raw — never a fabricated answer.

Before the ladder: residual_floor (build time)

residual_floor runs once, at build, not on the read path. When the extractor drops a number-bearing source span — its max per-claim cosine falls below the floor — that span is kept verbatim as an extra atom bound to the unit, closing the extractor-recall gap for stray numbers. residual_limit (default 24) caps how many are kept, ranked most-missed first, so the safety net can't defeat the token win. Embedding-only, no extra LLM call; None = off.

v0.5 hooks around the unit ladder

New in v0.5 Five knobs sit at fixed points around the eight gates. All default off; preset="multi_hop" arms the recall pair with calibrated thresholds, and explicit kwargs always override the preset.

KnobWhere it sitsWhat it does
adaptive_hitgate 1the hit bar self-calibrates against cross-unit score inflation as the cache grows; repeat/paraphrase queries keep hitting via the seed-reuse channel
provenance_admissionbefore any buildan exact-text containment probe — covered reads serve without building (duplicate-understanding prevention)
widen_chunkson a miss (build)the build reads up to N chunks of the dominant source (a duck-typed retriever.widen(artifact_id, limit=) or a BYO source_fetcher) instead of the retrieval keyhole; never fires at ingest. widen_on_admission extends it to thin-coverage admission rebuilds
recall_bridge / bridge_limitgate 5a hop-2 restart: rank other units' claims by similarity to the matched unit's own claims, reaching the second hop the query alone can't. Armed by preset="multi_hop"
serve="pool" / serve_budgetafter the ladderthe v0.5 preview of pool serving — deprecated in v0.6 (removal v0.7), superseded by read_path="pool" and never auto-mapped to it. See Context intelligence

Pool path ladder (v0.6, opt-in)

New in v0.6 On the pool path, every read is answered by budget-packing the global fresh-claim pool — the best claims from any fresh unit, ranked against the query — while units remain the ownership / freshness / build / provenance skeleton. Validated at n=605 (strict grading): 0.731 accuracy @ 981 mean tokens with a metadata pool_header — naive top-9's accuracy (0.711 @ 1311) at ~25% fewer context tokens, and naive's best measured point (k12: 0.731 @ 1729) at ~43% fewer; gold-claim serving rank p50/p75/p90 = 1/6/15. The v0.5 preview point (0.699 @ 1036) holds. This is parity at fewer tokens, not an accuracy win over naive — and the shipping default header grades 0.68 on the same rig; the 0.73 rung needs your metadata (pool_header, below).

A pool read runs these stages in order — the code's own stage names:

StageWhat runsBranch
P0 — validate / embedEmbed the query once; mint a deterministic read_id (the handle for report_refusal / report_success)
P1 — reuseSeed cosine ≥ reuse_threshold (0.9) = the same question asked again → forced serve, regardless of the gatea stale reuse match re-materializes in place first; a lossy-marked one (behavioral stack) repairs append-only first — the serve is never stale
P2 — pool scanThe ClaimIndex cosine scan over the namespace's claim rows; freshness is a live pull-mask — a stale unit's claims never serve
P3 — bounded TTL + key overlayClock-compare the candidate-head owners only (≤ 3 revalidator calls, ≤ 1 re-scan); then query_keys overlays the scan: a claim's serving score becomes max(content, key), keys counting only at/above key_floorTTL-expired owners drop out of the candidates
P4 — serve gateTop pre-rerank cosine the effective serve_gateserve; below → build. The reranker can never influence this decisionbelow the gate → P5
P5 — buildOne probe retrieval (the read's single query-shaped retrieval), classified per artifact: CONTAINED (zero synthesis — provenance proves coverage) · STALE-OWNED / THIN (rebuild that unit in place) · NOVEL (build a source-anchored unit); ≤ 3 synthesis ops per read, then re-scan the poolan all-contained probe is a structural admission — serve with zero synthesis
serving pipelineDedup: within-owner-only near-dup collapse (0.95) — cross-owner duplicates survive as corroborationrerank hook (serving order only) → pack to serve_budget: headers and separators count, stop at the first overflow, at-least-one always serves
span side channel(opt-in residual_spans) a fresh unit's residual span that outranks every fresh claim by span_margin serves as a labeled [source excerpt] line inside the same budget (≤ 2 per read)each span serve counts a lossy signal on its owner
P6 — S2 bandThe opt-in coverage_scorer judges the exact texts that will serve, only inside [coverage_floor, coverage_ceiling)
P7 — RAG floorFinal coverage < coverage_floor → append attributed raw ([source: id]-prefixed chunks), deduplicated, budget-capped, never emptyraw priority: the P5 probe → a reuse-rebuild's chunks → retrieve now
P8 — pack & serveRender claim groups under their attribution headers; cache_hit on this path = zero synthesis calls ran

Three guard rails fire at construction, not mid-read: read_path="pool" under the lexical HashingEmbedder raises (claim cosine collapses to keyword overlap); query_keys=True off the pool path raises (keys could attach and confirm yet structurally never fire); and pool without a pool_header warns — attribution falls back to the built-in header, the middle rung of a measured ~9-point ladder (below).

The pool knobs

KnobDefaultWhat it gates
read_path"unit"Which ladder runs. "unit" = byte-identical v0.5; "pool" = claim-pool-first serving
serve_budgetNone → 600 unit / 1000 poolPacked payload size (tokens; headers counted). The one knob most users touch; <= 0 raises
serve_gateNone (adaptive)The P4 serve-vs-build decision. Explicit float = absolute (reproducible benches); None adapts against the pool's null-shaped noise ceiling — floored at the embedder-derived coverage default (~0.28 on the shipped OpenAI embedder), ceilinged at that + 0.27
pool_headerNone → built-in ## {query[:60]}, else [source: {artifact_id}]Per-source-group attribution line. A measured ladder (n=605, strict): opaque id 0.64 → shipping default 0.68 → metadata callable [title | source | date] 0.73 — wire your own metadata (see the golden path). A raising hook is swallowed, never breaks serving
rerankerNoneServing order only — never serve/build/floor decisions, so a bad reranker can't cause a false serve or a skipped build
claim_indexNone → built-inBYO pool storage (ClaimIndex protocol; numpy / pure-python built-ins). A bare instance is single-namespace only; pass the factory form for multi-namespace
coverage_floorauto (~0.28 OpenAI)The same knob as unit gate 7 — on the pool path it is the escalation floor, read against the final (post-build, post-S2) coverage

Behavioral triggers (default-OFF)

New in v0.6 The behavioral stack sits around the pool ladder, not on it — nothing below runs unless switched on, and the pool defaults are byte-identical with it off. The loop: build-time span capture → read-time side channel → your answerer refuses → report_refusal(read_id) returns an attributed retry payload → report_success(read_id) confirms the rescue as a durable query key → the key overlays the next scan (P3) → repeated signals mark the unit lossyappend-only repair on its next rebuild touch. Measured on the n=605 rig: refusals 91 → 61 (−33%) with zero newly-wrong answers; the full stack +3.1 pts same-store at ~2% token cost; keyed-class first-pass 0% → 61% on mild paraphrases (small n — keys are a latency/round-trip lever, not an accuracy lever on diverse rewordings).

KnobDefaultWhat it gates
residual_spansFalseThe master switch: build-time span capture, side-channel serving, the report_refusal fallback, lossy marking, append-only repair
span_tau0.62Build-time capture: a fact-bearing source sentence whose max claim cosine is below this becomes a tier-2 span on the unit, never in the pool
span_margin0.0Read-time rule: a span must outrank the best fresh claim by this margin to serve in the side channel
span_serve_floor0.35report_refusal retry floor: minimum span–query cosine to return a retry payload (up to 2 attributed [source excerpt] lines, or None)
lossy_threshold2Span serves + raw fallbacks against one unit before it's marked lossy → append-only repair on its next rebuild touch (claims are never lost while the source hash is unchanged)
query_keysFalseBehavioral alternate keys from confirmed rescues. Requires read_path="pool" — a loud ValueError at construction otherwise
key_floor0.85Minimum key–query cosine for a key to count at all — below it the key row is ignored entirely. Raise it in dense same-topic corpora: sibling-article collisions are a measured watch item

report_refusal and report_success never raise: an unknown or aged-out read_id (64-read ring), a disarmed stack, or no qualifying span is a silent None / no-op. Neither makes a retrieval or LLM call; the only side effect is report_success persisting the confirmed key when a store is configured.

Other hooks on the read path

These sit alongside the ladders rather than on them:

  • route_by_claim (default off; unit path) — route the match on the best per-claim embedding instead of the unit's topic embedding; useful when one unit holds many distinct facts.
  • relevance_gate — a (query, chunks) -> chunks callable that drops irrelevant retrieved chunks before synthesis on either path, so a new unit is built from cleaner evidence.
  • depth — on the LLMSynthesizer (0.0 terse → 0.5 balanced default → 1.0 exhaustive); trades synthesis cost against how often later reads escalate. See The Synthesizer.
  • calibrate_thresholds(embedder, positives, negatives) — derive hit_threshold / coverage_floor from labeled (query, understanding) pairs for a custom or text-embedding-3-large embedder; the shipped embedders are auto-calibrated already.

Which knob for which workload

WorkloadReach for
Multi-hop — facts split across sourcespreset="multi_hop" New in v0.5 — arms recall + the hop-2 bridge with calibrated thresholds (replaces the manual recall_threshold ≈ 0.7)
Real-document corpora / cold startswiden_chunks New in v0.5 — build from the whole source, not the retrieval keyhole
Long-running cachesprovenance_admission + adaptive_hit New in v0.5 — no duplicate builds; a hit bar that survives cache growth
Token-budgeted servingread_path="pool" + serve_budget New in v0.6 — the pool read path; serve="pool" (the v0.5 preview) is deprecated
Per-source / attribution-heavy questionspool_header New in v0.6 — wire [title | source | date] metadata; the measured 0.64 → 0.68 → 0.73 ladder
Contradiction / collision-heavy corporahit_margin > 0 — costs rebuilds; leave off on clean data
Paraphrase-heavy queries over large unitsselect_floordeprecated in v0.5; use read_path="pool"
Refusal-heavy answer loops / extraction tailsresidual_spans=True + wire report_refusal/report_success New in v0.6 — refusals −33% measured, zero newly-wrong answers
Repeat traffic on paraphrased questionsquery_keys=True New in v0.6 — keyed-class first-pass 0% → 61% (small n); latency/round-trip compounding, not an accuracy lever on diverse rewordings
Messy real-world prose, numbers buried in textresidual_floor — recover extractor-missed number spans
High-stakes ambiguitycoverage_scorer (S2) — containment-grade check in the borderline band, on either path
Reproduce exact v0.3 behaviourextract=False + cross_unit_recall=False
CAUTION

hit_margin and coverage_scorer cost you something — extra rebuilds and an extra check respectively — so they earn their place only on ambiguous or collision-heavy data. On clean, structured corpora the pure-cosine defaults are already at parity; don't turn rungs up you don't need.

Reading the ladder in production

s = cache.stats()
s["hit_rate"]          # share of reads served from cache
s["escalation_rate"]   # share that hit the RAG floor
s["hit_threshold"], s["hit_margin"], s["coverage_floor"]
s["recall_threshold"], s["select_floor"], s["residual_floor"]

# read_path="pool" adds the pool dashboard:
s["serve_gate_effective"], s["pool_noise_ceiling"]      # the gate actually applied
s["pool_serves"], s["probe_reads"], s["admission_reuses"]
s["rebuilds_by_read"], s["builds_by_gap"]               # why builds happened
s["pool_claims_fresh"], s["pool_claims_total"], s["pool_mask_rate"]
s["avg_units_per_serve"], s["pool_scan_slow"]

cache.stats() reports hit_rate and escalation_rate alongside the effective operating thresholds — so you can see which rung is doing the work and confirm exactly which knobs are live. A climbing escalation_rate means understanding is too thin (raise depth); a low hit_rate on paraphrases of the same question means the match bar is too high (hit_threshold, unit path). On the pool path, cache_hit means zero synthesis calls ranprobe_reads counts reads that retrieved without building (the gate-miss dashboard), and a climbing builds_by_gap with flat accuracy means the serve gate is set too high.

Next

  • Context intelligence — the pool payload contract, the pool_header golden path, and the behavioral stack in detail.
  • The Synthesizer — where extractive understanding and depth come from.
  • get() & Result — the single read API and every field it returns.