# T1.6 — Prompt and output blob capture | Field | Value | |---|---| | Phase | P1 — Walking skeleton | | Size | S — under 1 day | | Status | Not started | | Flags | — | | Spec | inlined below | | Blocks | — | ## Goal Write the prompt to the blob store when it is built, and the output when the model responds. Record both as `BlobRef` on the attempt. ## Facts (inlined — no spec read needed) - Two capture points: `prompt-built` and model response. **The prior implementation had neither** — every downstream consumer worked from identifiers alone and could not answer "what did the agent actually see". - Prompts and outputs are large, so they go **by reference**; context partitions are identifiers and go inline (T1.5). That split is what makes lazy blob access (T4.3) possible. - Blobs are content-addressed within a tenant, never across (T0.7). - A `BlobRef` whose body later returns `None` is normal after reduction (T8.4) — the log carries a `Reduced { original, summary }` event saying what replaced it. ## Steps 1. At prompt assembly, serialize the final prompt, `BlobStore::put`, keep the returned `BlobRef`. 2. On model response, `put` the raw output, keep its `BlobRef`. 3. Attach both refs to the attempt record; commit them in the same `EventLog::commit` as the attempt transition, so a crash cannot leave a dangling ref in state with no log record. 4. Surface as `AttemptView.prompt: Option` and `AttemptView.output: Option`. 5. Test across a process restart: complete a run, drop the process, reopen the store, fetch the prompt by ref, assert the text. ## Acceptance - Prompt text retrievable from the blob store by ref **after a process restart**. ## Verify **Harness:** embedded store on a temp path that survives process exit; the test runs the agent in a **child process**, then reopens the store in the parent. **Integration test** — `tests/it_blob_capture_restart.rs`: 1. Child process: run one model step to completion, print the store path, exit. 2. Parent: reopen the store, read the attempt, take `prompt` and `output` refs. 3. `BlobStore::get` both; assert the prompt bytes equal the **assembled** prompt (with template variables substituted), not the template. 4. Re-hash both bodies; assert each matches its ref. 5. Crash case: arm a fault hook between the blob `put` and the commit; assert on reopen there is no attempt record pointing at a body-less ref, and no orphan body that no record points at. **Command:** `cargo test -p executor blob_capture -- --test-threads=1` **False pass:** - Reading the blob back in the same process from a warm cache. The restart is the point — an in-memory blob map passes everything else. - Asserting the prompt is non-empty rather than comparing to the expected assembled text. Capturing the template instead of the rendered prompt passes a non-empty check. ## Traps - Putting the blob outside the commit transaction and recording the ref inside it, or the reverse. Either way a crash leaves a ref with no body or a body no record points at. - Capturing the prompt template instead of the assembled prompt. The template is in the workflow definition; what the model saw is not. --- Background (not required to do this task): [rust-agentic-sys.md](../../../rust-agentic-sys.md) §8.6, §10.1 · [rust-agentic-task.md](../../../rust-agentic-task.md)