86 lines
3.3 KiB
Markdown
86 lines
3.3 KiB
Markdown
# T4.3 — Lazy blob access
|
||||
|
|
|
|||
|
|
| Field | Value |
|
|||
|
|
|---|---|
|
|||
|
|
| Phase | P4 — Verification |
|
|||
|
|
| Size | S — under 1 day |
|
|||
|
|
| Status | Not started |
|
|||
|
|
| Flags | — |
|
|||
|
|
| Spec | inlined below |
|
|||
|
|
| Blocks | — |
|
|||
|
|
|
|||
|
|
## Goal
|
|||
|
|
|
|||
|
|
A verifier that needs no text performs no blob reads.
|
|||
|
|
|
|||
|
|
## Facts (inlined — no spec read needed)
|
|||
|
|
|
|||
|
|
- Context partitions hold **identifiers** — packed, available-but-not-packed,
|
|||
|
|
dropped — and are small enough to inline. Prompts and outputs are large and go
|
|||
|
|
**by reference**.
|
|||
|
|
- Laziness matters concretely: a verifier that shells out and checks an exit code
|
|||
|
|
needs none of this. Making every verifier carry prompt text penalizes the
|
|||
|
|
common case and blows broker payload limits.
|
|||
|
|
- `VerifierCtx.blobs: Arc<dyn BlobStore>` is the mechanism — the verifier calls
|
|||
|
|
`get` if and only if it wants a body.
|
|||
|
|
- `get` may return `None` after reduction (T8.4); that is a normal answer, and a
|
|||
|
|
verifier must not treat it as an error.
|
|||
|
|
|
|||
|
|
## Steps
|
|||
|
|
|
|||
|
|
1. Confirm `EpisodeView` carries `BlobRef` only. No `prompt_text` field, no
|
|||
|
|
eagerly-resolved cache.
|
|||
|
|
2. Pass the store handle through `VerifierCtx.blobs`.
|
|||
|
|
3. Instrument the store with a call counter behind a test-only wrapper —
|
|||
|
|
counting is what turns this from an intention into an assertion.
|
|||
|
|
4. Write the exit-code verifier used as the acceptance case: it reads the attempt
|
|||
|
|
state and nothing else.
|
|||
|
|
5. Write a second, text-reading verifier and assert its call count is exactly the
|
|||
|
|
number of refs it asked for — no over-fetch, no prefetch-all.
|
|||
|
|
|
|||
|
|
## Acceptance
|
|||
|
|
|
|||
|
|
- An exit-code verifier records **zero** `BlobStore::get` calls.
|
|||
|
|
|
|||
|
|
## Verify
|
|||
|
|
|
|||
|
|
**Harness:** a counting `BlobStore` decorator wrapping the real store, injected
|
|||
|
|
through `VerifierCtx.blobs`.
|
|||
|
|
|
|||
|
|
**Integration test** — `tests/it_lazy_blobs.rs`:
|
|||
|
|
1. Run a real run with an exit-code verifier that reads only attempt state.
|
|||
|
|
2. Assert the counter is **exactly 0**.
|
|||
|
|
3. Run a text-reading verifier that resolves 2 refs. Assert the counter is
|
|||
|
|
**exactly 2** — not more (no prefetch) and not fewer (no silent cache hit
|
|||
|
|
masking a missing read).
|
|||
|
|
4. Assert `EpisodeView` has no field holding blob **bytes** — a compile-level
|
|||
|
|
check if the type is closed, otherwise a serialization size assertion on a run
|
|||
|
|
with a 1 MB prompt: the view must stay small.
|
|||
|
|
5. Reduction interaction: delete a blob body, then run the text verifier; assert
|
|||
|
|
`get` returns `Ok(None)` and the verifier handles it without erroring.
|
|||
|
|
6. Assert the counter is per-run, not global, so parallel tests do not mask each
|
|||
|
|
other.
|
|||
|
|
|
|||
|
|
**Command:** `cargo test -p verify lazy_blobs`
|
|||
|
|
|
|||
|
|
**False pass:**
|
|||
|
|
- Counting at the store level while a caching layer sits above it. The count then
|
|||
|
|
measures cache misses, not verifier behaviour. Wrap the handle the verifier
|
|||
|
|
actually receives.
|
|||
|
|
- Step 4 omitted: a `prompt_text` convenience field makes every verifier pay, and
|
|||
|
|
steps 1–3 still pass if the resolution happens at view-build time rather than
|
|||
|
|
through `cx.blobs`. The size assertion catches that.
|
|||
|
|
|
|||
|
|
## Traps
|
|||
|
|
|
|||
|
|
- A "convenience" `EpisodeView::prompt_text()` that resolves on access. Every
|
|||
|
|
verifier then pays, invisibly.
|
|||
|
|
- Prefetching all refs when the view is built, which is the same regression one
|
|||
|
|
layer earlier.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
Background (not required to do this task):
|
|||
|
|
[rust-agentic-sys.md](../../../rust-agentic-sys.md) §10.1 ·
|
|||
|
|
[rust-agentic-task.md](../../../rust-agentic-task.md)
|