143 lines
6.8 KiB
Markdown
143 lines
6.8 KiB
Markdown
# 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 1–3 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)
|