docs(mesh): MeshTransport config, Phase 2 as shipped, design-doc correction

Rig: all six docker-dev nodes carry MeshTransport__Mode plus both central
contact points. Mode is "${OTOPCUA_MESH_MODE:-Dps}", so the rig still comes up
on the transport it has always used and the live gate can flip the whole fleet
at `docker compose up` without a compose edit or a rebuild.

Docs: a MeshTransport section in Configuration.md (including why contact points
are addresses only, why the contact set does not scope delivery, and why
buffer-size=0 is a behaviour decision rather than a tuning knob), a "Command
transport" section in Redundancy.md contrasting the two modes, and a summary
beside the Redundancy notes in CLAUDE.md.

Corrects the design doc's claim that ScadaBridge replies with a typed failure to
every unhandled message. Neither of their comm actors has a ReceiveAny or an
Unhandled override; the idiom is per message type and fires on a missing
registration, and an unknown type dead-letters there as it does anywhere else.

Records the exit-gate deviation in the program plan: "an Ask timing out cleanly
against a stopped node" cannot be run, because no Ask crosses this boundary. The
honest equivalents are a stopped node failing the deploy at the apply deadline
and an unreachable contact dropping the command with a Warning.

Claude-Session: https://claude.ai/code/session_01GASWkNEi68FSCtvr6rLoEW
This commit is contained in:
Joseph Doherty
2026-07-22 17:46:46 -04:00
parent 4eca87f04d
commit e4cb9a08e0
6 changed files with 202 additions and 5 deletions
@@ -68,8 +68,14 @@ Other load-bearing details:
caller's Ask times out. *"It keeps the central coordinator stateless with respect to site
availability."* A `ConnectionStateChanged` mechanism was built for this and **deleted as dead
code**.
- **Every unhandled message replies with a typed failure rather than dropping**, so a central Ask
can never stall on a missing handler.
- ~~**Every unhandled message replies with a typed failure rather than dropping**, so a central Ask
can never stall on a missing handler.~~ **CORRECTED 2026-07-22 (Phase 2 recon).** This overstates
what ScadaBridge does. Neither of their comm actors has a `ReceiveAny` or an `Unhandled` override:
the typed-failure idiom is **per message type**, and it fires only when a *registration* is missing
(no ClusterClient for the site yet, no handler registered for that type). A genuinely unknown
message type still dead-letters exactly as it would anywhere else. Do not build a catch-all failure
reply believing you are copying them. OtOpcUa's Phase 2 comm actors list each type explicitly and
deliberately let an unknown type dead-letter loudly rather than republish it blind.
- **Sender preservation is the whole Ask idiom**: `_client.Tell(new ClusterClient.Send(path, msg),
Sender)` routes the reply straight back to the waiting Ask, bypassing the comm actor.