mirror of
https://github.com/Kpa-clawbot/meshcore-analyzer.git
synced 2026-05-18 21:25:08 +00:00
2e28aa3e04
RED235b65b4(CI will surface URL after PR open) — `test(#1229): tier-1 must prefer multi-observer edges`. Green:841fc5de. ## Summary Implements **Option C** from issue #1229: edge source-diversity confidence weighting. Each neighbor-graph edge already tracks the set of distinct observers that contributed to it (`NeighborEdge.Observers`). This PR is the first to consume that signal in the disambiguator. Tier-1 score in `pm.resolveWithContext` becomes `Score(now) × Confidence()` where: ``` Confidence() = min(1.0, max(1, |Observers|) / 3.0) ``` - 1 observer → 1/3 weight (single-source, suspect) - 2 observers → 2/3 weight - ≥3 observers → 1.0 (saturated, full historical weight) A 6-observer edge (30 obs) now beats a 1-observer edge (25 obs) by 3.6× (vs. 1.2× before) — enough to clear `affinityConfidenceRatio` and skip the tier-2 geo fallback that was misresolving in cross-region cases. Stacks with the geo-rejection filter merged in #1228/#1230 to give two independent defenses against cross-region prefix-collision pollution. ## Why C over A/B - **A (per-observer graphs):** N×memory cost, biggest refactor surface. - **B (per-region/IATA segmented):** requires region attribution on every packet + per-region cache plumbing; deferred follow-up. - **C:** smallest diff (~30 lines), no schema migration, leverages an existing field, composes additively with #1228. A and B remain valid follow-ups if C proves insufficient. ## Backward compatibility (persistence) `neighbor_edges` schema is **unchanged**. `Observers` is rebuilt by `BuildFromStoreWithOptions` from live observations on every graph refresh (5-min TTL). Persisted rows carry an empty set only during the post-restart warm-up; `Confidence()` defaults n→1 when `|Observers|==0`, so legacy rows resolve as single-observer (degraded but non-zero) confidence rather than disappearing. Defensive. ## Tests - `cmd/server/hop_disambig_confidence_test.go:48` — RED-then-GREEN E2E: two `8a` candidates from the same anchor, candX placed geo-near with 1 observer × 25 obs, candY placed geo-far with 6 observers × 5 obs. Without confidence weighting tier-1 falls through (1.2× ratio) and tier-2 picks the wrong (geo-near) candX. With confidence weighting tier-1 fires and picks candY. Asserts `method == "neighbor_affinity"` to pin the resolver path. - `TestNeighborEdge_ObserverSetIsDistinct` — guards the source-diversity counter against double-counting same-observer contributions and pins the `Confidence()` formula at both endpoints (single → fractional, ≥3 → 1.0). All existing tier-1 tests (`hop_disambig_tier1_test.go`) continue to pass — they seed with a single observer, so their weights drop from 1.0 to 1/3 uniformly across candidates, preserving the ratio guard outcome. Fixes #1229 --------- Co-authored-by: bot <bot@corescope.local>