Files
poimen/tasks/T8.1-metering.md
T

106 lines
4.7 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.
# T8.1 — Metering
| Field | Value |
|---|---|
| Phase | P8 — Operability |
| Size | M — 1 to 3 days |
| Status | Not started |
| Flags | — |
| Spec | inlined below |
| Blocks | — |
## Goal
Attribute every model call to `(TenantId, RunId, Purpose)` and enforce separate
ceilings for judging and replay.
## Facts (inlined — no spec read needed)
- **A framework that spends users' model budget on grading must account for it.**
- Every model call — agent, judge, or proposer — is attributed to
`(TenantId, RunId, Purpose)`, where `Purpose` distinguishes **work** from
**grading** from **replay**.
- Per-tenant ceilings on grading spend are enforced **at the group boundary**,
where group size is known and a tournament can be skipped or downsampled before
it starts — not mid-tournament.
- **Replay is the expensive one and needs its own ceiling separate from
judging.** N variants × M tasks is N·M full agent runs plus judge calls.
- Grading cost is reported **next to grading value**. A loop that costs more than
the work it grades may still be worth running; it should never be an unpleasant
discovery.
- A skipped run must reach terminal `Ungraded` with `BudgetExhausted`, not rest
in `Grading`. **A cost ceiling that also strands storage is a bill that arrives
twice**, landing on the tenants least able to absorb it.
- The grading spend ratio ceiling itself is unset — metered here, policy elsewhere.
## Steps
1. Define `Purpose { Work, Grading, Replay }` and thread it through every model
call site. No default — the call site knows.
2. Record usage per call keyed `(TenantId, RunId, Purpose)`: tokens in/out, model,
and cost if a price table is configured.
3. Define per-tenant ceilings: one for grading, a **separate** one for replay.
4. Check the grading ceiling at the group boundary. Over the ceiling: skip or
downsample, record `UngradedReason::BudgetExhausted`, and transition the run to
terminal `Ungraded` (T5.11).
5. Never let a ceiling check touch the agent execution path. Work continues
regardless of grading spend.
6. Emit a report pairing grading cost with grading value per tenant.
7. Test: drive a tenant over its grading ceiling and assert agent throughput is
unchanged.
## Acceptance
- A tenant exceeding its grading ceiling has tournaments **skipped with a
recorded reason**, and **agent work continues unaffected**.
- The skipped runs reach terminal `Ungraded` rather than resting in `Grading`.
## Verify
**Harness:** a model provider wrapper recording `(TenantId, RunId, Purpose,
tokens)` for every call, plus a tenant configured with a **zero** grading ceiling
and a generous replay ceiling (and the reverse).
**Integration test**`tests/it_metering.rs`:
1. Run a full cycle: agent work, grading, replay. Assert every recorded call
carries a `Purpose`, and that the three purposes are all present. Assert no
call is attributed to a default or unknown purpose.
2. **Ceiling separation:** with grading ceiling = 0 and replay ceiling high,
assert grading is skipped and replay still runs. Then invert and assert the
opposite. One shared ceiling fails one of these.
3. **Skip happens before spend:** assert model calls under `Purpose::Grading` is
**exactly 0** when over the ceiling — the check is at the group boundary,
before any call.
4. Assert agent throughput and latency are **unchanged** while the tenant is over
its grading ceiling; compare against a baseline run.
5. Assert skipped runs reach **terminal `Ungraded { BudgetExhausted }`** and that
T4.4's `is_reducible` returns true for them.
6. Assert the cost-versus-value report is produced per tenant.
7. Assert attribution survives a cold re-fold — the usage lives in the log, not
only in state.
**Command:** `cargo test -p operability metering`
**False pass:**
- Step 3 asserting only the recorded reason. Enforcing the ceiling **after** the
calls produces the correct reason and spends the money anyway.
- Step 5 omitted: this is the "bill arrives twice" case — the tenant is capped on
spend and then also accumulates storage forever because the run never leaves
`Grading`.
- Step 2 omitted: a single ceiling passes every other assertion here, then replay
consumes the whole budget in production.
## Traps
- One shared ceiling for judging and replay. Replay dominates and consumes it.
- Checking the ceiling mid-tournament, which pays for half a group and produces
nothing.
- Leaving budget-skipped runs in `Grading`. The cost ceiling then also strands
storage.
---
Background (not required to do this task):
[rust-agentic-sys.md](../../../rust-agentic-sys.md) §10.3, §11.6, §12.4, §14.1 ·
[rust-agentic-task.md](../../../rust-agentic-task.md)