Files
meshcore-analyzer/internal
SaarMesh-BotandClaude e7e51fa546 feat: ingest observer /neighbors report as confirmed scope evidence (#1865)
The ESP32 observer firmware now emits a periodic /neighbors report carrying
the observer's own configured region scopes (`self`) plus, for each zero-hop
neighbor, the scopes fetched via an OTA scope query. This records that
CONFIRMED configuration into a new nodes.configured_scope column, kept
strictly separate from the existing inferred `default_scope` (observed advert
transport scope) and transported_scopes (transmissions.scope_name).

Provenance is modelled explicitly rather than overloading default_scope:
default_scope is overwritten on every advert observation, so writing neighbor
scopes there would let the next inferred observation clobber a confirmed
value. A dedicated configured_scope (+ configured_scope_at) column preserves
the distinction and structurally satisfies the report contract.

Report semantics honored:
- Only neighbors with status=="responded" update configured_scope. A timeout
  is NOT evidence the scopes were cleared, so it never writes.
- Absence of a neighbor is never a signal: the report is size-capped and
  truncates by ordering, so missing != gone — no deletes ever happen.
- A responded neighbor with empty scopes is a valid "no scopes configured"
  statement and IS stored (the handler gates on status, not emptiness).
- self scopes are keyed by origin_id (the observer node pubkey) and need no
  OTA query. Report pubkeys are uppercase; nodes.public_key is lowercase hex,
  so keys are lowercased before the UPDATE. Unknown neighbors are a no-op
  until a later advert creates the node.
- Out-of-order reports can't clobber newer data (last-write-wins on
  configured_scope_at).

Changes:
- dbschema: additive ensureConfiguredScopeColumns migration on nodes +
  inactive_nodes (marker nodes_configured_scope_v1), asserted via mustCol.
- ingestor: handleNeighborsReport dispatch on topic
  meshcore/<region>/<observer_id>/neighbors (analogous to /status);
  Store.UpdateNodeConfiguredScope writer.
- server: PRAGMA-detect configured_scope (hasConfiguredScope) and expose it +
  configured_scope_at on the node read path.
- UI: node-detail (nodes.js + live.js) shows a "Configured scope" row marked
  confirmed, with last-confirmed timestamp, distinct from the observed scope.
- tests: handleNeighborsReport (responded writes, timeout/absence never
  clears, empty-responded stored, unknown no-op) + last-write-wins.

Topic format meshcore/<region>/<observer_id>/neighbors is assumed by analogy
to the /status topic; noted for reviewer confirmation against the firmware.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-26 12:09:54 +02:00
..