83 lines
3.3 KiB
Markdown
83 lines
3.3 KiB
Markdown
# 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 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)
|