This story is the moment the Lambda stops waiting (RFD-AGT-014). For an artifact route the
broker now does four things and goes home:
1. tx: pipeline record pending
+ agents.agent_run (delivery_status='awaiting_artifact',
expected_key, deadline_at, run_origin='system', trace context)
2. mint: assume the Chore-2978 signing role, run the D9 mint guard,
presign ONE create-only PUT URL (expiry = budget + slack + write allowance)
3. invoke_agent_runtime(accept='application/json') with the
result_delivery block stamped into the envelope
4. return. Hold nothing. (~2s — the brain keeps working)
delivery_status='awaiting_artifact' AND deadline_at < now()) still finds the run.
The key is agent-runs/{firm_id}/{yyyymm}/{run_id}/{seat}/result.json — the whole string
composed by the broker, {yyyymm} from the broker clock. The brain composes no part of it;
the presigned URL, not any prefix-wide IAM grant, binds the brain to that exact key. The outcome
(success | failure) is an in-envelope discriminator, not a second key. The brain's PUT
carries If-None-Match: *, which the Chore-2929 bucket policy forces — a second write is
refused by S3 with 412, and that 412 is the detector for the empty-key window (D21).
invoke_budget_s + delivery_slack_s + write_allowance_s
<= REMAINING signing-credential lifetime AT MINT ← never a nominal figure
<= 3,600 s ← the MEASURED role-chaining cap (Spike-2928)
| Seat | budget + slack + write allowance | Fits a fresh 1h session? |
|---|---|---|
| Registrar | 240 + 300 + 60 = 600s | yes |
| Deep Read | 700 + 700 + 60 = 1,460s | yes |
| Indigo | 840 + 840 + 60 = 1,740s | yes |
| The 8-hour ambition | 28,800 + 28,800 + 60 = 57,660s | refused at startup, term named: invoke_budget_s |
The guard reads the STS-returned Expiration of the assumed signing session — never the
nominal 3,600s. A cached session that can no longer cover the run is refreshed with one re-assume;
a run that cannot fit even a fresh session is refused with MintGuardRefusalError naming
the term that blew. A nominal figure would make the same code succeed at 10:00 and fail at 10:07 —
the Bug-2381 pattern in a new costume, and it is planted-broken tested.
X-Amz-Signature / X-Amz-Credential /
X-Amz-Security-Token values anywhere, as a backstop. (3) The block is stamped as a sibling
of the envelope's input — the prompt-assembly assertion proves it can never ride
input, history, or counsel_message into a model context (Indigo
ingests adversarial documents daily).
traceparent/tracestate are captured AT the pending-row write and stored on the
row (migration 0216) so the sweeper can LINK terminated runs, and stamped into the envelope's
meta so the brain echoes them into the artifact and the S3 object metadata — the results
consumer (#2982) then parents its AGENT_RESULT_DELIVERED span from the same trace id.
The two span names (functions.agent_result.delivered,
functions.agent_run.swept) are registered in code per the ADR-124 amendment (Chore-2935)
ahead of their first use. A dedicated test proves OTel baggage planted with matter/client identifiers
never reaches the stamped carrier — ADR-124's "attribute, never baggage" rule, asserted for this hop.
Every seam emits a structured agent_delivery.outcome line — a skip with no log is a design defect:
| Outcome | Seam |
|---|---|
budget_exceeds_ceiling | a seat whose budget cannot fit the 3,600s ceiling |
slack_below_write_allowance | D7 floor guard — a slack that would mass-fail healthy runs |
mint_guard_refused | remaining credential lifetime at mint cannot cover the run |
presign_failed | the signing role could not mint |
invoke_failed_pending_row_visible | invoke failed AFTER the pending row was written — the row is sweeper-visible |
dispatched | the healthy path |
iter_chunks in the dispatch._PRESIGNED_PARAM_RE rule removed.alembic check reports no drift.
This story ships the mechanism only. The rail cutovers (Deep Read, registrar, understanding, Vega),
the results consumer (#2982), and the sweeper (#2980) are their own tickets; conversational/WS rails
are untouched and keep working. src/shared/agent_delivery is banned from ever holding a
stream, and that ban is a test.