Node sites: a lone receiver within 150 m of a site joins it - #359
Conversation
Two receivers on one roof are routinely two independently typed fixes tens of metres apart, so the exact-equality site rule (#286) left them as two sites publishing two samples of one address — a 56 m and a 16 m case are live in the fleet, each logged as a near miss every cycle. A node alone at its coordinates now joins the nearest existing anchor within NODE_FUZZ_SITE_KM (150 m, the audit distance) and is published FROM the anchor's position with the site's offset, so the markers coincide and the true baseline between the receivers is not on the wire. Exact-equality sites are formed first with frozen anchors, so no site already published at one point re-fuzzes; the join is a greedy pass in id order, so chains do not merge a street and connection order is irrelevant. Coverage polygons, arcs and trails move by the same total delta; location_uncertainty_km widens by the radius for a site with a joined member. Radius 0 restores exact equality alone; the older NODE_FUZZ_SITE_AUDIT_KM name is still read. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Claude finished @jehanazad's task in 4m 48s —— View job Review: PR #359 — Node sites: proximity gate
Note: I could not execute SummaryThis is a careful, well-documented change. I traced the core logic in
Minor observations (not blocking)
Verification claimsThe PR's own numbers (91 passed, pre-commit clean) look consistent with the diff — the new No correctness issues found. This is ready from a code-review standpoint; the live-verification steps the author already flagged (checking |
Why
services/node_sites.py(#286) publishes co-located receivers at one point, but only when their configured coordinates are equal at 6 decimals. The fleet has two real cases the rule cannot see: a fourth receiver configured 56 m from three at one address, and a receiver 16 m from a pair at another. The co-location audit logs both every cycle. Each publishes its own donut draw, so the map shows a second marker ~1 km away (two independent draws, not the true 56 m) and an attacker holds two samples of one address: 2.36 km² of consistent region becomes 0.69 km².Aligning the configs (the log's advice) edits the physics input and would solve the node against a wrong position. This fixes it at the publication edge instead.
What
Two rules, in order, in
node_sites._cluster():NODE_FUZZ_SITE_KM(150 m, the distance the audit already used), else becomes one. Greedy over sorted ids, not transitive: every member is within the radius of its own anchor, four houses 100 m apart in a line become two sites, and connection order is irrelevant.A joined node is published from the anchor's configured position (
site_position), not from its own with a shared offset — sharing only the offset would leave two markers 56 m apart with the true baseline between the receivers on the wire.translate_polygonandpublic_point_deltacarry the join shift as well as the offset (_public_delta_deg), so arcs, trails and coverage stay rigid around the marker.location_uncertainty_km(node_id)widens by the radius for every member of a site with a joined member, so the single published point carries one honest radius.colocation_report()gainsproximity_joins(the nodes this moved, with their true gap) andnear_missesbecomes the residual band up to 2× the radius. Radius 0 restores exact equality alone.NODE_FUZZ_SITE_AUDIT_KMis still read as the older name.What moves on deploy
Only nodes the audit was already reporting: on test, exactly the 56 m and 16 m cases. Lone nodes and exact-equality sites hash and publish exactly as before (pinned by tests). Before deploying to staging or prod, grep that environment's
node_sites:WARN lines — the near-miss list is exactly the set of nodes that will move.NODE_FUZZ_SALT,_ORIGINAL_FRAMEand the lone-node identity are untouched.Design note:
docs/design-notes/2026-09-11-node-site-proximity-gate.md. Pairs with #358 (site markers): that PR groups markers by exact published coordinate, which now holds for joined nodes too.Verification
tests/test_node_sites.py+tests/test_public_location.py: 91 passed (new: 56 m join, 16 m join with a lower-id newcomer not re-anchoring the pair, published point coincides, raw-vs-rounded anchor, artefact rigidity, widened uncertainty, no chaining, order independence, nearest-site choice, beyond-radius stays lone, anchor disconnect does not move the joiner, radius 0, both env names).pre-commit runon changed files: passed./api/radar/nodesshould show retf1144fcd at the Greenville trio's point and ret72b1909e at the Atlanta pair's;location_uncertainty_km1.15 at both sites; onenode_sites:INFO line per join and no WARN.🤖 Generated with Claude Code