From 4eca87f04d6d802bf25162ff11e40cfe8ebb8832 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Wed, 22 Jul 2026 17:32:49 -0400 Subject: [PATCH] chore(mesh): mark Phase 2 task 8 complete; record the orphaned-client follow-up Claude-Session: https://claude.ai/code/session_01GASWkNEi68FSCtvr6rLoEW --- ...26-07-22-mesh-phase2-clusterclient-transport.md | 14 ++++++++++++++ ...sh-phase2-clusterclient-transport.md.tasks.json | 6 ++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/docs/plans/2026-07-22-mesh-phase2-clusterclient-transport.md b/docs/plans/2026-07-22-mesh-phase2-clusterclient-transport.md index b46f9015..10d9ba58 100644 --- a/docs/plans/2026-07-22-mesh-phase2-clusterclient-transport.md +++ b/docs/plans/2026-07-22-mesh-phase2-clusterclient-transport.md @@ -1002,3 +1002,17 @@ git commit -m "docs(mesh): Phase 2 live-gate record" - **The AdminUI's `Ok = true` already means "dispatched", not "applied".** Phase 2 moves that lie to a new transport without making it worse; do not let a reviewer believe Phase 2 introduced it. - **`redundancy-state` is bidirectional and carries two unrelated message types** (`RedundancyStateChanged` central→node, `OpcUaProbeResult` node→node on the same topic). It is explicitly **out of scope** here — but note `RedundancyStateActor` builds its snapshot from `Cluster.State`, so it is genuinely mesh-bound and is the hardest thing standing between here and Phase 6. - **`alerts` has a node-local subscriber** (`HistorianAdapterActor`), so Phase 5 cannot simply move it to gRPC without keeping a node-local leg. + +--- + +## Follow-ups found during execution (not fixed in Phase 2) + +- **`DefaultMeshClusterClientFactory`'s generation counter is per factory instance, and its clients + are top-level actors.** Production creates one factory per `CentralCommunicationActor` and captures + it in the props closure, so the counter survives a supervision restart and names stay unique — that + part is sound. What is *not* handled: the clients are `system.ActorOf`, not children, so a restart + of the comm actor loses `_client` without stopping it and leaves an orphaned ClusterClient dialling + receptionists forever. Harmless today (nothing sends to it) but it is a leak per restart, and it + will matter in Phase 6 when there is one client per cluster. Found by + `MeshClusterClientBoundaryTests`, which hit the name collision the moment a second factory was + constructed on the same system. diff --git a/docs/plans/2026-07-22-mesh-phase2-clusterclient-transport.md.tasks.json b/docs/plans/2026-07-22-mesh-phase2-clusterclient-transport.md.tasks.json index de080501..4a499e2b 100644 --- a/docs/plans/2026-07-22-mesh-phase2-clusterclient-transport.md.tasks.json +++ b/docs/plans/2026-07-22-mesh-phase2-clusterclient-transport.md.tasks.json @@ -100,14 +100,16 @@ { "id": 8, "subject": "Task 8: real two-ActorSystem ClusterClient boundary test + falsifiability control", - "status": "pending", + "status": "completed", "classification": "high-risk", "blockedBy": [ 7 ], "parallelizableWith": [ 9 - ] + ], + "commit": "5468b79d", + "note": "Not flaky (3/3 clean repeats; 4 tests, ~16s). Both legs sabotage-verified against production code, and the control was itself falsified by restoring the receptionist. The control necessarily removes RegisterService too -- registering would materialise the extension it is removing. Task 6's dropped per-node/singleton property does NOT land here (one node per side cannot discriminate); it is live-gate step 8, and the MeshCommActorPathTests comment was corrected to say so." }, { "id": 9,