92 lines
4.0 KiB
Markdown
92 lines
4.0 KiB
Markdown
# T4.4 — Retention ordering guard
|
|||
|
|
|
||
|
|
| Field | Value |
|
||
|
|
|---|---|
|
||
|
|
| Phase | P4 — Verification |
|
||
|
|
| Size | S — under 1 day |
|
||
|
|
| Status | Not started |
|
||
|
|
| Flags | — |
|
||
|
|
| Spec | inlined below |
|
||
|
|
| Blocks | — |
|
||
|
|
|
||
|
|
## Goal
|
||
|
|
|
||
|
|
Reduction must not outrun verification or grading. Eligibility is *grading has
|
||
|
|
terminated*, not *grading succeeded*.
|
||
|
|
|
||
|
|
## Facts (inlined — no spec read needed)
|
||
|
|
|
||
|
|
- Eligible states for reduction: **`Graded`, `Ungraded`, or `Archived`**.
|
||
|
|
- **Never a step-level finish timestamp.** A step can finish, be reduced, and
|
||
|
|
then run-level verification finds nothing left to check.
|
||
|
|
- `Ungraded` belongs in that set, and the tighter-looking `Graded`-only rule is
|
||
|
|
the one that gets written. A run that never gets a score — a novel `TaskId`
|
||
|
|
with no reference, a tournament group that closed without it, a tenant over its
|
||
|
|
grading ceiling — is **finished**. Gating on `Graded` alone leaves it
|
||
|
|
irreducible forever.
|
||
|
|
- The tenants that hit this are the low-volume ones and the cost-capped ones: the
|
||
|
|
two populations least able to absorb unbounded storage, and the two least
|
||
|
|
likely to have anyone watching for it.
|
||
|
|
- `Graded`-only passes every other test in this phase and strands storage only in
|
||
|
|
production. So the `Ungraded`-is-reducible case must be asserted directly.
|
||
|
|
|
||
|
|
## Steps
|
||
|
|
|
||
|
|
1. Write `fn is_reducible(run_state) -> bool` matching exactly
|
||
|
|
`Graded | Ungraded | Archived`. Exhaustive match, no `_` arm.
|
||
|
|
2. Make reduction (T8.4) call it as a precondition and **reject** — a typed
|
||
|
|
error naming the current state — rather than skipping silently. A silent skip
|
||
|
|
is indistinguishable from success and leaves no trace to investigate.
|
||
|
|
3. Delete any path that reads a step-level finish time for retention decisions.
|
||
|
|
4. Wire the rejection into metrics so refusals are countable.
|
||
|
|
5. Test both halves: a run resting in `Grading` is rejected; a run terminal in
|
||
|
|
`Ungraded` is reduced.
|
||
|
|
|
||
|
|
## Acceptance
|
||
|
|
|
||
|
|
- A run still resting in `Grading` is **not reducible**, and attempting it is a
|
||
|
|
**rejected operation** rather than a silent skip.
|
||
|
|
- A run that reached terminal `Ungraded` **is** reducible — asserted directly.
|
||
|
|
|
||
|
|
## Verify
|
||
|
|
|
||
|
|
**Harness:** runs parked in each lifecycle state, plus the reduction entry point
|
||
|
|
from T8.4 (or a stub of it that calls the same guard).
|
||
|
|
|
||
|
|
**Integration test** — `tests/it_retention_guard.rs`:
|
||
|
|
1. Table-driven over **every** run state. For each, call reduce and assert:
|
||
|
|
- `Graded`, `Ungraded`, `Archived` → **permitted**;
|
||
|
|
- every other state, including `Grading` and `Verifying` → **rejected with a
|
||
|
|
typed error naming the current state**.
|
||
|
|
2. Assert the rejection is an `Err`, not `Ok(())`. A silent skip is
|
||
|
|
indistinguishable from success and leaves nothing to investigate.
|
||
|
|
3. **Assert `Ungraded` is reducible directly**, with a run that reached
|
||
|
|
`Ungraded { NoReference }`. This is the case a `Graded`-only implementation
|
||
|
|
fails, and it is the only assertion that catches it.
|
||
|
|
4. Assert no code path reads a step-level finish timestamp for retention —
|
||
|
|
grep, plus a test where a step finished long ago while the run is still
|
||
|
|
`Verifying`: reduction must be refused.
|
||
|
|
5. Assert refusals increment a metric, so a stuck reducer is visible.
|
||
|
|
|
||
|
|
**Command:** `cargo test -p retention guard`
|
||
|
|
|
||
|
|
**False pass:**
|
||
|
|
- Testing only the `Grading`-is-refused half. `if state == Graded` passes that,
|
||
|
|
passes every other test in P4, and strands storage in production for exactly
|
||
|
|
the low-volume and cost-capped tenants least able to absorb it. Step 3 is the
|
||
|
|
whole point of this task's acceptance criterion.
|
||
|
|
- A table covering only the states someone remembered. Enumerate the run-state
|
||
|
|
enum exhaustively with no `_` arm so a new state forces a decision.
|
||
|
|
|
||
|
|
## Traps
|
||
|
|
|
||
|
|
- `if state == Graded` — passes this phase, strands storage in production.
|
||
|
|
- Returning `Ok(())` on ineligible runs "because there is nothing to do". Then a
|
||
|
|
stuck reducer looks healthy.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
Background (not required to do this task):
|
||
|
|
[rust-agentic-sys.md](../../../rust-agentic-sys.md) §5.2, §10.3 ·
|
||
|
|
[rust-agentic-task.md](../../../rust-agentic-task.md)
|