chore(mesh): mark Phase 2 task 8 complete; record the orphaned-client follow-up

Claude-Session: https://claude.ai/code/session_01GASWkNEi68FSCtvr6rLoEW
This commit is contained in:
Joseph Doherty
2026-07-22 17:32:49 -04:00
parent 5468b79d19
commit 4eca87f04d
2 changed files with 18 additions and 2 deletions
@@ -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.
@@ -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,