Files
lmxopcua/docs/plans/2026-07-22-mesh-phase2-clusterclient-transport.md.tasks.json
T
Joseph Doherty 1ec831883c docs(mesh): Phase 2 plan — comm actors + ClusterClient transport
Phase 2 of the per-cluster mesh program: move the three central->node command
channels (deployments, driver-control, alarm-commands) and the deployment-acks
reply channel off DistributedPubSub onto Akka ClusterClient, behind a config
flag.

Recon of both repos turned up three corrections to the design doc's account of
the sister project, all folded into the plan:

- Design doc S2 claims "every unhandled message replies with a typed failure".
  ScadaBridge has no ReceiveAny/Unhandled override in either comm actor; the
  typed-failure idiom fires only for a MISSING REGISTRATION, per message type.
- "No central buffering" was never a setting they wrote. They have zero
  akka.cluster.client HOCON and run the defaults (buffer-size 1000,
  reconnect-timeout off). We choose buffer-size = 0 deliberately.
- Publish -> Send is the wrong substitution. Today's deploy notify is a
  broadcast every DriverHostActor receives (no ClusterId filter exists on the
  node side), so it must become SendToAll. Send would deploy to exactly one
  node of the fleet and seal green on partial acks.

Also records the single-mesh duplicate-delivery trap (one ClusterClient in
Phase 2, not one per Cluster -- a receptionist serves its whole mesh) and one
deviation from the program plan's exit gate: there is no cross-boundary Ask in
Phase 2, because every migrated command is fire-and-forget with a local reply,
so "an Ask timing out cleanly" cannot be run as written.

Marks the program tracking tables current: prereq + Phase 1 are DONE (they
still read "not executed"), Phase 2 in progress.

Claude-Session: https://claude.ai/code/session_01GASWkNEi68FSCtvr6rLoEW
2026-07-22 10:37:37 -04:00

19 lines
2.5 KiB
JSON

{
"planPath": "docs/plans/2026-07-22-mesh-phase2-clusterclient-transport.md",
"note": "Per-cluster mesh Phase 2. Decisions pre-made: config-flagged dark switch (MeshTransport:Mode), buffer-size=0, alarm-commands central leg in scope. Three design-doc corrections and the single-mesh duplicate-delivery trap are documented in the plan header — read them before Task 3.",
"tasks": [
{"id": 0, "subject": "Task 0: MeshTransportOptions + validator + registration", "status": "pending", "classification": "small", "parallelizableWith": [1]},
{"id": 1, "subject": "Task 1: receptionist extension, buffer-size=0, frame-size logging (effective-config test)", "status": "pending", "classification": "small", "parallelizableWith": [0]},
{"id": 2, "subject": "Task 2: MeshCommand envelope + generation-suffixed ClusterClient factory", "status": "pending", "classification": "small", "blockedBy": [0]},
{"id": 3, "subject": "Task 3: CentralCommunicationActor — dark-switched SendToAll fan-out, DB-sourced contacts, ApplyAck forward", "status": "pending", "classification": "high-risk", "blockedBy": [1, 2], "parallelizableWith": [4]},
{"id": 4, "subject": "Task 4: NodeCommunicationActor — inbound to node-local EventStream, outbound ApplyAck", "status": "pending", "classification": "standard", "blockedBy": [1, 2], "parallelizableWith": [3]},
{"id": 5, "subject": "Task 5: node subscribers accept EventStream too; rename _coordinatorOverride -> _ackRouter", "status": "pending", "classification": "standard", "blockedBy": [4]},
{"id": 6, "subject": "Task 6: wire both sides, receptionist registration, pin the wire-contract actor paths", "status": "pending", "classification": "standard", "blockedBy": [3, 5]},
{"id": 7, "subject": "Task 7: route the five publish sites through the mesh router (nullable, mediator fallback)", "status": "pending", "classification": "standard", "blockedBy": [6]},
{"id": 8, "subject": "Task 8: real two-ActorSystem ClusterClient boundary test + falsifiability control", "status": "pending", "classification": "high-risk", "blockedBy": [7], "parallelizableWith": [9]},
{"id": 9, "subject": "Task 9: docker-dev rig config + docs + design-doc correction", "status": "pending", "classification": "small", "blockedBy": [7], "parallelizableWith": [8]},
{"id": 10, "subject": "Task 10: live gate on docker-dev (10 steps; step 4 and step 8 are the ones that matter)", "status": "pending", "classification": "high-risk", "blockedBy": [8, 9]}
],
"lastUpdated": "2026-07-22T00:00:00Z"
}