Skip to content

feat: surface runtime node faults on the node badge (I2C unACKed reads) - #94

Merged
xiduzo merged 2 commits into
mainfrom
feat/node-diagnostics
Jul 18, 2026
Merged

feat: surface runtime node faults on the node badge (I2C unACKed reads)#94
xiduzo merged 2 commits into
mainfrom
feat/node-diagnostics

Conversation

@xiduzo

@xiduzo xiduzo commented Jul 18, 2026

Copy link
Copy Markdown
Owner

Surfaces runtime node faults on the node's UI badge — first user: I2C reads that never ACK (a NACKing sensor used to fail silently with the monitor stuck at 0).

The bug

When an I2C device doesn't respond, the board streams an empty I2C_REPLY (F0 77 addr 00 regL regH F7) plus a STRING_DATA "I2C: Too few bytes received". The codec dropped the empty reply (if len < 8), so on_i2c_reply never fired and the fault was invisible — the outbound arm was correct, the device just wasn't answering.

Changes

  • Codec (firmata/mod.rs): parse zero-data replies (len >= 7) so the fault reaches the target node, routed by address+register.
  • New runtime→UI channel: Effects.node_diagnostics → compile-forced EffectsSink::report_diagnostic hook in both hosts (same ADR-0008/0009 pattern as cloud_requests). Nodes raise/clear via RuntimeContext::report_diagnostic / clear_diagnostic.
  • I2C node: on a short read, raises an error attributed by address and keeps its last good value (no garbage emit); clears on recovery — raise/clear only on a faulted transition, so a per-poll NACK doesn't spam.
  • UI: reuses the existing NodeContainer error/warning badge (no new UI). The I2C node shows "No response from 0x29: got 0 of 8 bytes. Check wiring (SDA/SCL/power), the address, and pull-ups.", outranking the advisory shared-address warning. Identical on desktop (Tauri node-diagnostic) and in-browser (reactor).

Verification

  • 590 core tests pass, incl. a new raise→dedup→clear test (short_i2c_reply_raises_then_clears_node_diagnostic).
  • Workspace clippy clean (-D warnings -W clippy::pedantic); wasm runtime compiles.
  • Frontend tsc clean; browser effects-sink conformance test updated (the exhaustive keyof Effects guard forced the new field to be handled) + passing.

Note: browser needs a wasm rebuild to pick up the new Effects field; desktop shows the badge on recompile.

🤖 Generated with Claude Code

xiduzo and others added 2 commits July 18, 2026 23:37
Adds a per-node runtime status channel (`Effects.node_diagnostics`), mirroring
the ADR-0008/0009 pattern: a new `EffectsSink::report_diagnostic` hook, compile-
forced in both hosts. Nodes raise/clear via `RuntimeContext::report_diagnostic`.

First use: I2C reads that never ACK. The board streams an empty I2C_REPLY plus
"I2C: Too few bytes received" when a device doesn't respond; the codec used to
drop the empty reply (`len < 8`), hiding the fault. Now it parses zero-data
replies (`len >= 7`) so the target node sees a short read, raises an error
diagnostic (attributed by address, keeps its last good value), and clears on
recovery — raised only on a transition so a per-poll NACK doesn't spam.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Consumes the new `node_diagnostics` effect on both hosts (browser reactor +
desktop `node-diagnostic` Tauri event) into a `useNodeDiagnosticsStore`, and
renders it through the existing `NodeContainer` error/warning badge — no new UI.
The I2C device node now shows a red error when its reads don't ACK ("No response
from 0x29: got 0 of 8 bytes. Check wiring…"), outranking the advisory shared-
address warning. The exhaustive `keyof Effects` guard in effects-sink forced the
new field to be handled; conformance test updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@xiduzo
xiduzo merged commit b14cd4f into main Jul 18, 2026
10 of 11 checks passed
@xiduzo
xiduzo deleted the feat/node-diagnostics branch July 18, 2026 22:02
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