ouroboros/tests/test_usage_compaction.py
Ouroboros 2ed63f8aa9 fix(state): read the usage ledger outside STATE_LOCK
update_budget_from_usage held STATE_LOCK (4.0s timeout) across
ensure_legacy_imported, usage_breakdown and usage_projection, which
contend for the monetary lock (45.0s timeout). A short-timeout lock held
across a long-timeout one starved every state.json reader, /api/state
among them, and stalled the supervisor loop. The ledger read now happens
outside the critical section; only load, compare, mutate and save remain
inside.

The lock was held deliberately: reading the ledger outside it lets an
older concurrent snapshot acquire STATE_LOCK later and regress the
monetary fields. That invariant is preserved by an ordering marker
instead of by lock duration. The marker is the lexicographic pair
(compaction_epoch, max live row seq), derived by _usage_rows from the
same validated rows the buckets come from and exposed as the private
_ledger_high_water_seq. Neither component works alone: compaction starts
a fresh dense epoch, so the live seq restarts at 1, header seq is always
1, pre_compaction_seq is per-row provenance into the archived range, and
source_last_seq is the just-folded file's length. Only compaction_epoch
is globally monotonic, and only the pair orders snapshots both inside an
epoch and across a fold.

Any marker strictly lower than the saved one is refused, whether the
epoch or only the sequence differs. An earlier revision of this change
wrote a lower epoch on the theory that it could only mean a restore.
That is wrong, and no restore is needed to break it: a writer can read
at epoch N, a reserve_attempt can compact to N+1, a second writer can
save the post-compaction projection, and the delayed writer would then
overwrite state.json with stale money. Equal and higher markers take the
normal write path. A genuine restore can therefore freeze the
compatibility projection until usage_ledger_high_water_seq is cleared;
that belongs to the restore path, not to this writer, and the refusal is
logged rather than silent. Each decision carries a greppable substring
(FRESHNESS MARKER UNKNOWN, STALE SNAPSHOT REJECTED).

The marker and every persisted accounting field now come from one
validated snapshot: usage_breakdown renders the projection from the same
final rows, so two writers cannot share a marker while saving
projections read at different sequences. An unreadable ledger is
unknown, never zero: the previous projection is preserved,
update_budget_from_usage returns a typed false, and _handle_llm_usage
records projection_update_status=unavailable instead of claiming an
availability it does not have.

usage_breakdown's private provenance is filtered at the gateway
boundary, where the unbounded-budget branch reuses the whole mapping as
its /api/state accounting projection. The filter drops every
leading-underscore key, so the class is closed rather than this one key.

Measured on a copy of a real 33 MB ledger in a throwaway data root, one
writer against six readers for 25s: reader lock-timeout fallbacks 21 to
0, reader max latency 4.05s to 1.07s (the 4.0s timeout ceiling was being
hit), reader throughput 5019 to 20503 calls. With six writers, 18 to 0
across three runs. Writers alone are faster, not slower: 431 to 855
calls, p50 29.3ms to 16.6ms.
2026-09-20 04:14:09 +03:00

957 lines
45 KiB
Python

"""Pins the seq-preserving compaction pass over the monetary ledger.
Design contract: docs/USAGE_COMPACTION.md. The invariants
pinned here are monetary-authority invariants:
1. decimal-exact money before/after; the production projections render EQUAL;
2. in-flight (unsettled) rows never fold and stay transitionable;
3. crash between archive write and ledger swap leaves the ledger byte-identical;
4. budget enforcement sees the same numbers across compaction;
6. idempotent kinds (subscription/external/legacy) never fold, so their replay dedup keeps working;
7. trigger policy: config SSOT threshold, thrash guard, verify-abort = no-op.
The reader side of the same organ — invariant 5 (the model-send join across
chained compactions) and invariant 8 (baseline rows are legal only as the
leading block) — lives in ``tests/test_usage_compaction_archive.py``; the
fixtures both modules share live in ``tests/fixtures_usage_compaction.py``.
"""
from __future__ import annotations
import base64
import decimal
import errno
import json
import logging
import os
import pathlib
import stat
import time
from decimal import Decimal
import pytest
from ouroboros import platform_layer
from ouroboros import usage_accounting as ua
from ouroboros import usage_compaction as uc
from ouroboros.agent_startup_checks import _hot_store_thresholds
from ouroboros.usage_ledger import UsageLedgerCorrupt, _validate_records
from tests import fixtures_usage_compaction as _fixtures
from tests.fixtures_usage_compaction import (
_append_raw_row,
_compact,
_ledger_lines,
_ledger_rows,
_raced_row,
_request,
_seed_mixed_ledger,
_settle,
)
# The three fixtures are re-exported through the module object rather than
# imported by name: a test's ``data_root`` parameter would shadow a bare
# import of the same name. pytest resolves them from this module either way.
compacted = _fixtures.compacted
data_root = _fixtures.data_root
data_root_any_tier = _fixtures.data_root_any_tier
def _decimal_money(rows):
"""Exact decimal (cost, bound) totals over FINAL rows, strings included.
Summed under an INDEPENDENT wide context — wider than the compactor's own
— so this oracle stays an oracle: a helper that rounds the same way the
code under test rounds cannot see the code under test round."""
finals = {}
for row in rows:
finals[str(row.get("attempt_id"))] = row
with decimal.localcontext() as context:
context.prec = 200
cost = Decimal(0)
bound = Decimal(0)
for row in finals.values():
if str(row.get("kind") or "") == "usage_baseline":
continue
value = row.get("cost_usd")
if value is not None and str(row.get("state") or "") == "settled":
cost += Decimal(str(value))
upper = row.get("reservation_upper_bound_usd")
if upper is not None:
bound += Decimal(str(upper))
return cost, bound
def _lock_path(data_root):
return data_root / "state" / "usage_attempts.lock"
def _charge_survived(data_root, injected, before_money):
"""The raced charge is in the live ledger, no baseline landed, no temp
residue, and money = before + that charge: the swap was refused whole."""
rows = _ledger_rows(data_root)
assert injected["attempt_id"] in {str(row.get("attempt_id")) for row in rows}
assert not any(row.get("kind") == "usage_baseline" for row in rows)
_validate_records(rows)
ledger_path = data_root / ua.LEDGER_REL
assert not list(ledger_path.parent.glob(f".{ledger_path.name}.tmp.*"))
assert _decimal_money(rows) == (before_money[0] + Decimal("0.25"), before_money[1])
def _snapshot_looks(monkeypatch, on_look=lambda looks: None):
"""Record every snapshot verdict the pass asks for, in order; ``on_look``
runs right after each answer — the instant an intrusion would land on it."""
looks: list = []
real = uc._snapshot_intact
def counting(path, raw):
looks.append(real(path, raw))
on_look(looks)
return looks[-1]
monkeypatch.setattr(uc, "_snapshot_intact", counting)
return looks
def _projection_snapshot(data_root):
def breakdown(**kwargs):
result = ua.usage_breakdown(data_root, **kwargs)
# The compatibility writer's freshness marker advances across a real
# compaction; monetary/non-money projection equality intentionally
# excludes that ordering fact.
result.pop("_ledger_high_water_seq", None)
return result
return (
ua.usage_projection(data_root),
ua.usage_projection(data_root, root_task_id="root"),
ua.usage_projection(data_root, root_task_id="root2"),
breakdown(),
breakdown(root_task_id="root"),
breakdown(task_id="t2"),
)
# --- 1 + 4: monetary exactness and budget equality ---------------------------
def test_compaction_preserves_money_and_projections_exactly(data_root):
_seed_mixed_ledger(data_root)
before_rows = _ledger_rows(data_root)
before_money = _decimal_money(before_rows)
before_projection = _projection_snapshot(data_root)
before_review = ua.skill_review_usage(
data_root, review_skill="skill-x", review_wave_id="w1")
receipt = _compact(data_root)
assert receipt is not None
assert receipt["folded_row_count"] > 0
after_rows = _ledger_rows(data_root)
assert len(after_rows) < len(before_rows)
assert _decimal_money(after_rows) == before_money
assert _projection_snapshot(data_root) == before_projection
# Review-attributed attempts are retained: the per-attempt wave projection
# is unchanged, attempt ids included.
after_review = ua.skill_review_usage(
data_root, review_skill="skill-x", review_wave_id="w1")
assert after_review == before_review
assert after_review["attempt_ids"]
# Baseline block structure: one header first, groups after, dense seq.
header = after_rows[0]
assert header["kind"] == "usage_baseline"
assert header["seq"] == 1
assert header["source_sha256"]
assert [row["seq"] for row in after_rows] == list(range(1, len(after_rows) + 1))
group_kinds = {row["kind"] for row in after_rows[1:]}
assert "usage_baseline" not in group_kinds
# Money on group rows is carried as exact-decimal strings.
groups = [row for row in after_rows if row["kind"] == "usage_baseline_group"]
assert groups
assert all(isinstance(row.get("cost_usd"), str)
for row in groups if row.get("cost_usd") is not None)
def test_processing_components_survive_compaction_without_becoming_cash(data_root):
_seed_mixed_ledger(data_root)
receipt = {"requested": "fast", "submitted": "fast", "submittedNative": "fast",
"observed": "mixed", "observedNative": ["fast", "standard"],
"source": "native_telemetry", "reason": None}
for index, knowledge in enumerate(("exact", "unknown")):
request = ua.AttemptRequest("claudexor::test=model", "claudexor", drive_root=data_root,
task_id=f"mode-{index}", root_task_id="root", reservation_usd=1,
processing_preference="fast")
hold = ua.reserve_attempt(request)
ua.mark_dispatched(hold)
ua.settle_attempt(hold, {"processing": receipt, "cost_evidence": {
"knowledge": knowledge, "cashUsd": 0 if knowledge == "exact" else None,
"valuationUsd": 5.25, "valuationKnowledge": "exact",
"processing": {"nativeMode": "fast", "kind": "paid_credits", "source": "native_policy"}}},
cost_usd=0 if knowledge == "exact" else None, cost_final=knowledge == "exact")
ua.record_subscription_session("session-modes", drive_root=data_root, route="claude",
attempt_execution=[{"attemptId": "one", "harnessId": "claude", "processing": receipt,
"usageCost": {"cashUsd": 0, "cashKnowledge": "unknown",
"valuationUsd": 10, "valuationKnowledge": "exact", "unknownUsd": 2}}])
before = _projection_snapshot(data_root)
component = before[0]["processing_summary"]
assert component["valuation_usd"] == 20.5 and component["unclassified_usd"] == 2
assert component["unknown_cash_rows"] == 2 and component["observed_modes"] == {"mixed": 3}
assert _compact(data_root) is not None
assert _projection_snapshot(data_root) == before
assert _compact(data_root) is not None
assert _projection_snapshot(data_root) == before
def test_group_sums_survive_beyond_the_default_decimal_precision(data_root, monkeypatch):
"""10**28 + 1 is 29 digits: the ambient 28-digit context loses the 1."""
monkeypatch.setenv("TOTAL_BUDGET", "1e40")
exact = Decimal("10000000000000000000000000001")
_settle(data_root, cost=1e28, cost_final=True)
_settle(data_root, cost=1.0, cost_final=True)
before = _decimal_money(_ledger_rows(data_root))
assert before[0] == exact
assert _compact(data_root) is not None
rows = _ledger_rows(data_root)
groups = [row for row in rows if row["kind"] == "usage_baseline_group"]
assert len(groups) == 1
assert Decimal(groups[0]["cost_usd"]) == exact
assert _decimal_money(rows) == before
def test_budget_enforcement_sees_identical_numbers(data_root, monkeypatch):
monkeypatch.setenv("TOTAL_BUDGET", "10")
_settle(data_root, cost=4.0, cost_final=True, reservation_usd=4.0)
_settle(data_root, cost=4.0, cost_final=True, reservation_usd=4.0)
before = ua.usage_projection(data_root)
assert _compact(data_root) is not None
assert ua.usage_projection(data_root) == before
# Remaining ≈ 2: a 1.5 reservation fits, a 3.0 reservation does not —
# exactly as before compaction.
reservation = ua.reserve_attempt(_request(data_root, reservation_usd=1.5))
ua.release_attempt(reservation, "probe")
with pytest.raises(ua.BudgetExceeded):
ua.reserve_attempt(_request(data_root, reservation_usd=3.0))
def test_root_budget_enforcement_survives_compaction(data_root):
_settle(data_root, cost=8.0, cost_final=True, reservation_usd=8.0,
task_id="rt", root_task_id="rooted", root_limit_usd=10.0)
assert _compact(data_root) is not None
with pytest.raises(ua.BudgetExceeded) as excinfo:
ua.reserve_attempt(_request(data_root, reservation_usd=5.0, task_id="rt2",
root_task_id="rooted", root_limit_usd=10.0))
assert excinfo.value.limit_scope == "root"
projection = ua.usage_projection(data_root, root_task_id="rooted")
assert projection["limit_usd"] == 10.0
assert projection["settled_usd"] == 8.0
# --- 2: in-flight rows never fold -------------------------------------------
def test_unsettled_rows_survive_and_stay_transitionable(data_root):
reserved, dispatched = _seed_mixed_ledger(data_root)
assert _compact(data_root) is not None
states = {
str(row.get("attempt_id")): str(row.get("state"))
for row in _ledger_rows(data_root)
}
assert states[reserved.attempt_id] == "reserved"
assert states[dispatched.attempt_id] == "dispatched"
# Their lifecycle continues over the compacted file.
ua.settle_attempt(dispatched, {"prompt_tokens": 3, "completion_tokens": 1},
cost_usd=0.25, cost_final=True)
ua.release_attempt(reserved, "not_dispatched")
finals = {
str(row.get("attempt_id")): str(row.get("state"))
for row in _ledger_rows(data_root)
}
assert finals[dispatched.attempt_id] == "settled"
assert finals[reserved.attempt_id] == "released"
# --- 3: crash-safety ---------------------------------------------------------
def test_crash_at_the_ledger_rename_leaves_ledger_intact(data_root, monkeypatch):
_seed_mixed_ledger(data_root)
ledger_path = data_root / ua.LEDGER_REL
archive_dir = data_root / "archive" / "usage_ledger"
before_bytes = ledger_path.read_bytes()
real_replace = os.replace
observed: dict = {}
def crashing_replace(src, dst):
if pathlib.Path(dst) != ledger_path:
return real_replace(src, dst)
# The power cut lands ON the rename itself. By contract the archive
# segment is already durable at this instant and holds the exact source
# bytes: a swap that happened first would find no segment here.
observed["segments"] = [
path.read_bytes() for path in sorted(archive_dir.glob("segment_*.jsonl"))
]
raise OSError(errno.EIO, "injected power loss at the ledger rename")
monkeypatch.setattr(os, "replace", crashing_replace)
with ua._locked(data_root) as heartbeat:
with pytest.raises(OSError):
uc.compact_usage_ledger_locked(data_root, heartbeat=heartbeat)
assert observed["segments"] == [before_bytes]
assert ledger_path.read_bytes() == before_bytes
_validate_records(_ledger_rows(data_root))
assert not list(ledger_path.parent.glob(f".{ledger_path.name}.tmp.*"))
# The orphaned archive segment is tolerated; the ledger keeps working and a
# retry (without the injection) compacts.
monkeypatch.setattr(os, "replace", real_replace)
_settle(data_root, cost=0.5, cost_final=True)
assert _compact(data_root) is not None
@pytest.mark.skipif(platform_layer.IS_WINDOWS, reason="directory fsync and inode identity are POSIX (the ARCHITECTURE row discloses the Windows no-op)")
def test_archive_directory_chain_is_durable_before_the_swap(data_root, monkeypatch):
_seed_mixed_ledger(data_root)
archive_dir = data_root / "archive" / "usage_ledger"
real_fsync = os.fsync
synced: list = []
swapped: list = []
def recording_fsync(fd):
info = os.fstat(fd) # a descriptor being fsync'd is open by construction
synced.append((info.st_dev, info.st_ino))
return real_fsync(fd)
real_swap = uc._swap_ledger_fsync
def watched_swap(*args):
swapped.append(len(synced))
return real_swap(*args)
monkeypatch.setattr(os, "fsync", recording_fsync)
monkeypatch.setattr(uc, "_swap_ledger_fsync", watched_swap)
assert _compact(data_root) is not None
assert swapped, "the swap never ran"
before_swap = set(synced[: swapped[0]])
# Every directory whose entry the archive chain created must be durable
# BEFORE the live ledger is replaced — not just the segment's own parent.
for directory in (archive_dir, archive_dir.parent, data_root):
info = directory.stat()
assert (info.st_dev, info.st_ino) in before_swap, directory
def test_posix_directory_fsync_failure_aborts_before_the_swap(data_root, monkeypatch):
if platform_layer.IS_WINDOWS: # pragma: no cover - platform predicate
pytest.skip("directory fsync is a disclosed no-op on Windows")
_seed_mixed_ledger(data_root)
ledger_path = data_root / ua.LEDGER_REL
before_bytes = ledger_path.read_bytes()
real_fsync = os.fsync
def failing_fsync(fd):
if stat.S_ISDIR(os.fstat(fd).st_mode):
raise OSError(errno.EIO, "injected directory fsync failure")
return real_fsync(fd)
monkeypatch.setattr(os, "fsync", failing_fsync)
with ua._locked(data_root) as heartbeat:
with pytest.raises(OSError):
uc.compact_usage_ledger_locked(data_root, heartbeat=heartbeat)
assert ledger_path.read_bytes() == before_bytes
def test_the_directory_chain_is_re_synced_on_the_retry_after_a_failed_pass(data_root, monkeypatch):
"""A pass that died on the directory fsync leaves the directories PRESENT
but not durable; the retry must sync them again rather than skip them for
existing, or a crash after its swap loses the archive the swap relies on."""
real_fsync = os.fsync
test_posix_directory_fsync_failure_aborts_before_the_swap(data_root, monkeypatch)
assert (data_root / "archive" / "usage_ledger").is_dir() # present, durability unknown
monkeypatch.setattr(os, "fsync", real_fsync)
# The retry is the first-pass proof itself, run over the directories the
# failed pass left behind: every level is fsync'd again before the swap.
test_archive_directory_chain_is_durable_before_the_swap(data_root, monkeypatch)
@pytest.mark.skipif(platform_layer.IS_WINDOWS, reason="directory fsync and inode identity are POSIX (the ARCHITECTURE row discloses the Windows no-op)")
def test_the_swap_fsyncs_the_candidate_before_the_rename_and_its_directory_after(data_root, monkeypatch):
""""A crash during the swap leaves the old file or the new one, both valid"
rests on two calls: the candidate temp fsync'd BEFORE the replace (without
it the renamed inode can hold unwritten data — neither the old ledger nor
the approved new one) and the ledger's own directory after it, without
which the rename may not survive the power cut. The archive half has three pins."""
_seed_mixed_ledger(data_root)
ledger_path = data_root / ua.LEDGER_REL
real_fsync, real_replace = os.fsync, os.replace
synced: list = []
swap: dict = {}
def recording_fsync(fd):
synced.append(os.fstat(fd).st_ino)
return real_fsync(fd)
def watched_replace(src, dst):
if pathlib.Path(dst) == ledger_path:
swap.update(candidate=os.stat(src).st_ino, before=len(synced))
return real_replace(src, dst)
monkeypatch.setattr(os, "fsync", recording_fsync)
monkeypatch.setattr(os, "replace", watched_replace)
assert _compact(data_root) is not None
assert swap["candidate"] in synced[: swap["before"]] # the bytes, before the rename
assert ledger_path.parent.stat().st_ino in synced[swap["before"]:] # the directory, after
# --- 1b: the lock the pass runs under ----------------------------------------
def test_monetary_lock_is_owner_aware_and_the_pass_heartbeats_it(data_root, monkeypatch):
_seed_mixed_ledger(data_root)
requested: dict = {}
real_acquire = platform_layer.acquire_exclusive_file_lock
def spy(path, **kwargs):
requested.update(kwargs)
return real_acquire(path, **kwargs)
monkeypatch.setattr(platform_layer, "acquire_exclusive_file_lock", spy)
lock_path = _lock_path(data_root)
with ua._locked(data_root) as heartbeat:
# A LIVE owner is never evicted on elapsed time alone: a stolen monetary
# lock means two writers rewriting the same authority.
assert requested.get("owner_aware_stale") is True
# A pass that outlives the staleness window keeps its lockfile young
# for acquirers that judge by age only.
os.utime(lock_path, (0.0, 0.0))
assert uc.compact_usage_ledger_locked(data_root, heartbeat=heartbeat) is not None
assert lock_path.stat().st_mtime > time.time() - 60
def test_append_between_snapshot_and_swap_aborts_instead_of_erasing_it(data_root, monkeypatch):
_seed_mixed_ledger(data_root)
before_money = _decimal_money(_ledger_rows(data_root))
original_write = uc._write_new_file_fsync
injected: dict = {}
def racing_write(path, payload, root):
# A writer that got the lock (age-broken lock, foreign repair) lands a
# settled charge AFTER the compactor snapshotted the file.
injected.update(_append_raw_row(data_root, _raced_row("sess-raced")))
original_write(path, payload, root)
monkeypatch.setattr(uc, "_write_new_file_fsync", racing_write)
assert _compact(data_root) is None # refused the swap
_charge_survived(data_root, injected, before_money)
def test_a_lost_lock_aborts_the_pass_instead_of_swapping(data_root):
"""A heartbeat is an OWNERSHIP verdict: losing it abandons the pass.
A pass that keeps building after the lock left it would swap a snapshot
over whatever the new owner appended in the meantime — and a pass handed
no heartbeat at all has nothing to prove ownership with: refused, not run."""
_seed_mixed_ledger(data_root)
ledger_path = data_root / ua.LEDGER_REL
before = ledger_path.read_bytes()
for heartbeat in (
lambda: False, # evicted / re-created under us
lambda: (_ for _ in ()).throw(OSError("lock unreadable")),
None, # the wire dropped: a caller defect, refused
):
assert uc.compact_usage_ledger_locked(data_root, heartbeat=heartbeat) is None
assert ledger_path.read_bytes() == before
assert not (data_root / "archive" / "usage_ledger").exists() # not even an orphan
def test_the_long_build_and_verification_section_beats_the_lock(data_root, monkeypatch):
"""The build/verify span is the long one; it must renew the hold WHILE it
runs, not only at its edges."""
_seed_mixed_ledger(data_root)
beats: list = []
seen: dict = {}
real_build = uc._build_candidate
def watched_build(*args, **kwargs):
seen["entry"] = len(beats)
result = real_build(*args, **kwargs)
seen["exit"] = len(beats)
return result
monkeypatch.setattr(uc, "_build_candidate", watched_build)
with ua._locked(data_root) as heartbeat:
def counting():
beats.append(1)
return heartbeat()
assert uc.compact_usage_ledger_locked(data_root, heartbeat=counting) is not None
assert seen["exit"] > seen["entry"] # the build itself renewed the hold
assert len(beats) > seen["exit"] # and so did the verification after it
def test_no_writer_can_append_between_the_snapshot_check_and_the_swap(data_root, monkeypatch):
"""The compare->replace window is closed by the lock, not by luck."""
_seed_mixed_ledger(data_root)
ledger_path = data_root / ua.LEDGER_REL
real_replace = os.replace
observed: dict = {}
def probing_replace(src, dst):
if pathlib.Path(dst) == ledger_path:
# The legitimate append path IS this acquisition; at the instant of
# the swap it must find the monetary lock held.
observed["free"] = platform_layer.acquire_exclusive_file_lock(
_lock_path(data_root), timeout_sec=0.2, stale_sec=3600.0,
poll_sec=0.02, owner_aware_stale=True,
)
return real_replace(src, dst)
monkeypatch.setattr(os, "replace", probing_replace)
assert _compact(data_root) is not None
assert observed["free"] is None
def test_every_ledger_writer_refuses_when_the_lock_cannot_be_taken(data_root_any_tier, monkeypatch):
"""No unlocked fallback: a lock that cannot be taken is a typed refusal.
An append that gave up on the lock and wrote anyway is exactly the second
writer the compaction snapshot cannot see."""
_settle(data_root_any_tier, cost=1.0, cost_final=True)
reserved = ua.reserve_attempt(_request(data_root_any_tier, task_id="probe"))
ledger_path = data_root_any_tier / ua.LEDGER_REL
before = ledger_path.read_bytes()
monkeypatch.setattr(
platform_layer, "acquire_exclusive_file_lock", lambda *a, **k: None)
for write in (
lambda: ua.reserve_attempt(_request(data_root_any_tier)),
lambda: ua.mark_dispatched(reserved),
lambda: ua.record_unmetered_external_dispatch(
"ext-locked", drive_root=data_root_any_tier, model="m", task_id="t"),
):
with pytest.raises(ua.UsageAccountingError):
write()
assert ledger_path.read_bytes() == before
@pytest.mark.parametrize("intrusion", ("written_over", "erased"))
def test_a_swap_that_did_not_land_is_a_typed_failure_not_a_receipt(data_root, monkeypatch, intrusion):
"""Post-verify: the receipt describes the bytes that are actually there — and
a charge landed on the OLD inode inside the one syscall between the last proof
and the rename (an out-of-protocol holder) is erased by that rename, with
nothing left at the path to show it. The old inode, held open across the swap
(POSIX), still shows it: those bytes are quarantined, integrity is flagged, and
the pass raises instead of returning a receipt over an erased charge."""
if intrusion == "erased" and platform_layer.IS_WINDOWS: # pragma: no cover - platform predicate
pytest.skip("Windows cannot hold the destination open across os.replace: the loss stays silent, disclosed")
_seed_mixed_ledger(data_root)
ledger_path = data_root / ua.LEDGER_REL
real_replace = os.replace
landed: dict = {}
def lying_replace(src, dst):
if pathlib.Path(dst) == ledger_path and intrusion == "erased":
landed.update(_append_raw_row(data_root, _raced_row("sess-in-the-syscall"))) # on the old inode
real_replace(src, dst)
if pathlib.Path(dst) == ledger_path and intrusion == "written_over":
with open(dst, "ab") as handle:
handle.write(b'{"kind":"attempt","attempt_id":"late"}\n')
monkeypatch.setattr(os, "replace", lying_replace)
with ua._locked(data_root) as heartbeat:
with pytest.raises(UsageLedgerCorrupt):
uc.compact_usage_ledger_locked(data_root, heartbeat=heartbeat)
if intrusion == "erased":
row = json.loads((data_root / ua.QUARANTINE_REL).read_text(encoding="utf-8").splitlines()[-1])
assert json.loads(base64.b64decode(row["raw_base64"])) == landed # the erased charge, byte for byte
assert ua.usage_projection(data_root)["integrity_degraded"] is True
def test_an_append_between_the_recheck_and_the_replace_aborts_without_loss(
data_root, monkeypatch
):
"""The pre-swap re-check is not the last look: the live ledger is proven
unchanged again INSIDE the swap, after the candidate bytes are durable and
immediately before the rename — the last instant the replace can still be
refused. A row that lands after the outer re-check therefore survives."""
_seed_mixed_ledger(data_root)
before_money = _decimal_money(_ledger_rows(data_root))
injected: dict = {}
def land_after_the_recheck(looks):
if len(looks) == 2 and looks[-1]:
# The charge lands AFTER the outer pre-swap re-check answered
# "intact" and BEFORE the rename trusts that answer.
injected.update(_append_raw_row(data_root, _raced_row("sess-last-instant")))
_snapshot_looks(monkeypatch, land_after_the_recheck)
assert _compact(data_root) is None # the replace was refused
_charge_survived(data_root, injected, before_money)
def test_a_same_size_rewrite_between_the_recheck_and_the_replace_also_refuses(data_root, monkeypatch):
"""The in-swap look proves the live file is still the snapshot by size AND
by bytes. Every intrusion the other pins inject is an append, so the size
half alone would satisfy all of them — and a foreign repair that rewrites a
row in place, changing no length, is exactly the swap that must not land."""
_seed_mixed_ledger(data_root)
ledger_path = data_root / ua.LEDGER_REL
rewritten: dict = {}
def rewrite_after_the_recheck(looks):
if len(looks) == 2 and looks[-1] and not rewritten:
raw = ledger_path.read_bytes()
spot = raw.rindex(b'"attempt_id"') + 20 # inside the id, one byte for one byte
rewritten["bytes"] = raw[:spot] + bytes([raw[spot] ^ 1]) + raw[spot + 1:]
ledger_path.write_bytes(rewritten["bytes"])
_snapshot_looks(monkeypatch, rewrite_after_the_recheck)
assert _compact(data_root) is None # the replace was refused
assert ledger_path.read_bytes() == rewritten["bytes"] # and nothing was erased
def test_a_hold_lost_at_the_archive_is_seen_before_the_snapshot_is_trusted(
data_root, monkeypatch
):
"""The pass proves ownership immediately BEFORE each snapshot look: a
re-check answered while the lock already belongs to someone else is a
meaningless answer, so the loss must abort before it is even asked."""
_seed_mixed_ledger(data_root)
ledger_path = data_root / ua.LEDGER_REL
before = ledger_path.read_bytes()
archive_dir = data_root / "archive" / "usage_ledger"
looks = _snapshot_looks(monkeypatch)
def heartbeat():
# Ownership dies at the exact moment the archive segment lands.
return not list(archive_dir.glob("segment_*.jsonl"))
with ua._locked(data_root):
assert uc.compact_usage_ledger_locked(data_root, heartbeat=heartbeat) is None
assert ledger_path.read_bytes() == before
assert len(looks) == 1 # the post-archive re-check never ran
assert list(archive_dir.glob("segment_*.jsonl")) # the orphan stays, disclosed
def test_a_hold_lost_before_the_first_commit_look_writes_no_orphan(data_root, monkeypatch):
"""The FIRST commit beat — after the archive bound check, before the
pre-archive snapshot look — is load-bearing too: a hold already lost there
must abort before that look is asked and before any segment is written,
or the pass writes an orphan (and trusts an answer it may not use) on
behalf of a lock that is no longer its own."""
_seed_mixed_ledger(data_root)
ledger_path = data_root / ua.LEDGER_REL
before = ledger_path.read_bytes()
lost = {"hold": False}
real_bound = uc._archive_dir_bounded
def losing_bound(root):
lost["hold"] = True # ownership dies as the commit section is entered
return real_bound(root)
looks = _snapshot_looks(monkeypatch)
monkeypatch.setattr(uc, "_archive_dir_bounded", losing_bound)
with ua._locked(data_root):
assert uc.compact_usage_ledger_locked(
data_root, heartbeat=lambda: not lost["hold"]) is None
assert ledger_path.read_bytes() == before
assert looks == [] # the pre-archive look was never asked
assert not (data_root / "archive").exists() # and no orphan was written
def test_a_hold_lost_after_the_recheck_aborts_before_the_swap(data_root, monkeypatch):
"""Ownership is proven once more between the re-check and the rename: a
verdict that arrived while ours cannot license a replace that happens
after the hold left us."""
_seed_mixed_ledger(data_root)
ledger_path = data_root / ua.LEDGER_REL
before = ledger_path.read_bytes()
looks = _snapshot_looks(monkeypatch)
def heartbeat():
# Ownership dies the moment the pre-swap re-check has answered.
return len(looks) < 2
with ua._locked(data_root):
assert uc.compact_usage_ledger_locked(data_root, heartbeat=heartbeat) is None
assert ledger_path.read_bytes() == before # no swap: byte-identical
assert not any(row.get("kind") == "usage_baseline" for row in _ledger_rows(data_root))
assert len(looks) == 2 # the in-swap look is never asked once the hold is gone
def test_a_hold_lost_after_the_last_snapshot_look_refuses_the_rename(data_root, monkeypatch):
"""The in-swap look is not the last proof: ownership is proven again AFTER
it answered, immediately before the rename, so the only interval a robbery
can slip through is the rename syscall itself — not the milliseconds of a
full-file compare. A hold lost the moment that look answered True, the new
holder's charge landing right behind it, refuses the replace."""
_seed_mixed_ledger(data_root)
before_money = _decimal_money(_ledger_rows(data_root))
injected: dict = {}
looks = _snapshot_looks(monkeypatch)
def heartbeat():
if len(looks) < 3:
return True
if not injected: # lands after the last look answered, before the rename
injected.update(_append_raw_row(data_root, _raced_row("sess-after-last-look")))
return False
with ua._locked(data_root):
assert uc.compact_usage_ledger_locked(data_root, heartbeat=heartbeat) is None
_charge_survived(data_root, injected, before_money)
def test_a_hold_lost_while_the_temp_is_written_refuses_the_replace(data_root):
"""Writing and fsyncing the candidate temp can take arbitrarily long, so
an ownership proof taken before the swap began is stale by the rename.
The proof therefore lives INSIDE the atomic writer — once the temp bytes
are durable, immediately before the snapshot look and the rename — and it
runs FIRST: a hold lost while the temp was written refuses the replace,
and a charge the new holder appended survives byte-for-byte."""
_seed_mixed_ledger(data_root)
before_money = _decimal_money(_ledger_rows(data_root))
ledger_path = data_root / ua.LEDGER_REL
injected: dict = {}
def heartbeat():
# Ownership dies while the candidate temp is being written: the first
# proof asked with the temp on disk answers False — and the new
# holder's charge lands with it, exactly the row a rename would erase.
if not list(ledger_path.parent.glob(f".{ledger_path.name}.tmp.*")):
return True
if not injected:
injected.update(_append_raw_row(data_root, _raced_row("sess-new-holder")))
return False
with ua._locked(data_root):
assert uc.compact_usage_ledger_locked(data_root, heartbeat=heartbeat) is None
_charge_survived(data_root, injected, before_money)
@pytest.mark.parametrize("intrusion", ("append", "hold_lost"))
def test_a_refused_rename_re_proves_the_hold_and_the_snapshot_before_retrying(
data_root, monkeypatch, intrusion
):
"""The atomic replace retries a refused rename (a Windows sharing
violation), and a proof taken before the refused attempt is stale by the
next one: between the attempts a charge can land or the hold can leave us.
Ownership and the snapshot are re-proven before EVERY attempt, so a
refusal followed by either intrusion never reaches a second rename, and
the landed row survives byte-for-byte."""
_seed_mixed_ledger(data_root)
before_money = _decimal_money(_ledger_rows(data_root))
ledger_path = data_root / ua.LEDGER_REL
before = ledger_path.read_bytes()
real_replace = os.replace
attempts: list = []
state = {"owned": True}
injected: dict = {}
def refusing_replace(src, dst):
if pathlib.Path(dst) != ledger_path:
return real_replace(src, dst)
attempts.append(1)
if len(attempts) == 1:
if intrusion == "append":
injected.update(_append_raw_row(data_root, _raced_row("sess-between-attempts")))
else:
state["owned"] = False
raise PermissionError(errno.EACCES, "injected sharing violation")
return real_replace(src, dst)
monkeypatch.setattr(os, "replace", refusing_replace)
with ua._locked(data_root) as heartbeat:
assert uc.compact_usage_ledger_locked(
data_root, heartbeat=lambda: state["owned"] and heartbeat()) is None
assert attempts == [1] # the second rename never happened
if intrusion == "append":
_charge_survived(data_root, injected, before_money)
else:
assert ledger_path.read_bytes() == before
assert not list(ledger_path.parent.glob(f".{ledger_path.name}.tmp.*"))
def test_the_pass_refuses_on_the_name_tier_while_appends_continue(data_root_any_tier, tmp_path, monkeypatch, caplog):
"""Where the lock directory takes no kernel locks the monetary lock is a name protocol
only — exclusion the pass cannot prove — so compaction refuses (disclosed reason, ledger
byte-identical) while appends continue under the name protocol; the refusal is durable
and typed — ONE usage_ledger_compaction_refused event per process per data root."""
_seed_mixed_ledger(data_root_any_tier)
ledger_path = data_root_any_tier / ua.LEDGER_REL
before = ledger_path.read_bytes()
monkeypatch.setattr(
platform_layer, "kernel_file_locks_enforced", lambda path: False, raising=False)
# The FIRST refusals cannot write their event (a full disk, an unwritable
# logs/) — append_jsonl reports exhausted retries as False, not only as an
# exception. Neither is "already told": marking the root before the row lands
# downgrades the durable event to a log line for the life of the process.
landing_append = uc.append_jsonl
monkeypatch.setattr(uc, "append_jsonl", lambda *a, **k: (_ for _ in ()).throw(OSError("no space left")))
with caplog.at_level(logging.WARNING, logger="ouroboros.usage_compaction"):
assert _compact(data_root_any_tier) is None
monkeypatch.setattr(uc, "append_jsonl", lambda *a, **k: False)
assert _compact(data_root_any_tier) is None
monkeypatch.setattr(uc, "append_jsonl", landing_append)
assert ledger_path.read_bytes() == before
assert not (data_root_any_tier / "archive").exists()
assert any("name tier" in record.getMessage() for record in caplog.records)
# The reserve-path trigger refuses the same way, and the append it serves lands.
monkeypatch.setattr("ouroboros.config.USAGE_LEDGER_COMPACT_BYTES", 1)
uc._COMPACT_ATTEMPTS.clear()
_settle(data_root_any_tier, cost=0.5, cost_final=True)
rows = _ledger_rows(data_root_any_tier)
assert not any(row.get("kind") == "usage_baseline" for row in rows)
assert len(rows) == len(before.splitlines()) + 3 # reserved, dispatched, settled
# Two refusals in this process, ONE durable typed row: an operator (and the
# 20 MB tripwire) can tell the tier apart from "nothing foldable".
def refusals():
events = (data_root_any_tier / "logs" / "events.jsonl").read_text(encoding="utf-8").splitlines()
return [row for row in map(json.loads, events) if row.get("type") == "usage_ledger_compaction_refused"]
assert [row["reason"] for row in refusals()] == ["name_tier"] # the one that could land
spelling = tmp_path / "linked-data" # ~/Ouroboros -> ~/ouro: one root, two names
spelling.symlink_to(data_root_any_tier)
assert _compact(spelling) is None
assert len(refusals()) == 1 # ONE data root, however it is spelled
ledger_note = next(note for rel, _, note in _hot_store_thresholds() if rel == "state/usage_attempts.jsonl")
assert "name tier" in ledger_note and "usage_ledger_compaction_refused" in ledger_note
# --- 6: idempotent kinds never fold ------------------------------------------
def test_subscription_replay_still_dedups_after_compaction(data_root, compacted):
before = len(_ledger_rows(data_root))
attempt_id = ua.record_subscription_session(
"sess-1", drive_root=data_root, route="claudexor:claude", model="fable",
task_id="t5", root_task_id="root", spend_usd=0.5, reset_at="2026-09-02T00:00:00Z")
assert attempt_id.startswith("session-")
assert len(_ledger_rows(data_root)) == before # no duplicate row
with pytest.raises(ua.UsageAccountingError):
ua.record_subscription_session("sess-1", drive_root=data_root, route="other-route", model="fable",
task_id="t5", root_task_id="root", spend_usd=0.5)
before = len(_ledger_rows(data_root))
ua.record_unmetered_external_dispatch("ext-1", drive_root=data_root, model="ext-model", task_id="t6",
prompt_tokens=7, completion_tokens=3)
assert len(_ledger_rows(data_root)) == before
def test_legacy_import_rows_are_retained_and_reimport_dedups(data_root):
events = data_root / "logs" / "events.jsonl"
events.parent.mkdir(parents=True, exist_ok=True)
events.write_text(json.dumps({
"type": "llm_usage", "model": "m", "provider": "openai", "cost": 0.7,
"prompt_tokens": 5, "completion_tokens": 2, "task_id": "lt",
}) + "\n", encoding="utf-8")
(data_root / ua.IMPORT_REL).unlink()
ua.ensure_legacy_imported(data_root)
legacy_ids = {
str(row["attempt_id"]) for row in _ledger_rows(data_root)
if str(row.get("kind", "")).startswith("legacy_")
}
assert legacy_ids
_settle(data_root, cost=0.9, cost_final=True)
assert _compact(data_root) is not None
live_ids = {str(row["attempt_id"]) for row in _ledger_rows(data_root)}
assert legacy_ids <= live_ids # never folded
# Watermark loss: the resumable import replays against the LIVE ledger and
# appends nothing new.
before = len(_ledger_rows(data_root))
(data_root / ua.IMPORT_REL).unlink()
result = ua.ensure_legacy_imported(data_root)
assert result["rows_appended"] == 0
assert len(_ledger_rows(data_root)) == before
# --- 7: trigger policy -------------------------------------------------------
def test_reserve_path_compacts_only_past_config_threshold(data_root, monkeypatch):
_seed_mixed_ledger(data_root)
# The pass rewrites the whole monetary authority, so it is correct ONLY
# while the ledger lock is held: prove the hold at the moment of the call
# rather than trusting where the call site sits.
holds: list = []
renewed: list = []
original = uc.compact_usage_ledger_locked
def observing(root, **kwargs):
probe = platform_layer.acquire_exclusive_file_lock( # None: held by someone else
_lock_path(data_root), timeout_sec=0.05, stale_sec=3600.0, poll_sec=0.01,
owner_aware_stale=True,
)
# ... and the hold is WIRED THROUGH: without the heartbeat every ownership proof inside the
# pass is a no-op and the swap runs unproven; pinned at the only production caller, to THIS lock.
os.utime(_lock_path(data_root), (0.0, 0.0))
renewed.append(kwargs["heartbeat"]() is True and _lock_path(data_root).stat().st_mtime > time.time() - 60)
holds.append(probe is None)
if probe is not None:
platform_layer.release_exclusive_file_lock(_lock_path(data_root), probe)
return original(root, **kwargs)
monkeypatch.setattr(uc, "compact_usage_ledger_locked", observing)
size = (data_root / ua.LEDGER_REL).stat().st_size
monkeypatch.setattr("ouroboros.config.USAGE_LEDGER_COMPACT_BYTES", size * 10)
uc._COMPACT_ATTEMPTS.clear()
_settle(data_root, cost=0.1, cost_final=True)
assert not any(row.get("kind") == "usage_baseline" for row in _ledger_rows(data_root))
assert holds == [] # below threshold: the stat fast-path never enters the pass
monkeypatch.setattr("ouroboros.config.USAGE_LEDGER_COMPACT_BYTES", 1)
_settle(data_root, cost=0.1, cost_final=True)
assert renewed == [True], "a stub, not the held lock's heartbeat"
assert holds == [True]
assert any(row.get("kind") == "usage_baseline" for row in _ledger_rows(data_root))
def test_unprofitable_pass_is_throttled(data_root, monkeypatch):
# Only in-flight rows: nothing foldable, compaction must not thrash.
for task in ("a", "b"):
reservation = ua.reserve_attempt(_request(data_root, task_id=task))
ua.mark_dispatched(reservation)
monkeypatch.setattr("ouroboros.config.USAGE_LEDGER_COMPACT_BYTES", 1)
monkeypatch.setattr(
"ouroboros.config.USAGE_LEDGER_COMPACT_RETRY_GROWTH_BYTES", 10_000_000)
uc._COMPACT_ATTEMPTS.clear()
calls = []
original = uc.compact_usage_ledger_locked
def counting(root, **kwargs):
calls.append(1)
return original(root, **kwargs)
monkeypatch.setattr(uc, "compact_usage_ledger_locked", counting)
before = _ledger_lines(data_root)
with ua._locked(data_root) as heartbeat:
assert uc.maybe_compact_usage_ledger_locked(data_root, heartbeat=heartbeat) is False
assert _ledger_lines(data_root) == before # nothing foldable -> no-op
with ua._locked(data_root) as heartbeat:
assert uc.maybe_compact_usage_ledger_locked(data_root, heartbeat=heartbeat) is False
assert len(calls) == 1 # second call throttled by the growth guard
def test_verify_abort_on_foreign_noncanonical_literal(data_root):
_settle(data_root, cost=1.0, cost_final=True)
ua.record_subscription_session("sess-nc", drive_root=data_root, route="claudexor:claude", model="fable",
task_id="t", root_task_id="root", spend_usd=0.5)
path = data_root / ua.LEDGER_REL
lines = _ledger_lines(data_root)
# A RETAINED row (subscription kind never folds) whose monetary literal is
# NOT double-round-trippable: a foreign writer's long literal. Its value
# survives as a double, but the exact decimal cannot be re-serialized, so
# the pass must abort and leave the ledger untouched. (Folded rows are
# immune by construction: their decimals are carried as exact strings.)
doctored = lines[-1].replace('"cost_usd":0.5', '"cost_usd":0.50000000000000002775557561563')
assert doctored != lines[-1]
path.write_text("\n".join(lines[:-1] + [doctored]) + "\n", encoding="utf-8")
before_bytes = path.read_bytes()
assert _compact(data_root) is None
assert path.read_bytes() == before_bytes
def test_a_session_row_carrying_normalized_counters_survives_compaction(data_root):
"""The optional input split rides an idempotency-bearing row, so the pass
retains its content rather than folding it into a baseline group."""
counters = {"total_tokens": 270, "cache_read_tokens": 130, "cache_write_tokens": None}
_seed_mixed_ledger(data_root)
ua.record_subscription_session("sess-counters", drive_root=data_root, route="claudexor:codex",
model="fable", task_id="t7", root_task_id="root", spend_usd=0.25,
input_token_usage=counters)
before = next(row for row in _ledger_rows(data_root) if row.get("session_id_sha256")
and row.get("input_token_usage"))
assert _compact(data_root) is not None
after = next(row for row in _ledger_rows(data_root)
if row.get("attempt_id") == before["attempt_id"])
resequenced = ("seq", "pre_compaction_seq") # the pass renumbers, never rewrites
assert {key: value for key, value in after.items() if key not in resequenced} == \
{key: value for key, value in before.items() if key not in resequenced}
assert after["input_token_usage"] == counters