Atlas's Step-5 review was supposed to type out live — the matter description streaming token-by-token over the ~2s generation window. Instead it arrived in 1–2 jumps. Every layer was already stream-capable, which made the bug counterintuitive: nobody was obviously buffering.
| Layer | State |
|---|---|
Brain (app/atlas/runtime.py, pramaan-agents) | Already emits a turn.delta per BAML partial — granular, one yield per token-ish chunk. |
| AgentCore Runtime | Already SSE-frames each yielded line as data: "<json>" (Bug-1229). But it only flushes those frames incrementally when the caller negotiates streaming; otherwise it collects the entire generator and returns one blob at the end. |
Broker (src/clerk_worker/runner.py, this repo) | Reads body.iter_chunks(4096), splits on newlines, unwraps data:, and pushes per parsed delta. Forwards data the instant it arrives — never waits to fill a buffer. |
| App (pramaan-app) | Renders every delta the moment it lands — verified live, caught the description mid-sentence. |
So three of four layers were stream-correct and the broker read was instant. The bunching was upstream of the broker read: the runtime handed the broker the whole stream at once, because of how the broker asked for it.
The broker invoked AgentCore with accept="application/json". Per the AgentCore InvokeAgentRuntime contract, application/json means buffer the entire generator and return one JSON blob when it finishes. SSE chunks flush incrementally only when the caller sends Accept: text/event-stream. The brain's per-token granularity and the runtime's SSE framing were both intact — they were just being collected behind a buffer the broker itself had requested.
| Piece | Change |
|---|---|
_invoke_brain | accept="application/json" → accept="text/event-stream" on invoke_agent_runtime. One line. The runtime now flushes each yielded frame as the brain produces it. |
Per-line push / data: unwrap | Unchanged. The iter_chunks + newline-split + data: unwrap already handle SSE framing (that framing was always present — Bug-1229). The v1 frame shape is untouched. |
| Regression lock | The happy-path unit test now asserts the broker negotiates text/event-stream, so a future revert to application/json fails CI instead of silently re-welding the stream. |
router_agents.py, retired by ADR-110) was not touched.ruff check src/clerk_worker/runner.py tests/.../test_runner.py -> clean pytest tests/unit/clerk_worker/test_runner.py -> 10 passed (incl. new assertion: invocation accept == "text/event-stream") No route/DTO/router change -> no OpenAPI snapshot impact.