{ "plan": "docs/plans/2026-07-22-clusterclient-to-grpc-plan.md", "design": "~/Desktop/scadaproj/scadabridge_clusterclient_to_grpc.md", "worktrees": { "feat/grpc-phase0-psk": "/Users/dohertj2/Desktop/ScadaBridge-phase0" }, "tasks": [ { "id": "T0.1", "phase": "0", "subject": "Delete the vestigial /user/management receptionist registration", "status": "completed", "activeForm": "Deleting the /user/management receptionist registration", "files": [ "src/ZB.MOM.WW.ScadaBridge.Host/Actors/AkkaHostedService.cs", "docs/requirements/Component-Host.md", "docs/requirements/Component-ManagementService.md", "docs/requirements/Component-Communication.md", "docs/components/ManagementService.md", "docs/components/Communication.md", "src/ZB.MOM.WW.ScadaBridge.CLI/README.md" ], "notes": "Actor stays; only the ClusterClientReceptionist.RegisterService call goes. Verified: CLI is Akka-free, so 6 docs claiming the CLI reaches ManagementActor over ClusterClient are stale and must be corrected in the same change." }, { "id": "T0.2", "phase": "0", "subject": "Record the dead IntegrationCallRequest exclusion (28 of 29 commands migrate)", "status": "completed", "activeForm": "Recording the dead IntegrationCallRequest exclusion", "files": [ "docs/known-issues/" ], "notes": "Plan says 'file a Gitea issue' \u2014 outward-facing, handed to the user. In-repo half is a known-issues note + the exclusion record." }, { "id": "T0.3", "phase": "0", "subject": "ControlPlaneAuthInterceptor + CommunicationOptions.GrpcPsk + SitePskProvider; gate SiteStream; attach PSK on central's clients", "status": "completed", "activeForm": "Building the control-plane PSK auth", "files": [ "src/ZB.MOM.WW.ScadaBridge.Host/ControlPlaneAuthInterceptor.cs", "src/ZB.MOM.WW.ScadaBridge.Communication/CommunicationOptions.cs", "src/ZB.MOM.WW.ScadaBridge.Communication/Grpc/ISitePskProvider.cs", "src/ZB.MOM.WW.ScadaBridge.Host/SitePskProvider.cs", "src/ZB.MOM.WW.ScadaBridge.Communication/Grpc/SiteStreamGrpcClient.cs", "src/ZB.MOM.WW.ScadaBridge.Communication/Grpc/SiteStreamGrpcClientFactory.cs", "src/ZB.MOM.WW.ScadaBridge.AuditLog/Central/GrpcPullAuditEventsClient.cs", "src/ZB.MOM.WW.ScadaBridge.AuditLog/Central/GrpcPullSiteCallsClient.cs", "src/ZB.MOM.WW.ScadaBridge.Host/Program.cs" ], "notes": "Service prefix verified from sitestream.proto: /sitestream.SiteStreamService/. Grpc 2.76 -> CallCredentials.FromInterceptor + UnsafeUseInsecureChannelCallCredentials is the async-safe attach path on h2c." }, { "id": "T0.4", "phase": "0", "subject": "Rig dev keys (3 sites + central secret seeds) + interceptor/provider/wiring tests", "status": "completed", "activeForm": "Seeding rig dev keys and writing the auth tests", "files": [ "docker/site-a-node-a/appsettings.Site.json", "docker/site-a-node-b/appsettings.Site.json", "docker/site-b-node-a/appsettings.Site.json", "docker/site-b-node-b/appsettings.Site.json", "docker/site-c-node-a/appsettings.Site.json", "docker/site-c-node-b/appsettings.Site.json", "tests/ZB.MOM.WW.ScadaBridge.Host.Tests/ControlPlaneAuthInterceptorTests.cs" ], "notes": "Fail-closed from day one \u2014 every environment (incl. docker-env2 and the gitignored deploy/wonder-app-vd03 overlay) needs its key before upgrade. Ops item for the user." }, { "id": "P0.DoD", "phase": "0", "subject": "Phase 0 DoD: suite green; rig unauthenticated => PermissionDenied, authenticated paths work; PR merged", "status": "in_progress", "activeForm": "Verifying the Phase 0 DoD", "blockedBy": [ "T0.1", "T0.2", "T0.3", "T0.4" ], "notes": "Live gate PASS 2026-07-22 (docs/plans/2026-07-22-clusterclient-to-grpc-live-gate.md). Suite: 29 non-Playwright suites / 6872 tests / 0 failures; Host.Tests 366. Playwright re-run against the stable rig was still in flight at compaction \u2014 the earlier 44 failures were contaminated by a concurrent rig rebuild and are VOID. REMAINING: confirm Playwright, then PR + merge (held for the user per the plan's stop-at-DoD rule)." }, { "id": "T1A.1", "phase": "1A", "subject": "central_control.proto (7 RPCs, checked-in codegen) + CentralControlDtoMapper + round-trip golden tests", "status": "pending", "activeForm": "Authoring central_control.proto and its mappers", "blockedBy": [ "P0.DoD" ] }, { "id": "T1A.2", "phase": "1A", "subject": "Central hosting: AddGrpc + per-site-PSK interceptor, CentralGrpcPort h2c listener, CentralControlGrpcService, readiness gate", "status": "pending", "activeForm": "Hosting CentralControlService on central", "blockedBy": [ "T1A.1" ] }, { "id": "T1A.3", "phase": "1A", "subject": "ICentralTransport (Akka extract + Grpc impl), CentralChannelProvider, CentralTransport flag, CentralGrpcEndpoints option", "status": "pending", "activeForm": "Building the site->central transport seam", "blockedBy": [ "T1A.1" ] }, { "id": "T1A.4", "phase": "1A", "subject": "Tests: actor-with-fake-transport x7, TestServer transport tests, S&F/audit/health suites pass unmodified", "status": "pending", "activeForm": "Testing the central control plane", "blockedBy": [ "T1A.2", "T1A.3" ] }, { "id": "P1A.DoD", "phase": "1A", "subject": "1A DoD: rig site-a on Grpc proves all 5 site->central paths while site-b/c stay Akka; PR merged before 1B", "status": "pending", "activeForm": "Verifying the 1A DoD", "blockedBy": [ "T1A.4" ] }, { "id": "T1B.1", "phase": "1B", "subject": "site_command.proto (6 oneof RPCs / 28 commands) + SiteCommandDtoMapper + round-trip golden tests", "status": "pending", "activeForm": "Authoring site_command.proto and its mappers", "blockedBy": [ "P0.DoD" ] }, { "id": "T1B.2", "phase": "1B", "subject": "SiteCommandDispatcher refactor (actor + SiteCommandGrpcService share it; parked stays node-local)", "status": "pending", "activeForm": "Extracting the site command dispatcher", "blockedBy": [ "T1B.1" ] }, { "id": "T1B.3", "phase": "1B", "subject": "ISiteCommandTransport in CentralCommunicationActor (Akka extract + Grpc impl), SitePairChannelProvider, SiteTransport flag", "status": "pending", "activeForm": "Building the central->site transport seam", "blockedBy": [ "T1B.1" ] }, { "id": "T1B.4", "phase": "1B", "subject": "Tests: dispatcher routing x28, actor envelope/reply plumbing, TestServer service tests, existing suites green", "status": "pending", "activeForm": "Testing the site command plane", "blockedBy": [ "T1B.2", "T1B.3" ] }, { "id": "P1B.DoD", "phase": "1B", "subject": "1B DoD: rig central on Grpc for site-a proves full command matrix incl. standby parked retry; rebased on 1A; PR merged", "status": "pending", "activeForm": "Verifying the 1B DoD", "blockedBy": [ "T1B.4", "P1A.DoD" ] }, { "id": "P2", "phase": "2", "subject": "All sites CentralTransport=Grpc; central-kill S&F soak, failback observed, health sequences clean", "status": "pending", "activeForm": "Running the site->central cutover soak", "blockedBy": [ "P1A.DoD" ] }, { "id": "P3", "phase": "3", "subject": "Central SiteTransport=Grpc all sites; full UI command matrix; site-kill mid-command clean; zero ClusterClient activity", "status": "pending", "activeForm": "Running the central->site cutover soak", "blockedBy": [ "P1B.DoD" ] }, { "id": "P4.1", "phase": "4", "subject": "Flip both flag defaults to Grpc + soak; delete Akka transports, ClusterClient creation, DefaultSiteClientFactory, receptionist registrations, CentralContactPoints, then the flags", "status": "pending", "activeForm": "Deleting the ClusterClient transport", "blockedBy": [ "P2", "P3" ] }, { "id": "P4.2", "phase": "4", "subject": "Grep-gates pass + docs updated (grpc_streams.md, Component-Host.md, Component-StoreAndForward.md, known-issues cross-ref)", "status": "pending", "activeForm": "Running the deletion grep-gates and doc sweep", "blockedBy": [ "P4.1" ] }, { "id": "P5", "phase": "5", "subject": "Live gate, 8 checks, recorded in docs/plans/2026-07-22-clusterclient-to-grpc-live-gate.md", "status": "pending", "activeForm": "Running the live gate", "blockedBy": [ "P4.2" ] } ] }