Files
poimen/tasks/T4.3-lazy-blob-access.md
T

86 lines
3.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 13 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)