(tasks) add tasks for harness
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
# 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)
|
||||
Reference in New Issue
Block a user