mirror of
https://github.com/ruvnet/RuView
synced 2026-08-11 20:41:44 +00:00
ba978041ae
# Conflicts: # docs/adr/README.md
2.8 KiB
2.8 KiB
ADR-295: Source provenance state machine — synthetic can never present as live
- Status: Accepted — initial implementation (this PR)
- Date: 2026-08-11
- Deciders: ruv
- Tags: provenance, honesty, ui, sensing-server, security
Context
An August 2026 external review found two provenance defects on the release path:
- The pose-fusion simulator starts in demo mode; on any page port other than
3000 the WebSocket target falls back to
localhost:8765, and if the connection fails the simulator keeps running while the status still reads "ready" — producing a convincing moving visualization with no live CSI (issue 1557). - The main sensing client labels the source live when the authenticated status endpoint returns an error for lack of authorization, until a real frame happens to correct it (issue 1526).
The common root cause: source state is a boolean (live vs not), so "unknown" collapses to "live". CLAUDE.md requires MEASURED/CLAIMED/SYNTHETIC labeling and forbids presenting synthetic output as real.
Decision
Define one canonical, mutually exclusive SourceState enum shared by the
sensing server and every UI/client that renders a source:
Synthetic— generated data (simulator/replay of synthetic fixtures).LiveVerified— frames from an authenticated, attested source.LiveUnverified— frames arriving but provenance not yet confirmed.Stale— last frame older than a configured freshness window.Disconnected— no source.
Rules enforced structurally:
Unknownis not a state. Any ambiguous condition resolves toLiveUnverified,Stale, orDisconnected— neverLiveVerified.- A status-endpoint error resolves to
Disconnected/LiveUnverified, never live-verified. - The simulator constructs
Syntheticand cannot transition to anyLive*state without a verified frame. Syntheticis watermarked in every view and every export.- Transitions are a pure function of (last-frame-age, auth-status, source-kind) so they are unit-testable without a clock or a socket.
Scope of this PR: the shared SourceState type + transition function + tests
in the sensing server, and wiring of the two identified surfaces (pose-fusion
simulator status, sensing client source label). Broader UI adoption follows.
Consequences
- Closes the "synthetic shown as live" and "unknown shown as live" classes.
- A small breaking change to any consumer currently reading a boolean source flag; mitigated by exposing a compatibility accessor during migration.
Validation
- Unit tests for every transition, especially: auth-error → not-live;
simulator → never live without a verified frame; freshness expiry →
Stale; watermark present on synthetic export. cargo test -p wifi-densepose-sensing-server.