Files
poimen/tasks/T1.6-prompt-and-output-blob-capture.md
T

3.3 KiB

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<BlobRef> and AttemptView.output: Option<BlobRef>.
  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 testtests/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 §8.6, §10.1 · rust-agentic-task.md