Files
MeshTerm/tests
Jean-Pierre MartineauandClaude Opus 5 99cd616ae5 The purge keeps the contacts worth a slot, and archives the rest
A device's contact table is finite, and a busy mesh fills it with whatever
adverts arrived — mostly nodes heard once, in passing, four hops out — until
there is no room left to discover anyone new. The age ladder could only ask
how long ago; it had nothing to say about which of two equally quiet contacts
was the one you would miss.

So contacts are now rated. Six weighted terms, each normalised and summed:
direct correspondence heaviest at 30, then how lately heard (25), how often
(15), channel posting (12), hops (10) and distance (8). Additive rather than
multiplicative, because a product lets one zero annihilate a contact that is
strong everywhere else — a node advertising no position would have scored
nothing however much you talk to it.

The rule the whole thing rests on is that an unknown scores *neutral*. Half a
real mesh advertises no location, plenty of nodes are never overheard as a
relayed frame so carry no hop count, and a channel poster whose name two
contacts share can be attributed to neither — the wire gives a channel frame
no sender key at all. A term that cannot be computed takes the population
median of the contacts that could, so the decision falls to the axes that are
known. Scoring those as zero would have swept by how much metadata a node
happens to broadcast. It is also why the two least reliable terms carry the
two smallest weights, and why distance calibrates against this mesh's own
median spread rather than a constant: 50 km is far downtown and unremarkable
in a valley.

A newcomer gets 25 points decaying to nothing over a fortnight, because every
other term rewards accumulated evidence and a node first heard this morning is
otherwise arithmetically identical to one quiet for a year. Four things are
never candidates at all: a watched node, anyone you have sent a message to, a
repeater whose admin login is stored, and a contact MeshTerm has never heard —
that last one predates this history, and sweeping it would be purging by how
long the app has been running. They still rank, since a percentile that
dropped them would not be a rank against your contacts; they just consume no
kept slot, so starring a node never deepens the next sweep.

Only the percentile is ever displayed. A raw 47.3 means nothing without the
distribution it came from and would need a legend the moment the weights were
retuned.

The victims are archived, not deleted: off the device, which is the scarce
resource, and kept here with a stamp. Everything else was already independent
of the contact row — reception history, map position, transcripts — and the
store keeps the full key, so a restore is one write, which the chat screen
already performs on demand when a send is rejected for an unknown recipient.
merge_contacts learns to withhold them, or the union would put them straight
back and the sweep would look like it did nothing.

The old age ladder survives as the second section of the one picker, because
"everything I haven't heard in a year" is a plain predictable operation a
ranking cannot express. Both routes converge on the same preview — weakest
first, with each row's percentile and the two or three facts that put it
there — so an age rung reaching into contacts that rank perfectly well says so
before the typed confirm rather than after.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 00:32:55 -04:00
..
2026-08-30 02:59:01 -04:00