Story-3296 Walkthrough

Outcome: Blake now has a second run mode. The carver (shipped in Story-3291) splits a bundle and stops. The clerk โ€” this story โ€” takes ONE document, does the three registration passes on it, and drops each one to its own file the moment it is done. All four of Blake's document passes moved onto the single model chain the founder crowned on 2026-08-02, each leg fused at 70 seconds, and each member's journey now sticks to whichever leg answered first.

This is the agents half of Phase 4. The knife that cuts annexures into real PDFs and the fan-out that invokes fifteen clerks are pramaan-functions (Story-3294 / 3295). The firm style guide's storage is Story-3292 (pramaan-functions #927). The adversarial corpus for the new context surface is Story-3297. This PR is the brain.

๐Ÿ›ฐ๏ธ Satellite โ€” three problems, one shape

1. A bundle run was one long silence. Sixteen members, held in memory, one file at the end. A crash on member #16 lost members #1โ€“15, and nothing downstream could react until it was all over. ADR-180 cut the worker in two: the carver looks at a pile of paper and says "this is sixteen documents"; the clerk takes ONE document and registers it properly. Different jobs, now different runs.

2. An annexure read alone is almost meaningless. "A two-page bank statement" is what the clerk sees. "The bank statement the petitioner relies on for ground III" is what it actually is. So an annexure's clerk is handed the main filing's reads and the list of all members โ€” and that is a new way for a poisoned document to reach fifteen other prompts, which is why the block arrives in its own fence and is declared the weakest source in the prompt.

3. The same document was being paid for three times. Classify, card and segment all re-send the same pages. A provider cache is keyed per model on the opening bytes of the request โ€” so if the ask comes first, or if the three steps run on three different models, none of it caches. Both had to change together.

โœˆ๏ธ Airplane โ€” what a bundle looks like now

bundle upload
   โ””โ”€ Blake  mode="carve"                       โ† Story-3291, unchanged
        โ”œโ”€ 1 model call
        โ”œโ”€ PUT runs/<run>/steps/1-carve.json    โ†’ broker cuts 16 PDFs
        โ””โ”€ run ENDS

   โ””โ”€ Blake  mode="clerk"   ร— 16                โ† THIS STORY
        โ”‚   MAIN member first, then 15 annexures in parallel
        โ”‚   ONE seat for the whole member (sticky)
        โ”œโ”€ classify โ†’ PUT steps/2-classify.member-00.json   โ”
        โ”œโ”€ card     โ†’ PUT steps/3-card.member-00.json       โ”‚ each written the
        โ””โ”€ segment  โ†’ PUT steps/4-segment.member-00.json    โ”˜ MOMENT it finishes

The timing of those three PUTs is the feature, not a detail. The broker fans the fifteen annexure clerks out on the MAIN member's card drop. If the card only became visible when the whole member finished, segment would sit on the critical path of a bundle it has nothing to do with.

All four PUTs still go through the same one function, agents/_shared/result_delivery.py. The story adds destinations, never an egress path. Blake holds no S3 key, no AWS SDK and no bucket name โ€” just one-time signed URLs the broker minted, and CI still fails the build if a second HTTPS PUT site appears anywhere in agents/.

๐Ÿ”ฌ Microscope โ€” the four things that are easy to get wrong

1. The chain: one seat, three legs, crowned by measurement

#aliasmodelwhy it is there
1openai-balanced__openrouteropenai/gpt-5.6-lunacheapest that passes both gates; the door that reports cache classes
2google-economy__openroutergoogle/gemini-3.5-flash-litea DIFFERENT model, so one vendor's bad day is not the journey's
3openai-balanced__directopenai:gpt-5.6-lunathe same model on its own door โ€” an OpenRouter outage is not a Luna outage

Gemini scored HIGHER and is still second, on purpose. Spike-3271 measured Gemini Lite at 1.000 card precision against Luna's 0.846โ€“0.867. The founder put Luna first because the chain is cost-first: the fallback fires on operational failure โ€” timeout, transport, empty answer, invalid JSON โ€” never on a valid-but-below-bar answer. So Gemini's quality is insurance, not the routine path. Do not "fix" this by promoting the better scorer.

The wired-but-never-live chain (NVIDIA glm-5.2 โ†’ kimi K3) is gone. On the production carve engine it scored 0 of 3: not wrong answers, no answers inside the budget.

2. The fuse is per LEG, and that is the whole lesson of 2026-07-24

chain leg (70s)  ร—3 = 210s  <  step window (240s)  ร—3 = 720s  =  clerk watchdog

On 2026-07-24 three staging runs died with every model healthy, because the timeout measured the whole pass: a slow-but-fine primary spent the budget, and the fallback inherited a leg that was already burnt. Each leg now gets its own full 70 seconds (journey_seat._FusedLeg). 90s was rejected by the founder for the arithmetic reason that 3 ร— 90 = 270 does not fit in 240. Segment keeps its own 105s per-window override, and additionally cannot outrun its pass deadline โ€” each of its legs starts with min(105s, time left), so a window that falls through all three legs still cannot hand the invoke watchdog a run whose honest error it would discard.

The clerk's outer watchdog is 720s, and that number is the one place this PR extended the founder's arithmetic rather than copying it. His 240s is the per-step window his 3 ร— 70 sits inside; a three-step run needs three of them. It does not breach the old "โ‰ค the registration consumer's 240s read timeout" note, because that note predates artifact delivery โ€” a clerk invocation acks immediately and works in the background, so no caller is holding a socket open for it.

3. Stickiness: the cache claim, made concrete

A member's seat is created once and threaded through all three passes. Once a leg answers, the cursor sits on it, so card and segment start on the model that already has those document bytes warm. A failure moves the cursor forward only โ€” a later step never re-asks a leg that already failed for this member.

Carve builds its own seat and throws it away. It reads the bundle's header strip, not any member's text, so it shares no cache prefix with the journey. Pinning it to a member's leg would buy nothing and would let one member's bad luck choose the next bundle's carve model.

4. Document-first is a caching change AND a fence change

The material turn now opens with the document and closes with the per-step ask. Everything volatile โ€” the catalog, the metadata, the vocabulary โ€” sits between them.

Moving a block in a fenced prompt is a security edit, because a fence only holds while the reader is told a block is fenced before it reads the contents, and while no zone can forge another's closer. So the fence order was re-verified, not assumed: tests/test_registrar_document_first_prompts.py asserts each zone opens and closes exactly once, that no two zones interleave, that the card's two random nonces are still distinct, and that the counsel note and the family context both land outside the document fence.

Two new zones join the trust rules. FAMILY CONTEXT (D18) is model-written text about a document Blake did not read: it may interpret and may never supply a quote, page, date, party, amount or type, may never move privilege or significance, and loses to the pages in front of it. FIRM STYLE GUIDE (D23) governs the wording of two prose fields and nothing else.

Both are byte-free when absent. Not a null, not an empty heading โ€” the renderer returns "", so a standalone upload at a firm with no style guide gets a prompt that differs from before only by the reordering. And both log the absent branch, because a no-op branch that stays silent is indistinguishable from one that never ran โ€” which is exactly how Story-3115 shipped a feature that never executed once, for eleven merges.

๐Ÿงช What was checked, and what was not

ClaimThe check
All four seats resolve to the exact three crowned aliases; no stale NVIDIA/K3 chaintest_registrar_journey_seat.py โ€” asserts the alias tuple and that any surviving mention of the dead chain is a comment
70s shared fuse, 3 ร— 70 inside the watchdog; segment keeps 105ssame file โ€” the arithmetic is asserted, including that 3 ร— 90 would breach it
Per-member leg stickiness; monotonic advance; carve independentthree tests driving a fake chain and reading which legs were asked
Forced primary failure โ†’ Gemini serves the member's remaining journeya forced-failure drill over real Model subclasses, asserting the SECOND step also lands on Gemini
Forced OpenRouter outage โ†’ Luna direct completes inside the watchdogboth gateway legs hang past their fuse (the worst case, not a clean error); the direct leg finishes and the elapsed time is asserted against the watchdog
Three drops, in step order, each as its step finishestest_registrar_clerk.py โ€” asserts the exact minted keys, in order, before the run's own result
A clerk missing one step envelope fails before any model callsame file โ€” and asserts no task was registered, so nothing was paid for
Empty D23 block โ‡’ byte-identical promptsstring equality on both prose prompts, with and without the argument
Settings built fresh per call, per legasserts no settings dict is shared between two builds or between two legs of one build
Blake's offline eval suitespython -m evals.run โ€” govern, critic and comparator gates 100%

Not proved here: nothing about the deployed runtime. This repo cannot verify AgentCore state (Terraform in pramaan-infra owns it), and the end-to-end bundle โ€” carve drop โ†’ 16 documents โ†’ 48 drops โ†’ final index โ€” needs the broker halves that land in Story-3294/3295. The founder's watched-bundle gate is Phase 5, and this PR does not claim it.

๐Ÿ“‹ Doctrine that moved in the same PR (the merge-blocker rule)