Story 2071: CE-13 β€” the ONE bundle builder (folding five stores into one pack a brain can read)

WHY. The four pillars now hold real data β€” cards (what each document is), retrieval (hybrid FTS+vector+rerank), the Neptune graph of record (contradictions, chronology), and the relational assertions projection. But a brain cannot read five stores mid-run: a panel run is bounded and non-streaming, and the brain's only egress is the inference provider (pramaan-docs ADR-110). Something must fold all five into one bounded, versioned, provenance-carrying pack the broker pre-fetches and the prompt sandwich wraps. The catastrophe if that assembly is ad-hoc per consumer: the synthesizer judges a proposal built from a different context than the proposers saw, the cache key drifts, and "what did the model actually read" becomes unanswerable β€” in a system whose entire promise is byte-exact provenance. CE-13 is the ONE builder: identical requests produce a byte-identical version_hash (so all four roles of a run pin the same pack), watermarks make staleness decidable and flagged instead of silent, and a debounced rebuild consumer keeps the cache fresh without a rebuild-per-fact stampede during an intake burst.

πŸ›°οΈ Satellite β€” the one sentence

A pure assembler folds cards β†’ chronology β†’ evidence β†’ assertions β†’ contradictions into a context_bundle.v1, spends each section its slice of a named budget tier's token ceiling (recipes live in settings, eval-tuned, not doctrine), stamps a version_hash over exactly what it serves, and caches it in context.bundles; an I/O orchestrator fetches the five sections from the real stores around it, and a quiet-window rebuild consumer drains ops.context_bundle_jobs so a burst of triggers coalesces onto one rebuild.

✈️ Airplane β€” the sandwich chef with a fixed-size plate

Think of a chef plating one dish the diner (a brain) eats in a single sitting. The frame is the placemat β€” it sits outside the plate (and outside the hash): the prompt is system + charter β†’ frame@frame_version β†’ bundle@version_hash, so the frame is carried once, beside the bundle, never baked in (collision #6). The plate is a fixed size β€” the budget tier (S/M/L β†’ a token ceiling). The recipe decides how much of the plate each of the five courses gets (a strategy diner gets more assertions+contradictions; a drafting diner more evidence). If a course won't fit its slice, the chef serves fewer items and says so (truncated=True) β€” never overfills the plate.

Two facts make the dish honest. First, the ticket: the chef writes a version_hash over exactly the food on the plate, so two identical orders get the identical ticket and cache to one entry β€” and the four roles of a command panel all quote the same ticket, so the critic judges what the proposer ate. Second, the freshness sticker: the plate records the watermarks it was built from, so when the pantry restocks (a card updates) the diner is told the dish is stale β€” served anyway, flagged, never silently.

Restocking is the last trick. Fifty documents landing in a minute would have the chef re-plate fifty times, each throwing away the last. Instead each restock drops one sticky note per matter in a bin (the partial-unique index enforces one), and each new note slides the "cook after" time forward. A runner drains the bin only for notes whose time has passed β€” so the dish is re-plated once, after the burst quiets (target ≀ 2 min).

  request(profile, budget, role_hint, focus?)
        β”‚
        β–Ό   build_bundle  (I/O)
   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
   β”‚ cards       ← context.document_cards        (spans inline)β”‚
   β”‚ chronology  ← Neptune Event traversal        (resolver)   β”‚
   β”‚ evidence    ← CE-5 hybrid+rerank  IFF focus   (else empty) β”‚
   β”‚ assertions  ← context.assertions            (spans inline)β”‚
   β”‚ contradictions ← Neptune CONTRADICTS edges    (resolver)   β”‚
   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                   β–Ό   assemble_bundle  (PURE)
        recipe Γ— budget β†’ per-section token allocation
        truncate to fit  β†’  truncated: bool
        sha256(firm,matter,axes,layout,served items) β†’ version_hash
                   β–Ό
        upsert context.bundles   (one per firmΓ—matterΓ—profileΓ—budgetΓ—role_hint)

  frame  ──stays OUTSIDE──▢  cache key = (prompt_version, frame_version, version_hash)

  card/assertion/graph change ─▢ enqueue_rebuild ─┐  (not_before = now + debounce)
                                                   β–Ό
     50 triggers ─▢ ONE pending job (partial-unique) ─▢ drain when not_before ≀ now
  

πŸ”¬ Microscopic β€” the four decisions that carry the story

1. The pure/I-O split β€” so the invariants test without infrastructure

assemble_bundle is a pure function: it takes already-fetched section inputs and produces the bundle β€” no DB, no graph, no clock. That is where the two merge gates live and where they test with zero infrastructure: byte-identical hash (same items in β†’ same version_hash) and token ceiling + truncated (a small budget cuts items, the total never exceeds the ceiling, a cut section says so). build_bundle is the thin I/O shell that fetches the five sections and calls the pure core. A green pure test is necessary; the integration test against a real, empty-first-run matter is what proves the door is not welded shut.

2. version_hash is over what is SERVED, and the frame is not in it

The hash is computed after truncation, over (firm, matter, profile, budget, role_hint, layout_version) plus each section's kind and the JSON of the exact items served β€” spans carry the verbatim quote, so "item refs AND texts" are both in it. built_at and the watermarks are deliberately excluded (they are staleness metadata, not content), which is why two builds seconds apart still hash identically. The frame lives outside the bundle and outside the hash; a section-order or recipe change bumps layout_version (a hash input) to invalidate caches without forking the schema.

3. Recipes in settings, budgets as config β€” tuned, not doctrine

The S/M/L tiers survive as named tiers, but every number behind them β€” the ceiling and the per-profile section weights β€” is an eval-tuned config default in settings.py (context_bundle_budget_tokens, context_bundle_recipes), per the RFD's supplement 7A: token cost and latency are forces the golden-corpus eval balances, and lost-in-the-middle means the eval may tune a budget down. Consuming code never hardcodes a weight; it reads the recipe. The builder proves the ceiling regardless of what the eval picks β€” token_estimate and truncated enforce it.

4. Staleness is decidable, and rebuild is debounced β€” never silent, never a stampede

A bundle records the watermarks it was built_from. Read-time staleness is the cheap comparison β€” has a card/assertion updated_at advanced past the stored watermark? β€” because recomputing the graph watermark on every read would defeat the cache; the event-debounced consumer is what re-derives the expensive ones. A stale bundle is still served, flagged. Rebuild triggers coalesce: the ops.context_bundle_jobs partial-unique index guarantees one undispatched job per matter, each trigger slides not_before forward, and the drain claims only due jobs with FOR UPDATE SKIP LOCKED so parallel workers partition the ledger instead of double-firing.

Merge gate (CE-13)Where it is proven
byte-identical-hashtest_bundle_builder.py::test_identical_inputs_are_byte_identical (unit) + test_context_bundle.py::…hash_is_byte_identical (real DB)
token ceilings + truncatedtest_total_never_exceeds_budget_ceiling, test_truncation_flag_set_when_items_dropped (unit)
rebuild SLA (quiet-window)test_drain_respects_not_before_debounce + test_enqueue_coalesces_burst_into_one_job (real DB)
empty first-run not welded shuttest_build_bundle_empty_first_run_is_live_and_cached (real DB, zero-rows matter)
Recall it in one sentence: CE-13 is the ONE builder that folds five real stores into one budgeted, byte-hashed context_bundle.v1 β€” recipes and budgets are eval-tuned settings, the frame stays outside the hash, staleness is flagged not silent, and a quiet-window consumer coalesces an intake burst into a single rebuild.