From a88e85c235b63a5b416b91721fb991c24774d173 Mon Sep 17 00:00:00 2001 From: fy59 Date: Tue, 22 Sep 2026 13:15:49 +0200 Subject: [PATCH] fix(sync): ignore terminal legacy generation capacity --- tests/test_generation_archival.py | 48 +++++++++++++++++++++++++++++++ tools/sync_generation_exchange.py | 17 ++++++++++- 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/tests/test_generation_archival.py b/tests/test_generation_archival.py index 1d5a138..64ffd0e 100644 --- a/tests/test_generation_archival.py +++ b/tests/test_generation_archival.py @@ -95,6 +95,54 @@ class GenerationArchiveTest(unittest.TestCase): with sqlite3.connect(self.database) as database: self.assertEqual(0, database.execute("SELECT COUNT(*) FROM sync_generation_archives").fetchone()[0]) + def test_historical_acknowledged_rows_without_ack_ledger_do_not_exhaust_admission(self): + with sqlite3.connect(self.database) as database: + producer = generation.peer_identity(database, "desktop") + database.commit() + parent = None + for index in range(generation.MAX_RETAINED_PER_PEER): + suffix = f"{index + 1:012d}" + generation_id = f"gen_10000000-0000-4000-8000-{suffix}" + run_id = f"sy_20000000-0000-4000-8000-{suffix}" + database.execute( + "INSERT INTO sync_generations VALUES(?,?,?,?,?,?,?,?,?,?,?,?)", + ( + generation_id, + run_id, + producer, + PEER, + "desktop", + f"2026-09-16T00:00:{index:02d}+00:00", + parent, + "0" * 64, + "acknowledged", + "{}", + str(self.owned / "staging" / generation_id), + f"2026-09-16T00:01:{index:02d}+00:00", + ), + ) + parent = generation_id + database.commit() + + stage, manifest, _ = generation.capture_desktop(self.database, self.owned, PEER) + self.assertTrue(stage.is_dir()) + self.assertEqual(parent, manifest["parent_generation_id"]) + with sqlite3.connect(self.database) as database: + self.assertEqual( + 0, + database.execute( + "SELECT COUNT(*) FROM sync_acknowledgements " + "WHERE generation_id LIKE 'gen_10000000-%'" + ).fetchone()[0], + ) + self.assertEqual( + 0, + database.execute( + "SELECT COUNT(*) FROM sync_generation_archives " + "WHERE generation_id LIKE 'gen_10000000-%'" + ).fetchone()[0], + ) + def test_missing_then_late_ack_is_protected_until_correlated(self): values = self.create_acknowledged() stage, manifest, digest = generation.capture_desktop(self.database, self.owned, PEER) diff --git a/tools/sync_generation_exchange.py b/tools/sync_generation_exchange.py index cd54dee..5e2e89c 100644 --- a/tools/sync_generation_exchange.py +++ b/tools/sync_generation_exchange.py @@ -364,7 +364,22 @@ def capture_desktop(database: Path, owned_root: Path, consumer: str, archive_acknowledged(database, owned_root, consumer) with closing(connect_database(database)) as admission: require_schema(admission) - retained = admission.execute("SELECT COUNT(*) FROM sync_generations g WHERE consumer_peer_id=? AND status IN('captured','published','waiting_acknowledgement','acknowledged') AND NOT EXISTS(SELECT 1 FROM sync_generation_archives a WHERE a.generation_id=g.generation_id)", (consumer,)).fetchone()[0] + # WHY: early private generation builds can contain terminal + # acknowledged rows from before durable ACK-ledger insertion was + # enforced. They cannot be archived without fabricating missing ACK + # evidence, but they are terminal and must not permanently consume the + # active admission window. Current production acknowledgements always + # have a durable sync_acknowledgements row and remain counted until + # archive_acknowledged() safely moves them out of the active window. + retained = admission.execute( + "SELECT COUNT(*) FROM sync_generations g WHERE consumer_peer_id=? " + "AND (g.status IN('captured','published','waiting_acknowledgement') OR " + "(g.status='acknowledged' AND EXISTS(SELECT 1 FROM sync_acknowledgements k " + "WHERE k.generation_id=g.generation_id))) " + "AND NOT EXISTS(SELECT 1 FROM sync_generation_archives a " + "WHERE a.generation_id=g.generation_id)", + (consumer,), + ).fetchone()[0] if retained >= MAX_RETAINED_PER_PEER: raise GenerationError("generation retention capacity exhausted") stage = owned_root / "staging" / generation