core/evals/edge_budget/contract.md
Shay 66b8c7c431 feat(edge): edge-deployment budget gate — deterministic per-turn persistence cost
A2 of the refined sequencing. Proves (deterministically, not by assertion) what a
long-running CORE life costs to persist per turn on a constrained offline device.
Measures the Shape B+ checkpoint BYTES per turn (session_state.json) over the real
turn loop — bytes, not wall-clock latency (machine-dependent → flaky). Reuses the L10
continuity corpus.

Measured cliff: save_session_state re-serializes the FULL snapshot every turn, so
per-turn bytes are O(n) in the accumulated life — 3,811 → 88,189 bytes (23x) over 24
turns, ~1.3KB/vault-entry re-written every turn. That blocks continuous-life at the edge.

The gate encodes the edge REQUIREMENT (≤16 KiB/turn regardless of session length) as
xfail(strict): it fails today (documenting the cliff), runs green in CI, and flips to
a hard failure the moment incremental/append-only persistence (O(Δ)/turn) lands —
forcing us to retire it. Plus a regression ceiling (passes today) and a determinism
check (the byte metric is reproducible → a valid gate).

The fix is algorithmic (incremental persistence, Python/Ring-2), NOT a language
rewrite. Tagged core/array_codec.py as the locked reference contract for a future
gated Ring-1 Zig byte-exact codec (ADR-0196 G0-G8) — step 3, only after the O(Δ) fix
and only if this gate proves the codec is still the bottleneck. See contract.md.
2026-06-06 10:27:10 -07:00

3.6 KiB
Raw Blame History

Edge-budget lane — contract (A2)

Status: GATE (the edge axis made falsifiable). Telos: project-core-is-one-continuous-life deployed at the edge — offline, no-GPU, deterministic, on a constrained device (clinic / disaster-center / rural-school box).

What it proves

That a long-running CORE life stays affordable to persist per turn on a constrained device — measured deterministically, not asserted. The metric is the bytes the Shape B+ checkpoint writes each turn (engine_state/session_state.json), captured over the real turn loop (CognitiveTurnPipeline + ChatRuntime(persist_session_state=True)). Bytes, not wall-clock latency: latency is machine-dependent and would make the gate flaky in CI; the snapshot bytes are reproducible (proven by test_cost_metric_is_deterministic).

The cliff (measured, 24-turn soak)

save_session_state re-serializes the full snapshot every turn, so per-turn cost is O(n) in the accumulated life (the vault):

turn vault session_state.json bytes
0 2 3,811
2 8 11,884
4 14 20,228
8 25 32,831
12 37 48,993
16 48 62,965
20 60 78,564
23 68 88,189

Per-turn bytes grow ~linearly with vault size (~1.3 KB/entry, re-written every turn): growth ratio 23× over 24 turns, cumulative ~1.1 MB. Extrapolated, a life of 1,000 turns writes multiple MB per turn; 10,000 turns, tens of MB per turn. That is the edge-deployability blocker for continuous life.

The gate

  • test_per_turn_checkpoint_cost_is_within_edge_budgetxfail(strict=True). The edge requirement: max_per_turn_bytes ≤ 16 KiB regardless of session length (a bounded device budget; an O(Δ) implementation writes only the turn's delta, ~a few KB). Today's O(n) snapshot breaches it by turn ~4, so it is an expected failure that documents the cliff. When incremental/append-only persistence lands and per-turn bytes go flat, this xpassesstrict turns it into a hard CI failure → we retire the xfail. That is the falsifiable handle: the cliff is a red gate that the fix turns green.
  • test_persistence_cost_regression_ceiling — passes today; guards against making the cliff worse (per-turn ≤ 160 KiB, total ≤ 4 MiB).
  • test_cost_grows_with_accumulated_state_today — records the current O(n) signature on the record (so the fix is a visible delta).
  • test_cost_metric_is_deterministic — the byte series is reproducible across runs.

The fix this gate is waiting for

Incremental / append-only persistence — algorithmic, in Python (Ring 2). Persist only the turn's delta (new vault entries + the fixed-size field/anchor/scalar state) instead of re-serializing all history; periodic compaction; preserve bit-exact resume (Shape B+) and torn-write atomicity. The vault is append-mostly and the field is fixed-size, so O(Δ)/turn is natural, not a fight against the architecture. This is not a micro-optimization and not a language rewrite.

Zig-codec follow-up (tagged — NOT authorized)

Once persistence is O(Δ) and this gate is green, if the bounded per-turn codec is still the device bottleneck, core/array_codec.py is the locked reference contract (ADR-0196 decision rule 1) for a Ring-1 Zig byte-exact codec component — gated through the G0G8 ladder with a parity + determinism + mechanical-advantage proof, behind an explicit selector. A Zig rewrite of today's O(n) snapshot would only accelerate the wrong asymptotics, so it is step 3, after the algorithmic fix and after this gate proves it's needed. Tag lives in core/array_codec.py.