Coordinator rulings and Important findings from review r0:
1. FC23 is opt-in (new UseFC23ForMultiRegisterWrites, default false); a config
without it - including the persisted "disableFC23": false every form-saved
config carries - keeps the FC16 wire. DisableFC23 stays the hard kill-switch.
2. An explicit maxReadGap 0 means coalescing off again (its pre-lane meaning);
the gap-8 default applies only to an absent key.
3. Coils/DiscreteInputs tags persisted with a register dataType (the old
editor saved Int16) are coerced to Bool, never refused, with no deploy
warning (a warning can be promoted to a blocking error). The editor loads
them as Bool. Register region + Bool is still refused.
4. One comms-fault slot for reads and writes: a good read clears a write's
transport fault and vice versa.
Minors: Dispose writes its own Unknown health (m1); persisted region/address
are authoritative over addressString so a Family change cannot remap a live
tag (m2); a malformed-but-delivered reply no longer fails the unit fast (m3);
a fresh socket published after a concurrent dispose is torn down (m4); the
editor resets address text per tag (m5); master merged (m6); the FC23 latch
log says re-init restores it (m7). Back-compat tests use the old persisted
JSON shapes and fail without the fixes.
Refs #741
Claude-Session: https://claude.ai/code/session_01Ub9hGmYLQvn9XETzw7CMsT
After a comms fault the device value is unknown, so re-asserting the same
setpoint must reach the wire - the write-side twin of the value-only
recovery dedup trap.
Refs #741
Claude-Session: https://claude.ai/code/session_01Ub9hGmYLQvn9XETzw7CMsT
H4: exception 01/02/03 (and undecodable data) are device refusals - per-tag
Bad with a once-per-tag Warning, no Degraded, and they advance
LastSuccessfulRead (the device answered), so an all-refused driver no longer
trips the 90 s watchdog. 04/05/06 still degrade (answered); 0A/0B degrade
without advancing. Health is one worst-wins write per batch instead of
last-writer-wins per tag. A comms failure on one unit fails the rest of
that unit's batch fast; an unreachable endpoint fails the whole batch fast.
H7: write transport failures map to BadCommunicationError, value overflow and
array length mismatch to BadOutOfRange, conversion failures to
BadTypeMismatch; writes report write-side health (a good write or a refusal
clears a write-side Degraded; a good read does not).
Lifecycle: every health write and the transport/table publish carry a
generation; a superseded init releases its transport and publishes nothing,
a read straddling a re-init cannot degrade the new transport, and the
shutdown's final write cannot clobber a newer init. The tag table is
build-and-swapped (re-init no longer merges into the old table). Teardown
joins the probe/re-probe loops (bounded); a probe answer advances
LastSuccessfulRead and an exception PDU from the device counts as Running.
Transports latch dispose atomically and never reconnect after it.
Refs #741
Claude-Session: https://claude.ai/code/session_01Ub9hGmYLQvn9XETzw7CMsT