Purge stale contacts: a one-day rung at the top of the ladder

The tightest rung was a week, which is no help to a device whose contact
table filled up in one busy afternoon. Nothing else moves: the rung sweeps
only contacts actually heard and since gone quiet, the never-heard bucket
still stands apart, and the typed `delete` gate still names the span.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jean-Pierre MartineauandClaude Opus 5 committed 2026-08-10 02:31:33 -04:00
1 parent 2a3b78e015
commit 4c7fa8f812
2 files changed
+8 -2

No files matched your search

+2 -1
View File
@@ -11,7 +11,7 @@ and backing out of that page returns to the list right where it stood. The one-s
:func:`~meshterm.ui.widgets.contacts_table`; only the menu gets the live list.
Below the contacts sits one maintenance action — **Purge stale contacts** — that sweeps out
the contacts a device has accumulated but no longer hears. It offers an age ladder (a week up
the contacts a device has accumulated but no longer hears. It offers an age ladder (a day up
to a year, plus a *never heard* bucket), each rung showing a live count of how many contacts
it would remove, so the impact is visible before anything is chosen; the pick is then gated
behind a typed ``delete`` confirm. A purge deletes the contacts from the device's table and
@@ -55,6 +55,7 @@ _DAY = 86400
#: bucket, so an age rung never quietly deletes a freshly-added contact that hasn't had time
#: to advert yet.
PURGE_RUNGS: tuple[tuple[str, int], ...] = (
("Older than 1 day", _DAY),
("Older than 1 week", 7 * _DAY),
("Older than 1 month", 30 * _DAY),
("Older than 3 months", 90 * _DAY),
+6 -1
View File
@@ -327,11 +327,16 @@ def test_purge_buckets_split_stale_from_never_heard() -> None:
now = utcnow()
fresh = Contact(name="Fresh", public_key="aa" * 32, last_seen=now - timedelta(hours=1))
quiet = Contact(name="Quiet", public_key="ee" * 32, last_seen=now - timedelta(days=3))
old = Contact(name="Old", public_key="bb" * 32, last_seen=now - timedelta(days=45))
ancient = Contact(name="Ancient", public_key="cc" * 32, last_seen=now - timedelta(days=400))
unheard = Contact(name="Unheard", public_key="dd" * 32) # no last_seen
contacts = [fresh, old, ancient, unheard]
contacts = [fresh, quiet, old, ancient, unheard]
# A day catches everything heard but not heard *today* — the tightest rung the ladder
# offers (JP, 2026-08-10), so a contact three days quiet is swept where the week rung
# would still have kept it.
assert {c.name for c in stale_past(contacts, _DAY)} == {"Quiet", "Old", "Ancient"}
# A week catches the two aged ones but not the fresh — and never the never-heard one.
week = {c.name for c in stale_past(contacts, 7 * _DAY)}
assert week == {"Old", "Ancient"}