Files
poimen/tasks/T8.4-reduction-and-tiering.md
T

143 lines
6.8 KiB
Markdown
Raw Normal View History

2026-08-17 23:05:20 -07:00
# T8.4 — Reduction and tiering
| Field | Value |
|---|---|
| Phase | P8 — Operability |
| Size | L — over 3 days |
| Status | Not started |
| Flags | — |
| Spec | inlined below |
| Blocks | — |
## Goal
Reduce episodes to a token ceiling, then move terminal reduced runs to cold
storage. Never rewrite a blob body; never edit a log record.
## Facts (inlined — no spec read needed)
Vocabulary, because the previous revision collided two of these: **`Archived`**
is a run state; **reduction** is the token-budget operation; **tiering** is the
move to cold storage.
- **Retention is measured in tokens**, because tokens are the currency of every
downstream consumer: what a replay costs, what fits in a judge's window, what
an export is billed at. Default ceiling 200k per run, per-tenant configurable.
- **On self-hosted weights that ceiling is not free to choose.** It is bounded by
`max_context_tokens / 2` (T5.2), because a pairwise judge reads two episodes
into one KV cache and two 200k episodes do not fit on any single device. Where
the derived bound is tighter, **the bound wins** and the judge reads a
further-reduced view.
Loss order is fixed:
| Kept | Reduced | Dropped |
|---|---|---|
| every transition record | blob bodies → summary blob | raw text on dead branches |
| context partitions, tool info, usage | dead-branch attempts → summary | |
| verifier results, grades | | |
- **Reduction never rewrites a blob and never edits the log.** Blobs are
content-addressed, so replacing a body under its existing ref makes the ref a
lie; repointing the log at a new ref is the history rewrite the append-only
rules forbid.
- Instead: write the summary as a **new** blob, append a
`Reduced { original: BlobRef, summary: BlobRef }` event, and **only then**
delete the original body. The reduction is a later fact about an earlier
record, not a change to it. `BlobStore::get` on the original returns `None`,
and the fold knows why and what stands in its place — so drop-and-re-fold from
LSN 0 still yields byte-identical state, which it would not if the mapping
lived only in the projection.
- The transition sequence always survives. What reduces is **text**, because it
dominates token count and is the only part with a cheap lossy representation. A
reduced episode can still be graded, attributed and structurally rewound — it
just cannot be replayed verbatim.
- **Tiering: moved, not copied**, with local rows deleted only after the remote
commit acknowledges.
- Eligibility is `Graded`/`Ungraded`/`Archived` (T4.4) — grading terminated.
- Open: what produces the summary. A model call makes reduction
non-deterministic, which interacts badly with replay; extractive or structural
reduction may suffice.
## Steps
1. Compute the effective ceiling as `min(configured_ceiling, max_context_tokens / 2)`
from T5.2. Never read the configured value alone.
2. Gate on T4.4's `is_reducible`. Reject, do not skip.
3. Reduce in the fixed loss order: blob bodies to summaries first, then
dead-branch attempts, then raw text on dead branches.
4. For each reduction: `put` the summary blob → `commit` the
`Reduced { original, summary }` event → **then** `delete` the original body.
That order survives a crash at any point.
5. Make the fold handle `Reduced` so a `None` from `get` is explained by the log.
6. Tiering: write to cold storage, wait for the remote commit acknowledgement,
then delete local rows. Crash between the two leaves a duplicate, not a loss —
so make the move idempotent.
7. Verify by re-hashing every surviving `BlobRef` against its body.
## Acceptance
- A run exceeding the ceiling reduces; **transitions survive intact**; a reduced
episode is **still gradeable**.
- Every surviving `BlobRef` still hashes to its content — asserted by
**re-hashing**, since a rewritten body type-checks silently.
- Drop the state tables and re-fold a reduced run from LSN 0: state is
**byte-identical**. This fails if the original→summary mapping lives anywhere
but the log.
- Tiering is **idempotent under a crash between remote commit and local delete**.
## Verify
**Harness:** a run built to exceed the token ceiling, T0.8's
`assert_refold_identical`, and a cold-storage stub that can fail between remote
commit and local delete.
**Integration test**`tests/it_reduction_tiering.rs`:
1. Reduce a run over the ceiling. Assert the **transition sequence is intact**
compare the event list before and after; only `Reduced` events were added,
none removed or altered.
2. Assert the reduced episode is **still gradeable**: run it through the grading
path and assert a real `Score`.
3. **Re-hash every surviving `BlobRef`** against its body and assert equality. A
rewritten body type-checks silently and this is the only assertion that sees
it.
4. **The load-bearing test:** drop the state tables, re-fold the reduced run from
LSN 0, assert **byte-identical** state. This fails if the original→summary
mapping lives anywhere but the log.
5. Assert `get` on an original ref returns `Ok(None)` and that the fold explains
it via the `Reduced` event.
6. **Ordering under crash:** arm faults at each of the three points (after `put`,
after `commit`, after `delete`). Assert no state exists where a record points
at a deleted body with no `Reduced` event.
7. **Ceiling derivation:** configure a 200k ceiling with a hardware-derived bound
of 80k; assert the **bound wins** and reduction targets 80k.
8. **Tiering idempotence:** crash between remote commit and local delete; re-run.
Assert one copy remains, no data lost, operation converges.
9. Assert reduction refuses on a run in `Grading` (T4.4).
**Command:** `cargo test -p retention --features test-hooks reduction -- --test-threads=1`
**False pass:**
- Step 4 omitted. Storing the original→summary mapping in the projection makes
steps 13 pass perfectly and silently destroys drop-and-re-fold, which is the
property the whole durability design rests on.
- Step 3 replaced by "the ref still resolves". A rewritten body resolves fine.
- Step 7 omitted: reading the configured ceiling alone passes every test on
hardware nobody checked, and fails at the first real judge call.
- Step 6 with faults only after `delete`, which is the safe point.
## Traps
- Overwriting the blob body under its existing ref. It type-checks, it saves an
event, and it makes the ref a lie.
- Repointing the log record at the summary ref. That is a history rewrite.
- Deleting the original before the `Reduced` event commits. A crash then leaves a
ref with no body and no explanation.
- Reading the configured ceiling without the hardware-derived bound.
---
Background (not required to do this task):
[rust-agentic-sys.md](../../../rust-agentic-sys.md) §8.6, §8.7, §10.3, §14.2, §18 ·
[rust-agentic-task.md](../../../rust-agentic-task.md)