diff --git a/docs/plans/2026-07-22-clusterclient-to-grpc-live-gate.md b/docs/plans/2026-07-22-clusterclient-to-grpc-live-gate.md index 387c87e7..ff510384 100644 --- a/docs/plans/2026-07-22-clusterclient-to-grpc-live-gate.md +++ b/docs/plans/2026-07-22-clusterclient-to-grpc-live-gate.md @@ -196,3 +196,41 @@ the one `ConfigureKestrel` call — `Program.ParseHttpBindPorts` + `CentralHttpB for a fuller 1A proof if a deployed-instance rig is set up before then. - Cross-node failover/failback of the site→central channel under a central-node kill (unit-proven via TestServer; not exercised on the rig at 1A). + +--- + +## Phase 1B — site command plane (central→site over gRPC) — **PASS (proportionate)** (2026-07-23) + +Branch `feat/grpc-sitecommand` (rebased onto 1A-merged main). Rig rebuilt with **central +flipped to `SiteTransport=Grpc`** — a DoD-test-only edit to both central appsettings, reverted +from the branch (default stays `Akka`). + +`SiteTransport` is a **central-wide** flag (`CentralCommunicationActor.SelectTransport` picks one +transport for all sites), so the plan's "flip for site-a only" is not achievable — the flip +routes central→site commands for **all three sites** to gRPC. Command-plane per-site coexistence +therefore cannot be shown (unlike the site→central plane in 1A, which is per-site). This is a +plan-vs-code finding, recorded rather than worked around. + +### Checks + +| # | Check | Result | +|---|---|---| +| 1 | central→site commands ride authenticated gRPC `SiteCommandService` | **PASS** — `ExecuteQuery` (event-log) and `ExecuteParked` (parked query) HTTP/2 → 200 | +| 2 | Per-site PSK resolution across all sites | **PASS** — site-a, site-b, site-c each answered `ExecuteQuery` → 200 under its own `SB-GRPC-PSK-{site}`; **0** auth failures on any site node | +| 3 | Central HTTP surface intact under the central-wide gRPC flip | **PASS** — central `:5000` **and** `:8083` both listening; 9001 ready `200`, LB `200` (the 1A Kestrel fix carried through the merge) | +| 4 | Query round-trips return correct data | **PASS** — every `health event-log`/`parked-messages` returned `success:true` with the right `siteId` and empty result sets (bare rig) | + +### Not driven on this proportionate gate + +- **`TriggerSiteFailover`** — unit-proven (two ordering tests pin ack-before-`Leave` via the + dispatcher's dry-run resolve + deferred `CommitLeave`), but not live-driven here: it is + destructive (forces the active node to leave) and has no CLI verb (UI/management-only). +- **Tag commands (`BrowseNode`/`ReadTagValues`/`WriteTag`) and the lifecycle enable/disable + matrix** — need a deployed instance + data connection the bare rig lacks (same blocker as 1A). +- **Parked retry against the STANDBY node** — needs a parked operation to exist, which needs a + deployed instance. +- **Command-plane coexistence (site-b/c on Akka while site-a on gRPC)** — not expressible; the + flag is central-wide (check-1/2 instead prove all three sites over gRPC with distinct keys). + +The instance-dependent matrix (tag ops, lifecycle, standby parked retry) and `TriggerSiteFailover` +get their live exercise in Phase 3's full UI command matrix; the transport itself is proven here.