Skip to content

fix(rufield): simulated source now feeds /api/field, honestly - #1720

Open
fallen-pc wants to merge 1 commit into
ruvnet:mainfrom
fallen-pc:fix/rufield-simulated-source-honesty
Open

fix(rufield): simulated source now feeds /api/field, honestly#1720
fallen-pc wants to merge 1 commit into
ruvnet:mainfrom
fallen-pc:fix/rufield-simulated-source-honesty

Conversation

@fallen-pc

Copy link
Copy Markdown

Summary

Two joined bugs in the RuField P3 live surface (/api/field, /ws/field).

1. The wiring gap. --source simulated — the project's own documented "no hardware" quick start — never populated node_states, the map engine_bridge.observe_cycle reads to produce a trust class. emit_rufield_event is a deliberate no-op without a class ("no class ⇒ nothing honest to stamp"), so /api/field//ws/field silently returned zero events forever under Docker demo mode. Not documented anywhere as a limitation — traced this while wiring up rufield-viewer --source live against a local server and getting events: [] no matter how long I waited.

2. The honesty gap the wiring fix would have introduced. Once wired, wifi-densepose-rufield's bridge (snapshot_to_field_event) unconditionally hardcoded synthetic: false and vendor: "esp32" on every emitted event — an assumption that only held because real ESP32 UDP ingestion was previously the sole caller. Simply wiring the simulated path through would have shipped simulated data signed and labeled indistinguishable from real hardware — exactly the dishonesty this crate's own docs (ADR-262 §0/§6) exist to prevent.

Change

  • simulated_data_task now mirrors its per-tick frame into node_states under a synthetic node id (SIMULATED_NODE_ID), and drives the same governed cycle (engine_bridge.observe_cycle) + emit_rufield_event call the real ESP32 UDP path already uses.
  • SensingSnapshot grows a synthetic: bool field, threaded through build_snapshotemit_rufield_eventsnapshot_to_field_event, so a simulated cycle is labeled synthetic: true / vendor: "simulated" / simulated_node_N end-to-end, and a real cycle stays synthetic: false / vendor: "esp32" exactly as before.
  • New regression test gate_synthetic_flag_passes_through_honestly in wifi-densepose-rufield proves the flag isn't hardcoded either direction.

Test plan

  • cargo test -p wifi-densepose-rufield -p wifi-densepose-sensing-server --no-default-features — all passing (22 + 8 rufield-related tests, no regressions)
  • npx @ruvnet/ruview claim-check on this PR body — PASS
  • Live-verified: ran the sensing-server locally with --source simulated and a real WDP_RUFIELD_SIGNING_SEED. Before the fix: GET /api/fieldevents: [] forever, /api/v1/statuseffective_class: null forever. After: real signed FieldEvents flowing, e.g. event_id: "ruview-simulated_node_1-...", provenance.synthetic: true, sensor.vendor: "simulated", valid ed25519 signature, dev_signing_key: false.

Two joined bugs, both in the RuField P3 live surface:

1. `--source simulated` (the project's own "no hardware" quick start)
   never populated node_states, the map engine_bridge.observe_cycle
   reads to produce a trust class. Without a class, emit_rufield_event
   is a no-op by design ("no class => nothing honest to stamp"), so
   /api/field and /ws/field silently returned zero events forever
   under Docker demo mode - not documented anywhere as a limitation.

2. Once wired, the fix would have made things worse: the
   wifi-densepose-rufield bridge hardcoded synthetic: false and
   vendor: "esp32" on every event, an assumption that held only
   because real ESP32 UDP was previously the sole caller. Simulated
   cycles would have been signed and shipped indistinguishable from
   real hardware - the exact dishonesty this crate's own docs (ADR-262
   Sec0/Sec6) exist to prevent.

Fixes both: simulated_data_task now mirrors its frame into node_states
under a synthetic node id and drives the same governed cycle +
emit_rufield_event call as the real ESP32 path; SensingSnapshot grows
a `synthetic` field threaded through build_snapshot ->
emit_rufield_event -> snapshot_to_field_event, so a simulated cycle is
labelled synthetic: true / vendor: "simulated" end to end.

Live-verified: /api/field now returns real signed FieldEvents under
`--source simulated`, correctly self-labelled.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant