Files
poimen/tasks/T0.8-fold-and-re-derive.md
T

93 lines
3.9 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.
# T0.8 — Fold and re-derive
| Field | Value |
|---|---|
| Phase | P0 — Foundations |
| Size | M — 1 to 3 days |
| Status | Not started |
| Flags | — |
| Spec | inlined below |
| Blocks | P1 gate |
## Goal
The projection from log to materialized state, as a pure function. This task
establishes the property every later phase re-runs.
## Facts (inlined — no spec read needed)
- **Nothing derived is authoritative.** Materialized state is a cache of the log.
If it cannot be dropped and rebuilt byte-identically, it has hidden inputs and
that is a bug.
- The fold is pure: `fold(state, record) -> state`. No clock reads, no random,
no filesystem, no map iteration order leaking into the output.
- Reduction (T8.4) is designed around this property: it appends a
`Reduced { original, summary }` event rather than editing the log or rewriting
a blob, precisely so the fold still reaches the same state from LSN 0.
- Restart folds forward from the newest checkpoint (T2.5); `None` means fold from
LSN 0. Checkpoints are an optimization only — deleting every checkpoint costs
startup time and nothing else.
## Steps
1. Define the materialized state type: runs, branches, attempts, refs, intent
status. Whatever the query surface (T1.7) needs and nothing more.
2. Write `fn fold(state: &mut State, record: &LogRecord)` as a pure function.
Take no `&self`, no store handle, no `Instant::now()`.
3. Use ordered collections (`BTreeMap`) anywhere the state is serialized, so
byte-identity does not depend on hash seed.
4. Write `rebuild(log, key) -> State`: read from LSN 0 (or from the newest
checkpoint once T2.5 lands), fold every record in order.
5. Write the property test as a **reusable helper**, not a one-off: drop the
state tables, `rebuild`, assert the serialized bytes match. Every subsequent
phase calls this helper on its own fixtures.
6. Wire that helper into P1P8 fixture suites as they land.
## Acceptance
- Drop the state tables, re-fold from LSN 0, assert byte-identical state.
- The helper is exported and used by at least one later phase's fixture set.
## Verify
**Harness:** export `assert_refold_identical(&log, key)` as a **public test
helper from this crate**. Every later phase calls it on its own fixtures; that
reuse is the deliverable, not the one-off test.
**Integration test**`tests/it_refold.rs`:
1. Build a log containing a retry, a rewind (once T2.3 lands) and a reduction
(once T8.4 lands) — the three cases with non-trivial fold logic.
2. Snapshot `serialize(state)`.
3. Drop the state tables entirely.
4. `rebuild` from LSN 0.
5. Assert **byte equality** of the serialized state, not `PartialEq`.
6. Repeat the fold twice in the same process and assert both runs produce
identical bytes — catches hash-seed and iteration-order leakage.
7. Purity check: run the fold with the system clock moved forward and with a
different `TZ`; assert the bytes are unchanged.
**Command:** `cargo test -p projection refold`
**False pass:**
- Comparing states with derived `PartialEq`. `HashMap` compares order-insensitively,
so byte-identity can be broken while the test is green — and byte-identity is
the actual property.
- Testing on a log with one linear run and no branches, retries or reductions.
Every implementation passes that.
- Running the fold once and comparing to the live state that was built by the
same code path incrementally — assert against a committed expected snapshot for
at least one fixture.
## Traps
- `HashMap` in serialized state — iteration order varies and byte-identity fails
intermittently, which reads as flakiness rather than as the bug it is.
- Any hidden input: a timestamp taken at fold time, a config value read from the
environment, a default filled in from the current schema version.
---
Background (not required to do this task):
[rust-agentic-sys.md](../../../rust-agentic-sys.md) §1, §8.6 ·
[rust-agentic-task.md](../../../rust-agentic-task.md)