The letter arrived. Nobody opened it.
Gate 1 proved delivery, and delivery is a real thing to have proved: Blake
writes a result, the object lands under agent-runs/, the
verifier recomputes its digest, the run reaches delivered, a
replayed PUT is refused 412. Every one of those held.
And the document sat at registering.
agent_results_consumer persisted the verified effects into
agents.agent_run_artifact_effect and emitted one
agent_run.artifact_delivered outbox event.
Nothing read either. grep -rl
agent_run_artifact_effect src/ returned the table definition and
its writer, and that was the whole list.
So a successfully classified document moved no product state at all. No
tool intent ran, so nothing was named or tagged. No terminal
registration_status was stamped, so counsel's screen said
"registering" indefinitely. No IB-3 inbox item was minted, so a
side conflict or a privilege suspicion went unraised. No understanding
fan-out fired, so there was no card, no sections, no brief. This Story is
the part that opens the letter.
Why it was ever like this
Because Bug-3323 removed the thing that used to do it. The registration
consumer once held Blake's invoke stream open and folded it — intents,
verdict, status, items, fan-out, all inline, all while the socket was
still up. Blake has no streaming route left: since Story-2958 every result
mode goes through start_artifact_run, which refuses an
invocation carrying no result_delivery and answers a valid
one with {"run_id", "status": "accepted"}. The broker mints,
dispatches and returns.
Bug-3323 deleted the fold and left the four behaviours it drove standing in the file, unreachable, with a note pointing at this Story. That note was honest — the state was inert, not "retired" while still running — but inert is not shipped.
The delivered artifact cannot say what it classified
This is the fact the whole design turns on, and it is not obvious.
registrar.classification.v1 is a TurnEffectData:
class TurnEffectData(ContractModel):
"""A versioned registrar result; token usage deliberately lives outside effects."""
turn: dict[str, Any]
A turn, and nothing else. No document_id — and Blake's
RegistrarTurn does not carry one either, because the broker
supplies identity and the brain never gets to choose it. That is the right
contract. It also means a delivered classification, sitting in the
database as verified truth, cannot name the document it is about.
The run row can. The broker knew the document when it minted the run. So
agents.agent_run gains document_id, written at
dispatch by write_pending_artifact_run. A second thing falls
out of that for free: _persist_effects skips the D19
staleness check for any effect that names no target, so every classify
effect ever was in the always-apply branch. It now has an ordering
surface like every other document-scoped effect.
The settlers cannot apply anything, and that is not a bug
The obvious design is to apply the effects where they are verified. It cannot work, and the reason is in Terraform rather than in Python.
| Lambda | What its role holds |
|---|---|
agent-results-consumer · agent-run-sweeper |
runs-bucket read · VPC ENI · logs · one SSM read |
document-registration-consumer |
AgentCore invoke · documents bucket · the graph · the enrich queue · the registrar DB scope |
Applying a classification means execute_tool_intent under
policy and RLS, the registration_status machine, the IB-3
producers and the CE-7 fan-out. That is the second row, not the first.
Doing it in the settler would mean granting a second identity the whole
fan-out's authority — the opposite of what R6 asks for.
The door that already existed
No new queue, no new bus, no new status store, no retry framework. Both settlers write ONE outbox row, inside the transaction that settles the run, and the relay that already drains registration and resegment rows to the registrar queue drains this one too.
results consumer ─┐
├─▶ ops.document_ingest_outbox (existing table, 5th event type)
sweeper ──────────┘ │
▼
registration relay (existing, tuple filter)
│
▼
registrar SQS queue (existing)
│
▼
process_artifact_delivered (new route, same handler)
├─ _execute_intents
├─ _finalize_registration (status + IB-3)
└─ _fan_out
The consumer already routes by event_type: registration,
resegment, and now document.artifact.delivered. The relay
already takes a tuple. The only new things are a migration widening a
CHECK, one column carrying the run id, and one function reading the effect
rows.
One detail worth naming: the apply ticket is deliberately NOT added to the immediate-dispatch map. Its producer has no SQS grant at all, so the scheduled relay tick is the delivery path — up to a minute of latency, never a lost apply, because the row is durable and undispatched until the relay sends it.
Exactly-once, three deep, and the outermost is the database's
-
The ticket's key is the RUN.
document_artifact:<run_id>withON CONFLICT DO NOTHING, so a replayed artifact resolves to the same row and enqueues nothing twice. A genuine re-registration mints a new run and is entitled to its own ticket. -
The document row is locked, and the lock covers everything.
SQS is at-least-once and the sweeper's ticket can race the delivery's, so
two appliers can both read
registering._claim_terminaltakesFOR UPDATE, and the whole apply — intents, status, IB-3 items — runs inside that one transaction. The loser blocks, wakes to a terminal status, and executes nothing at all. -
The old guards still stand. The executor's
(document_id, extraction_run_id)claim and the inbox dedup key, exactly as the inline path had them — now the backstop rather than the front line.
The first cut ran each intent in its own transaction, outside
the lock, on the reasoning that "one effect committing does not depend on
the next" and that a redelivery would be harmless because the tools are
upserts. Review caught it: that argument is about SEQUENTIAL redelivery
and does not survive two CONCURRENT deliveries, where both passes execute
every intent before either reaches the lock and
claim_registration lets the same run proceed. It also meant
a mid-list refusal committed the intents before it. Per-intent
transactions were right when a broker held a stream and a long run could
die halfway. On this path they were wrong twice.
And one level up, because not creating the conflict is a better answer
than arbitrating it: the registering re-drive edge used to
mint a SECOND Blake run over the same extraction, so both delivered, both
ticketed, and whichever settled first took the document — the older
classification could beat the newer one. A dispatch now stands down while a
run is still inside its delivery window. Past deadline_at the
sweeper settles it, its ticket stamps the terminal failure, and the retry
edge re-queues. One paid Blake pass per registration instead of two.
That is prevention, and prevention alone was not enough — which review said
plainly and the first cut of this Story did not believe. The window it
leaves open is the one that matters: past deadline_at the
older run A is no longer "in flight", so a redelivery legitimately mints
newer run B before the sweeper has got to A. A's late artifact then
settles first, finds no persisted effect to lose to, tickets, and wins the
document; B arrives to a terminal document and is dropped as a duplicate.
The same inversion in its worse dress: the sweeper settles the lapsed A, and
A's expiry ticket stamps registration_failed on a document B is
at that moment classifying perfectly well.
So the arbitration is against newer RUNS, not against effects
that happened to arrive first. The freshness guard in
_persist_effects compares one effect against effects
already persisted for the document — it can only ever see runs
that arrived, so arrival order silently stands in for run order. The
document-level guard lives instead at
write_artifact_apply_ticket, the ONE door all three settlers
pass through (the results consumer's success, its refusal, the sweeper's
expiry): a run that a newer run of the same agent family has superseded
for the same document writes no ticket at all. Whatever it settled to
stays on the run row as RUN truth; it is simply not allowed to become
DOCUMENT truth over a fresher attempt. The predicate is
(firm, document, agent_key) — scoped by family on purpose, so
a document's card pass can never starve the classification it is
downstream of.
Both hot reads on that column — the dispatch's "is one already in flight"
and the settle's "has a newer one superseded me" — get an index in the same
migration. agents.agent_run grows forever; unindexed they are
two sequential scans per document.
No branch can leave a document at registering
That sentence is the acceptance criterion, so it is worth listing what "no branch" covers:
| What happened to the run | What the document gets |
|---|---|
delivered, all_clear | registered, intents executed, fan-out fired |
delivered, needs_review | registration_needs_review + the blocking IB-3 item |
| delivered, both advisory holds open (I-3) | escalated to registration_needs_review |
| delivered, clean, but a privilege hold is open | registered, fan-out HELD and logged (I-11) |
| brain reported a failure | registration_failed + the brain's error class |
| artifact refused or quarantined | registration_failed + the verifier's sentence |
| swept — nothing ever arrived | registration_failed + the sweeper's sentence |
| a turn this broker cannot read | registration_needs_review (the default), never a crash loop |
Each failure repeats the settler's own lawyer-safe message onto the document, so the reason is visible where counsel looks rather than only in a run row nobody opens.
How we know it works
Every assertion was watched red before it was watched green, by planting the revert and running the test:
| Planted revert | Goes red |
|---|---|
| settle stops writing the ticket | test_the_results_consumer_settle_writes_the_apply_ticket |
| sweeper stops writing the ticket | test_the_sweeper_tickets_the_document_it_would_have_stranded |
| event type dropped from the relay filter | test_the_apply_ticket_rides_the_existing_registration_relay |
| D19 fallback removed | test_the_results_consumer_settle_writes_the_apply_ticket |
the applier returns skipped (the pre-Story state) | 11 unit tests |
_claim_terminal no longer consulted | test_the_row_lock_is_what_stops_two_tickets_racing |
| unapplicable delivery stops stamping a terminal | 3 × test_an_unapplicable_delivery_stamps_a_named_terminal_failure |
| a typed tool refusal propagates instead of terminating | test_a_refused_tool_intent_stamps_a_terminal_failure_not_a_retry_loop |
| the intents are hoisted back outside the lock | test_the_intents_run_inside_the_locked_transaction AND test_two_concurrent_applies_execute_the_intents_exactly_once |
_claim_terminal drops .with_for_update() and becomes a plain read | test_the_document_row_lock_really_blocks_the_second_apply |
the intent retype is removed (an invalid intent escapes as ValidationError) | test_an_intent_too_malformed_to_build_stamps_a_terminal_failure, test_a_deterministic_executor_raise_terminates_rather_than_retries |
the terminal door is widened to swallow SQLAlchemyError too | test_a_database_failure_mid_apply_still_retries_and_stamps_nothing |
| a second run is minted while one is in flight | test_a_redelivery_does_not_mint_a_second_run_while_one_is_in_flight |
| the supersession guard is removed from the ticket door | test_a_superseded_run_writes_no_apply_ticket |
the agent_key predicate is dropped from it | test_a_newer_run_of_another_family_supersedes_nothing |
the document_id predicate is dropped from it | test_a_newer_run_for_ANOTHER_document_supersedes_nothing |
The four database-level ones are not modelled races — they are two real
transactions on one Postgres row, and the lock proof only passes because the
second transaction genuinely blocks in the database and wakes to what the
first committed. A _claim_terminal that had quietly become a
plain SELECT passes every unit test in this Story and fails
that one.
The replay proof counts rows in the database after two independent settles, not calls into a mock. A test that asserted "the writer was called once" would pass with the unique constraint removed.
What this Story does NOT close
The card and the sections still do not complete. The
fan-out fires, and its card and segment legs stand down — because
src/shared/context/card_pass.py and
src/shared/context/segmentation.py still invoke Blake with
accept="text/event-stream", and Blake has refused that on
every mode since Story-2958. The card leg reports
card_pass_skipped outcome=no_card_artifact; segmentation
falls back to its flat D13 section. Recorded, not silent — but not
complete. Filed as pramaan-spec Bug-3354, functions lane,
under this Story's own Epic-3286 — a named ticket with an owner, not a
paragraph in a walkthrough nobody re-reads.
Fixing it means moving both passes onto the artifact rail, which turns a
synchronous seven-step fan-out into a chained applier across three
artifact deliveries, and touches the CE-18 backfill and the D30 re-read
door as well. This Story is deliberately shaped to be what that chains
off: the apply dispatch is a schema lookup, so
document.card.v1 and document.sections.v1
appliers land there and nowhere else, and
write_pending_artifact_run(document_id=...) already gives
their runs a projection target.
Four more callers are dead the same way and, as of 2026-08-03, unticketed:
insight_brief.py, understanding_pass.py,
deep_read_orchestrator/handler.py,
clerk_worker/runner.py.
And what is deleted
_StreamResult, _StreamFailure and
_persist_production_effects are gone in the same PR, along
with the UNREACHABLE banner and the not_yet_applied /
awaits: Story-3325 fields on the dispatch log line. The
production-effects block was unreachable twice over: the classify route
never emitted a family or a card event either. Nothing stays temporary.