Symptom

An INPUT Server.Pin whose hardware GPIO line was HIGH rendered in the UI as “LOW” (dim dot) even though the server’s persisted meta.state was ON and a direct REST call to /node/{id} returned state == "ON". A downstream Executor.LogicGate (NOT) was producing the correct relay output, confirming that server-side state was right — only the client display was stale.

Root cause

PinReconciliationTask corrects mismatches via ServerNodeManager.updatePinState (direct DB write) by design, to bypass the EventTracker 1-second debounce so corrections cannot be dropped. But that path only emits a STATE_CHANGE event, which carries Node.state (NodeState) — not meta.state (DigitalState). The client’s EventClient PIN_CHANGED handler is the only path that updates PinMetaData.state; without a PIN_CHANGED event firing, /events SSE subscribers never learned about the corrected digital level.

OUTPUT pins are unaffected because ServerPinProcessor.process cascades into PiManager.setHigh/setLow, which posts PIN_CHANGED. INPUT pins fall through the Mode.IN -> { } empty branch, so reconciliation was the only path that touched their persisted state — and it skipped the event.

Fix

After nodeManager.updatePinState(node, hardwareState) in PinReconciliationTask.reconcile, also post Event(node.id, EventType.PIN_CHANGED, PinEventPayload(hardwareState)) on EventFlowContainer. EventMonitor’s server-side handler is idempotent for this case (it saves the same state again) and the redundant executeSources is harmless because the LogicGate re-entrancy guard already deduplicates.

Prevention

A regression test (PinReconciliationTaskTest) now collects emissions on EventFlowContainer and asserts that a PIN_CHANGED event is posted whenever reconciliation actually corrects state. The class of bug — “server mutates meta but only fires STATE_CHANGE” — is wider than this single call site; any future code path that bypasses setHigh/setLow for a Pin should explicitly post PIN_CHANGED, and the contract is now documented inline at the reconciliation call site.