ouroboros/tests/test_usage_compaction_fingerprint.py
Ouroboros 3837e384fd Let a refused or lost promote stop reading as pending, and pin what the mutation pass found loose
The pre-push panel's third reviewer ran 56 mutations and drove the real
supervisor handler. One defect and several unpinned branches:

- A refusal that writes no result of its own (rejected attachments, and the
  duplicate-id exit) left this promote's emitted stub on disk, so the
  reconciliation read answered 'admission pending, do not promote the same
  work a second time' for ever and the owner's work never started. Both exits
  now replace the stub of their OWN routing token; with no stub on disk they
  still write nothing. This corrects the claim in 8d57aabe3 that every refused
  promote already overwrote the stub.
- The pending sentence states only what the row proves (emitted, no receipt,
  the time now) and hands the decision back: an event can be lost with a
  restarted supervisor, so a standing ban was a lie waiting to happen.
- The admission gates use a strict form, is_own_admission_stub: no token is
  no claim, so a tokenless admission meets any stub as a row that owns the id,
  and _promote_duplicate_reason requires its token. This closes the disclosed
  tokenless residual structurally.
- An emitted stub is neither the project's last result nor a steerable
  predecessor: the lookup skips it like a child, and the door says what it is.
- prompts/SYSTEM.md, the same one sentence: a read-only subagent reads
  deliverables, every subagent its parent's and root's task files (the earlier
  'never a sibling's' is false on a single-drive geometry).
- The light-mode refusal says 'runtime control data', since it lists the task
  drive under the writable roots in the same text.

New pins, each re-run against the reviewer's surviving mutation and now red
without its line: the stub's token and status conditions, the token passed
by the promote handler, the retry window of the compaction floor, the
read-only gate of the lineage redirect, the project filter and omission count
of a room's live roots, the secret-name filter of a Deliverables listing, and
the host-issued authority source of a listed row.
2026-09-21 14:28:56 +03:00

528 lines
24 KiB
Python

"""What the compaction pass COMPARES before it commits, and how it records a refusal.
Companion to ``tests/test_usage_compaction.py`` (the pass itself) and
``tests/test_usage_compaction_archive.py`` (the archive reader). Two pins live
here:
1. the self-check compares the readers' NON-MONEY projection and leaves money
to the exact decimal totals — a float summary rounded at six places can
round a per-row sum and an exact per-group sum to different last digits,
which on the owner's live ledger aborted a CORRECT fold 240 times and held
the file at 77.8 MB against an 8 MB trigger. Counts, weights, tokens and
limits are still compared, and real money movement still aborts;
2. every policy abort leaves a typed, deduplicated
``usage_ledger_compaction_skipped`` event naming its reason, so the 20 MB
health tripwire's cause is recoverable afterwards instead of living only in
an INFO log line.
"""
from __future__ import annotations
import decimal
import json
from decimal import Decimal
from ouroboros import usage_accounting as ua
from ouroboros import usage_compaction as uc
from tests import fixtures_usage_compaction as _fixtures
from tests.fixtures_usage_compaction import _compact, _ledger_rows, _settle
data_root = _fixtures.data_root
data_root_any_tier = _fixtures.data_root_any_tier
# The two costs are VERBATIM from root 304db373 of the owner's live ledger:
# their exact sum 2.4675885 sits on the six-place rounding boundary, so the
# per-row float sum (2.4675884999999997) rounds down and the exact per-group
# sum rounds up. One of the 51 buckets that aborted the live fold.
_DRIFT_COSTS = (1.9542475, 0.513341)
_DRIFT_EXACT_SUM = Decimal("2.4675885")
def _final_rows(rows):
finals = {}
for row in rows:
finals[str(row.get("attempt_id"))] = row
return list(finals.values())
def _exact_money(rows):
"""Exact (settled cost, reservation bound) totals under a WIDE context."""
with decimal.localcontext() as context:
context.prec = 200
cost = bound = Decimal(0)
for row in _final_rows(rows):
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 _rewrite_one_group_row(monkeypatch, mutate):
"""Let ``mutate`` edit the single group row of the built candidate."""
real_build = uc._build_candidate
def build(records, decimal_records, raw, beat):
candidate, receipt = real_build(records, decimal_records, raw, beat)
lines = candidate.decode("utf-8").splitlines()
rebuilt = []
for line in lines:
row = json.loads(line)
if str(row.get("kind") or "") == "usage_baseline_group":
mutate(row)
line = uc._dumps_row(row)
rebuilt.append(line)
candidate = ("\n".join(rebuilt) + "\n").encode("utf-8")
receipt["compacted_size_bytes"] = len(candidate)
return candidate, receipt
monkeypatch.setattr(uc, "_build_candidate", build)
def test_one_ulp_float_rounding_drift_no_longer_aborts_the_fold(data_root):
"""The live defect, reduced: two settled rows whose float money rounds one
unit away from their exact sum now FOLD. Money stays decimal-identical and
the non-money view is untouched; the disclosed price is that the rounded
float a reader displays may move by 1e-6 (a ten-thousandth of a cent)."""
for cost in _DRIFT_COSTS:
_settle(data_root, cost=cost, cost_final=True)
before_rows = _ledger_rows(data_root)
before = ua._summary(_final_rows(before_rows))
receipt = _compact(data_root)
assert receipt is not None # pre-fix: _Abort("aggregation fingerprint mismatch")
after_rows = _ledger_rows(data_root)
groups = [row for row in after_rows if row.get("kind") == "usage_baseline_group"]
assert len(groups) == 1
assert Decimal(groups[0]["cost_usd"]) == _DRIFT_EXACT_SUM
after = ua._summary(_final_rows(after_rows))
# The drift this test exists for: real, one unit of the sixth place, and
# in the FLOAT view only.
assert before["settled_usd"] == 2.467588
assert after["settled_usd"] == 2.467589
assert _exact_money(after_rows) == _exact_money(before_rows)
assert _exact_money(after_rows)[0] == _DRIFT_EXACT_SUM
# Everything the fold could actually have lost is still identical.
assert uc._render_fingerprint(_final_rows(after_rows)) == uc._render_fingerprint(
_final_rows(before_rows)
)
assert (after["attempt_counts"], after["non_final_rows"], after["unknown_unmetered"]) == (
before["attempt_counts"], before["non_final_rows"], before["unknown_unmetered"]
)
def test_money_moved_in_the_candidate_still_aborts_on_exact_decimals(
data_root, monkeypatch,
):
"""Money is no longer in the fingerprint, so the EXACT decimal totals are
the guard that must catch a candidate whose dollars moved. A cent added to
the group row leaves every non-money value identical and must still abort,
byte-identically."""
for cost in _DRIFT_COSTS:
_settle(data_root, cost=cost, cost_final=True)
before_bytes = (data_root / ua.LEDGER_REL).read_bytes()
_rewrite_one_group_row(
monkeypatch,
lambda row: row.update({"cost_usd": format(Decimal(row["cost_usd"]) + Decimal("0.01"), "f")}),
)
assert _compact(data_root) is None
assert (data_root / ua.LEDGER_REL).read_bytes() == before_bytes
def test_lost_folded_weight_still_aborts_on_the_non_money_fingerprint(
data_root, monkeypatch,
):
"""The subtraction removed money and nothing else: a candidate that drops
one folded attempt from the group weight changes only counts, and the
fingerprint still refuses it."""
for cost in _DRIFT_COSTS:
_settle(data_root, cost=cost, cost_final=True)
before_bytes = (data_root / ua.LEDGER_REL).read_bytes()
_rewrite_one_group_row(
monkeypatch,
lambda row: row.update({"folded_attempt_count": int(row["folded_attempt_count"]) - 1}),
)
assert _compact(data_root) is None
assert (data_root / ua.LEDGER_REL).read_bytes() == before_bytes
def _skip_events(data_root):
path = data_root / "logs" / "events.jsonl"
if not path.exists():
return []
return [
row for row in (json.loads(line) for line in path.read_text(encoding="utf-8").splitlines() if line.strip())
if row.get("type") == "usage_ledger_compaction_skipped"
]
def test_a_policy_abort_records_its_typed_reason_once_per_cause(data_root, monkeypatch):
"""An abort leaves the ledger byte-identical and the pass runs again later,
so the reason has to outlive the pass: one typed event per process per
cause, which is what lets the ledger's size tripwire be diagnosed instead
of just observed."""
ua.reserve_attempt(ua.AttemptRequest( # in-flight only: nothing folds
model="openai/gpt-5.2", provider="openai", reservation_usd=1.0,
drive_root=data_root, task_id="open", root_task_id="root", source="test",
))
assert _compact(data_root) is None
assert _compact(data_root) is None # same cause again: still ONE row
assert [row["reason"] for row in _skip_events(data_root)] == ["nothing foldable"]
assert _skip_events(data_root)[0]["ts"]
for cost in _DRIFT_COSTS:
_settle(data_root, cost=cost, cost_final=True)
_rewrite_one_group_row(
monkeypatch,
lambda row: row.update({"cost_usd": format(Decimal(row["cost_usd"]) + Decimal("0.01"), "f")}),
)
assert _compact(data_root) is None
assert _compact(data_root) is None
# A NEW cause is never hidden behind the one already told.
assert [row["reason"] for row in _skip_events(data_root)] == [
"nothing foldable", "decimal money totals mismatch",
]
def test_fingerprint_carries_no_money_but_keeps_limits_and_counts(data_root):
"""The projection shape itself: no money key anywhere in it, while the
per-root ``root_limit_usd`` and every count axis stay compared."""
_settle(data_root, cost=1.25, cost_final=True, root_limit_usd=40.0)
_settle(data_root, cost=2.5, cost_final=True, task_id="t2", root_limit_usd=50.0)
fingerprint = uc._render_fingerprint(_final_rows(_ledger_rows(data_root)))
buckets = [fingerprint["summary"], fingerprint["breakdown"]]
buckets += [summary for summary, _limit in fingerprint["by_root"].values()]
for grouped, unattributed in fingerprint["axes"].values():
buckets += [*grouped.values(), unattributed]
for bucket in buckets:
assert not uc._FINGERPRINT_MONEY_KEYS & set(bucket)
assert "attempt_counts" in bucket
assert fingerprint["by_root"]["root"][1] == 40.0 # min known root limit
assert fingerprint["summary"]["attempt_counts"] == {"settled": 2}
assert fingerprint["breakdown"]["physical_calls"] == 2
# --- The growth guard after a COMMITTED pass ---------------------------------
#
# The trigger is a size threshold and the residue that cannot fold (group rows,
# retained idempotent and review-attributed rows) only ever grows, so a
# compacted ledger settles just under the threshold and stays there. While a
# success cleared the memo, that left nothing between the threshold and the
# pass: every reservation rewrote the whole monetary authority under the held
# lock and copied the entire live file into a new, never-collected archive
# segment to save a few kilobytes. Measured on the owner's live-ledger copy
# before this fix: 200 production chains on the folded 7.87 MB ledger ran 7
# full passes and grew the archive from 77.8 MB to 125.8 MB while the live
# file gained 114 KB — a ninth of the 1 MB the guard asks for.
def _trigger(data_root):
with ua._locked(data_root) as heartbeat:
return uc.maybe_compact_usage_ledger_locked(data_root, heartbeat=heartbeat)
def _folds(data_root):
return sum(1 for row in _ledger_rows(data_root) if row.get("kind") == "usage_baseline")
def _count_passes(monkeypatch):
"""Every entry into the pass itself, whatever its outcome."""
entered = []
original = uc.compact_usage_ledger_locked
def counting(root, **kwargs):
entered.append(1)
return original(root, **kwargs)
monkeypatch.setattr(uc, "compact_usage_ledger_locked", counting)
return entered
def _grow_past(data_root, monkeypatch, target, tag):
"""Append real chains until the live file is larger than ``target`` bytes.
The trigger is lifted while growing, so only the explicit ``_trigger``
calls in each test exercise the guard (``reserve_attempt`` runs it too).
"""
path = data_root / ua.LEDGER_REL
monkeypatch.setattr("ouroboros.config.USAGE_LEDGER_COMPACT_BYTES", 10 ** 12)
for index in range(400):
if path.stat().st_size > target:
break
_settle(data_root, cost=0.25, cost_final=True, task_id="%s-%d" % (tag, index))
else:
raise AssertionError("the fixture never grew past %d bytes" % target)
monkeypatch.setattr("ouroboros.config.USAGE_LEDGER_COMPACT_BYTES", 1)
def test_a_committed_fold_arms_the_same_growth_guard_as_an_abort(data_root, monkeypatch):
"""A pass that COMMITTED must throttle the next one exactly like a pass that
aborted: the memo means "the size this process last ran a pass on", not
"the size the last unprofitable pass saw"."""
_fixtures._seed_mixed_ledger(data_root)
monkeypatch.setattr("ouroboros.config.USAGE_LEDGER_COMPACT_BYTES", 1)
monkeypatch.setattr("ouroboros.config.USAGE_LEDGER_COMPACT_RETRY_GROWTH_BYTES", 1_000_000)
uc._COMPACT_ATTEMPTS.clear()
entered = []
original = uc.compact_usage_ledger_locked
monkeypatch.setattr(uc, "compact_usage_ledger_locked",
lambda root, **kwargs: (entered.append(1), original(root, **kwargs))[1])
assert _trigger(data_root) is True # first pass: above threshold, no memo yet
assert _folds(data_root) == 1
compacted_bytes = (data_root / ua.LEDGER_REL).read_bytes()
# Still far above the threshold and freshly foldable rows keep arriving, so
# the threshold alone would re-enter the pass on every settle.
for index in range(3):
_settle(data_root, cost=0.25, cost_final=True, task_id="after-%d" % index)
assert _trigger(data_root) is False
assert len(entered) == 1, "the pass ran again below the growth threshold"
# Declined, not aborted: the pass was never entered, so it recorded no
# typed skip reason and the fold it already committed still stands.
assert _skip_events(data_root) == []
assert (data_root / ua.LEDGER_REL).read_bytes().startswith(compacted_bytes)
# Real growth releases it, and the new pass folds again. Growth means bytes
# that arrived: the ledger's own floor is the size the last pass READ, so a
# narrowed retry window no longer stands in for an append.
monkeypatch.setattr("ouroboros.config.USAGE_LEDGER_COMPACT_RETRY_GROWTH_BYTES", 1)
_grow_past(data_root, monkeypatch, _ledger_rows(data_root)[0]["source_size_bytes"], "regrowth")
assert _trigger(data_root) is True
assert len(entered) == 2
assert _folds(data_root) == 1 # the header is replaced, never accumulated
assert int(_ledger_rows(data_root)[0]["compaction_epoch"]) == 2
def test_repeated_chains_cost_at_most_one_pass_per_growth_window(data_root, monkeypatch):
"""The refuters' driver, in miniature: chains that keep the ledger above the
trigger may not buy one full rewrite each. The bound is the guard's own
arithmetic — a pass, then one more per RETRY_GROWTH_BYTES of real growth —
not a magic number."""
_fixtures._seed_mixed_ledger(data_root)
growth = 4_096
monkeypatch.setattr("ouroboros.config.USAGE_LEDGER_COMPACT_BYTES", 1)
monkeypatch.setattr("ouroboros.config.USAGE_LEDGER_COMPACT_RETRY_GROWTH_BYTES", growth)
uc._COMPACT_ATTEMPTS.clear()
passes = []
original = uc.compact_usage_ledger_locked
def counting(root, **kwargs):
receipt = original(root, **kwargs)
passes.append(receipt is not None)
return receipt
monkeypatch.setattr(uc, "compact_usage_ledger_locked", counting)
path = data_root / ua.LEDGER_REL
added = 0
for index in range(30):
before = path.stat().st_size
_settle(data_root, cost=0.5, cost_final=True, task_id="chain-%d" % index)
added += max(0, path.stat().st_size - before) # a pass shrinks it: count appends only
assert _trigger(data_root) is not None
assert added > 0
assert len(passes) <= 1 + added // growth, (
"%d passes for %d appended bytes at a %d-byte guard" % (len(passes), added, growth)
)
assert all(passes), "a pass that aborts would prove nothing about the guard"
# --- The growth floor ACROSS processes ---------------------------------------
#
# The memo above is per-process and keyed by the file's identity, and a
# committed pass REPLACES that file: at the moment one process folds, every
# other process's memo stops matching and re-enters a full pass on the next
# reservation, and a process that has just started has no memo at all while the
# residue younger than the fold horizon holds the ledger above the trigger for
# good. Measured on the owner's live install: 100 passes archiving 2.37 GB in
# three days for 4.88 MB of live-file gain, 93 of 99 consecutive passes entered
# after less than 1 MB of growth. A guard that must hold across processes
# cannot live in one process's memory — and the pass already stamps what it
# needs into line 1: `source_size_bytes` is the size it read.
def test_a_peer_process_does_not_refold_what_another_just_folded(data_root, monkeypatch):
"""Two process memos over one ledger, with the swap between them. The peer's
memo names the inode the pass replaced, so the per-process guard is blind to
a pass it did not run; only a floor read off the ledger can throttle it."""
_fixtures._seed_mixed_ledger(data_root)
monkeypatch.setattr("ouroboros.config.USAGE_LEDGER_COMPACT_BYTES", 1)
monkeypatch.setattr("ouroboros.config.USAGE_LEDGER_COMPACT_RETRY_GROWTH_BYTES", 1_000_000)
uc._COMPACT_ATTEMPTS.clear()
key = str(data_root.resolve(strict=False))
before = (data_root / ua.LEDGER_REL).stat()
peer_memo = (before.st_ino, before.st_dev, before.st_size) # the peer looked first
entered = _count_passes(monkeypatch)
assert _trigger(data_root) is True # this process folds
assert (data_root / ua.LEDGER_REL).stat().st_ino != before.st_ino # the swap is real
assert _ledger_rows(data_root)[0]["source_size_bytes"] == before.st_size
uc._COMPACT_ATTEMPTS.clear()
uc._COMPACT_ATTEMPTS[key] = peer_memo # the peer, carrying the memo it built
for index in range(3):
_settle(data_root, cost=0.25, cost_final=True, task_id="peer-%d" % index)
assert _trigger(data_root) is False
assert len(entered) == 1, "the peer refolded a ledger another process had just folded"
assert _folds(data_root) == 1
assert _skip_events(data_root) == [] # declined before the pass, not aborted inside it
# The floor is what that pass READ plus the retry window, not the bare size: a
# ledger that regrew just past the old size is still inside the window (at the
# measured 0.02 % gain that is a few KB, the very cascade this guard removes).
_grow_past(data_root, monkeypatch, before.st_size + 1, "peer-growth")
assert _trigger(data_root) is False
assert len(entered) == 1
# Quiet on real growth: once the file passes what that pass READ plus the
# retry window, the peer folds on its own.
monkeypatch.setattr("ouroboros.config.USAGE_LEDGER_COMPACT_RETRY_GROWTH_BYTES", 1)
assert _trigger(data_root) is True
assert len(entered) == 2
assert int(_ledger_rows(data_root)[0]["compaction_epoch"]) == 2
def test_a_fresh_process_declines_a_ledger_that_was_just_folded(data_root, monkeypatch):
"""`prior is None` is the other half of the cascade: the residue that cannot
fold keeps a compacted ledger above the trigger for good, so every newly
started process used to buy one full rewrite on its first reservation."""
_fixtures._seed_mixed_ledger(data_root)
monkeypatch.setattr("ouroboros.config.USAGE_LEDGER_COMPACT_BYTES", 1)
monkeypatch.setattr("ouroboros.config.USAGE_LEDGER_COMPACT_RETRY_GROWTH_BYTES", 1_000_000)
uc._COMPACT_ATTEMPTS.clear()
source_size = (data_root / ua.LEDGER_REL).stat().st_size
entered = _count_passes(monkeypatch)
assert _trigger(data_root) is True
assert (data_root / ua.LEDGER_REL).stat().st_size < source_size # still above the trigger
uc._COMPACT_ATTEMPTS.clear() # a process that has just started, nothing remembered
assert _trigger(data_root) is False
assert _trigger(data_root) is False
assert len(entered) == 1
assert uc._COMPACT_ATTEMPTS == {}, "a declined pass is not a pass this process ran"
monkeypatch.setattr("ouroboros.config.USAGE_LEDGER_COMPACT_RETRY_GROWTH_BYTES", 1)
_grow_past(data_root, monkeypatch, source_size + 1, "fresh-growth")
uc._COMPACT_ATTEMPTS.clear()
assert _trigger(data_root) is True # real growth, and a fresh process still folds
assert len(entered) == 2
def test_an_unstamped_ledger_keeps_exactly_the_per_process_guard(data_root, monkeypatch):
"""No pass has ever run here, so line 1 carries no floor. The trigger must
behave exactly as it did: the pass runs, and an unprofitable one is still
throttled by the memo of the process that ran it."""
for task in ("a", "b"): # in-flight only: nothing foldable
reservation = ua.reserve_attempt(_fixtures._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()
entered = _count_passes(monkeypatch)
assert uc._live_baseline_header(data_root) is None # no stamp, no floor to read
assert _trigger(data_root) is False
assert len(entered) == 1 # the pass ran and aborted
assert [row["reason"] for row in _skip_events(data_root)] == ["nothing foldable"]
assert _trigger(data_root) is False
assert len(entered) == 1, "the abort is still throttled inside this process"
def test_a_stamp_without_a_usable_size_falls_back_to_the_memo(data_root, monkeypatch):
"""A header whose recorded source size is not a positive int answers no
floor at all. Falling back to the per-process guard keeps the ledger
compactable instead of freezing it behind a number nobody can read."""
_fixtures._seed_mixed_ledger(data_root)
monkeypatch.setattr("ouroboros.config.USAGE_LEDGER_COMPACT_BYTES", 1)
monkeypatch.setattr("ouroboros.config.USAGE_LEDGER_COMPACT_RETRY_GROWTH_BYTES", 1_000_000)
uc._COMPACT_ATTEMPTS.clear()
assert _trigger(data_root) is True
path = data_root / ua.LEDGER_REL
lines = path.read_text(encoding="utf-8").splitlines()
header = json.loads(lines[0])
header.pop("source_size_bytes")
lines[0] = uc._dumps_row(header)
path.write_text("\n".join(lines) + "\n", encoding="utf-8")
entered = _count_passes(monkeypatch)
uc._COMPACT_ATTEMPTS.clear() # a fresh process meets the unusable stamp
assert _trigger(data_root) is False # ran, then aborted on the broken header
assert len(entered) == 1
assert _trigger(data_root) is False
assert len(entered) == 1, "the memo is what throttles a ledger with no readable floor"
def test_an_unreadable_leading_row_surfaces_where_it_always_did(data_root, monkeypatch):
"""The floor reads line 1, so it meets a corrupt ledger before anything else
does. It may neither raise into the caller's reservation nor quietly decline
in place of the pass: the corruption is still reported by the normal read."""
_fixtures._seed_mixed_ledger(data_root)
path = data_root / ua.LEDGER_REL
lines = path.read_text(encoding="utf-8").splitlines()
lines[0] = "{not json at all"
path.write_text("\n".join(lines) + "\n", encoding="utf-8")
monkeypatch.setattr("ouroboros.config.USAGE_LEDGER_COMPACT_BYTES", 1)
uc._COMPACT_ATTEMPTS.clear()
entered = _count_passes(monkeypatch)
try:
uc._live_baseline_header(data_root)
raise AssertionError("an unreadable leading row must be typed corruption")
except uc.UsageLedgerCorrupt:
pass
assert _trigger(data_root) is False # contained, never raised into the reservation
assert len(entered) == 1, "the guard swallowed the corrupt ledger instead of the pass"
try:
ua.reserve_attempt(_fixtures._request(data_root, task_id="after-corrupt"))
raise AssertionError("the corrupt ledger must still fail the normal read")
except uc.UsageLedgerCorrupt:
pass
def test_a_restored_older_ledger_uses_the_floor_it_carries(data_root, monkeypatch):
"""A ledger restored from a backup brings its own, older stamp. The floor is
whatever THAT header records: the guard reads the file in front of it, never
a size belonging to a generation that is no longer there."""
_fixtures._seed_mixed_ledger(data_root)
monkeypatch.setattr("ouroboros.config.USAGE_LEDGER_COMPACT_BYTES", 1)
monkeypatch.setattr("ouroboros.config.USAGE_LEDGER_COMPACT_RETRY_GROWTH_BYTES", 1)
uc._COMPACT_ATTEMPTS.clear()
path = data_root / ua.LEDGER_REL
first_source = path.stat().st_size
assert _trigger(data_root) is True
restored_bytes = path.read_bytes() # the backup: epoch 1, its own stamp
_grow_past(data_root, monkeypatch, first_source * 3, "epoch2")
uc._COMPACT_ATTEMPTS.clear()
second_source = path.stat().st_size
assert _trigger(data_root) is True
assert int(_ledger_rows(data_root)[0]["compaction_epoch"]) == 2
path.write_bytes(restored_bytes) # the operator restores the older generation
uc._COMPACT_ATTEMPTS.clear()
entered = _count_passes(monkeypatch)
assert _ledger_rows(data_root)[0]["source_size_bytes"] == first_source
assert _trigger(data_root) is False # below the floor the restored file carries
assert len(entered) == 0
_grow_past(data_root, monkeypatch, first_source + 1, "restored-growth")
assert path.stat().st_size < second_source, "the fixture overshot the discriminator"
assert _trigger(data_root) is True # its own header released it, not the one it lost
assert len(entered) == 1
assert int(_ledger_rows(data_root)[0]["compaction_epoch"]) == 2