From 0f6a607fa189aa16163659a785628eee7970d00e Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Thu, 25 Jun 2026 12:36:15 -0400 Subject: [PATCH] docs: record Galaxy library adoption (0.2.0) complete + caveats MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mark A2 handoff and stillpending §2 adopted; note the host-side design (GatewayBrowseScopeProvider, dashboard summary projector), the lib 0.2.0 upstream changes, and caveats (NSSM deploy config, pre-existing NU1903 + IntegrationTests EventStreamService breaks). Point CLAUDE.md at the package. --- A2-galaxyrepository-adoption-handoff.md | 61 +++++++++++++++++++++---- CLAUDE.md | 2 +- stillpending.md | 2 +- 3 files changed, 53 insertions(+), 12 deletions(-) diff --git a/A2-galaxyrepository-adoption-handoff.md b/A2-galaxyrepository-adoption-handoff.md index 2df7b77..88ec33c 100644 --- a/A2-galaxyrepository-adoption-handoff.md +++ b/A2-galaxyrepository-adoption-handoff.md @@ -1,5 +1,24 @@ # A2 — Adopt the shared `ZB.MOM.WW.GalaxyRepository` library +> **✅ ADOPTED 2026-06-25 (branch `feat/galaxyrepository-adoption`).** mxaccessgw now consumes +> `ZB.MOM.WW.GalaxyRepository` **0.2.0** and the inline Galaxy code is deleted (27 files, −2959 LOC). +> What shipped: +> - **Lib 0.2.0** (published to Gitea) closed the two real upstream gaps: **alarm-attribute discovery** +> (`GalaxyAlarmAttributeRow` + `IGalaxyRepository.GetAlarmAttributesAsync` + `AlarmAttributesSql`) and an +> injectable **`IGalaxyBrowseScopeProvider`** (default `NullGalaxyBrowseScopeProvider` = no scoping; +> HistorianGateway @ 0.1.0 unaffected) wired into the lib's gRPC service. Lib test suite: 64 green. +> - **mxaccessgw** registers `GatewayBrowseScopeProvider : IGalaxyBrowseScopeProvider` (reads the API key's +> `EffectiveConstraints.BrowseSubtrees`) before `AddZbGalaxyRepository(config,"MxGateway:Galaxy")`, maps +> `MapZbGalaxyRepository()`, and switched `GatewayGrpcScopeResolver` galaxy request types to the lib +> `.Grpc` namespace (the global authz interceptor now intercepts the lib service). The **dashboard summary +> stays host-side** (`DashboardGalaxySummaryProjector`, memoized by cache `Sequence`) since the lib entry +> doesn't carry it. Server build zero-warning; 327 targeted tests green. +> - The end-to-end host authz chain is covered (`GalaxyRepositoryHostWiringTests`), and the lib gained the +> ported browse-projector / deploy-notifier / refresh-service (Server-005 timeout guard) tests. +> +> **Caveats / follow-ups** (see "Post-adoption notes" at the bottom). +> Original handoff (now historical) follows. + > Handoff note. Written 2026-06-25 from the HistorianGateway side, where the shared lib is > already consumed in production. This is the mxaccessgw half of the cross-repo > "Galaxy-browse normalization" follow-on (HistorianGateway `pending.md` A2 / @@ -153,17 +172,39 @@ be **deleted**. **Keep** the mxaccessgw-specific ones that exercise behavior the `GalaxyAlarmAttributeMappingTests`, `GalaxyFilterInputSafetyTests`, `GalaxyRepositoryGrpcServiceTests` (unless their subjects move upstream too), and the live `IntegrationTests/Galaxy/**`. -## Suggested order +## Suggested order — all DONE (2026-06-25) -1. ~~Verify the **gRPC-service authz parity** question~~ **DONE (2026-06-25):** wholesale swap is unsafe — - per-key browse-subtree filtering is baked into the service body. The service must keep an mxaccessgw - subtree-scoping hook (push the provider upstream, or wrap). See the ⚠️ block above. -2. Decide alarm-attributes: **upstream into the lib (`0.2.0`)** vs keep inline on top of shared interfaces. -3. If upstreaming: do that in `scadaproj/ZB.MOM.WW.GalaxyRepository` first, publish, bump the version. -4. `nuget.config` + `csproj` + DI/endpoint wiring; delete the superseded inline files; rebind namespaces - in dashboard/alarms/security consumers. -5. Delete duplicated tests; build zero-warning; run the suite; live-validate browse + alarm watch-list. -6. Propagate to the scadaproj umbrella index + HistorianGateway's `pending.md` A2 (mark adopted). +1. ~~Verify the **gRPC-service authz parity** question~~ **DONE:** wholesale swap was unsafe — per-key + browse-subtree filtering was baked into the service body. Resolved by pushing an injectable + `IGalaxyBrowseScopeProvider` hook **upstream** into the lib (option preferred in the ⚠️ block). +2. ~~Decide alarm-attributes~~ **DONE:** upstreamed into the lib as part of `0.2.0`. +3. ~~Upstream + publish + bump~~ **DONE:** lib `0.1.0 → 0.2.0`, packed and pushed to the Gitea feed (verified live). +4. ~~`nuget.config` + `csproj` + DI/endpoint wiring; delete inline; rebind~~ **DONE.** +5. ~~Delete duplicated tests; build zero-warning; run the suite~~ **DONE** (lib 64 green; gateway 327 targeted green). + Live-validate browse + alarm watch-list is the one remaining **manual** step (needs Galaxy SQL + a running + gateway — opt-in `MXGATEWAY_RUN_LIVE_GALAXY_TESTS=1`); not runnable from the dev Mac. +6. **Remaining:** propagate to the scadaproj umbrella index + HistorianGateway's `pending.md` §A2 (mark adopted) + — cross-repo, do in those repos. + +## Post-adoption notes / caveats + +- **Deployment config (NSSM):** the deployed services (`MxAccessGw` on 10.100.0.48; the wonder host) read + config from **NSSM environment variables, not `appsettings.json`**. The lib's `SnapshotCachePath` default + is empty (persistence no-ops). `appsettings.json` sets `MxGateway:Galaxy:SnapshotCachePath` + + `PersistSnapshot`, but the deployments must carry `MxGateway__Galaxy__SnapshotCachePath` and + `MxGateway__Galaxy__PersistSnapshot` in their NSSM env on redeploy, or snapshot persistence silently + no-ops in production. +- **Pre-existing NU1903 (unrelated):** adding the package surfaced a transitive `SQLitePCLRaw.lib.e_sqlite3` + 2.1.11 advisory (GHSA-2m69-gcr7-jv3q, no upstream patch) that breaks the build under `TreatWarningsAsErrors` + — already red on `main`. Resolved with a targeted `NuGetAuditSuppress` in `src/Directory.Build.props` + (its own commit). Remove the suppression once a patched e_sqlite3 ships. +- **Pre-existing IntegrationTests break (unrelated, NOT fixed here):** `IntegrationTests/WorkerLiveMxAccessSmokeTests.cs` + constructs `EventStreamService` with 6 ctor args, but a prior event-stream refactor reduced that ctor — so + the IntegrationTests project does not compile (already broken on `main`, independent of Galaxy). The Galaxy + live tests there were rebound to the lib and compile in isolation, but the project won't build until that + unrelated call site is fixed. Track separately. +- **No republish needed for the lib test additions:** the browse-projector / deploy-notifier / refresh-service + tests were added to the lib AFTER 0.2.0 was published; tests aren't shipped, so 0.2.0 is unchanged. ## Reference pointers diff --git a/CLAUDE.md b/CLAUDE.md index 4250aa9..1980a8e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -8,7 +8,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co The architecture is a two-process design — read `gateway.md` before making structural changes: -- **Gateway** (`src/ZB.MOM.WW.MxGateway.Server`, .NET 10, x64): ASP.NET Core gRPC server. Owns the public API, sessions, auth, the Blazor dashboard, and the Galaxy Repository SQL browse RPCs. **Never instantiates MXAccess COM directly.** +- **Gateway** (`src/ZB.MOM.WW.MxGateway.Server`, .NET 10, x64): ASP.NET Core gRPC server. Owns the public API, sessions, auth, the Blazor dashboard, and the Galaxy Repository SQL browse RPCs. The Galaxy-browse implementation comes from the shared **`ZB.MOM.WW.GalaxyRepository`** package (`AddZbGalaxyRepository`/`MapZbGalaxyRepository`), not inline code; mxaccessgw adds `GatewayBrowseScopeProvider` (per-key browse-subtree scoping) and a host-side dashboard summary projector. See `A2-galaxyrepository-adoption-handoff.md`. **Never instantiates MXAccess COM directly.** - **Worker** (`src/ZB.MOM.WW.MxGateway.Worker`, .NET Framework 4.8, **x86**): one process per session. Owns one MXAccess COM instance on a dedicated STA, pumps Windows messages, and converts COM events to protobuf. - **IPC**: gateway↔worker uses one bidirectional named pipe per worker (`mxaccess-gateway-{gatewayPid}-{sessionId}`) with length-prefixed `WorkerEnvelope` protobuf frames. Gateway hosts the pipe server and launches the worker. **gRPC is not used inside the worker** — .NET Framework 4.8 doesn't have a first-class gRPC stack. - **Contracts** (`src/ZB.MOM.WW.MxGateway.Contracts`): multi-targets `net10.0;net48` and owns the `.proto` files (`mxaccess_gateway.proto`, `mxaccess_worker.proto`, `galaxy_repository.proto`). All other projects consume the generated types from here. Do not hand-edit anything under `Generated/`. diff --git a/stillpending.md b/stillpending.md index 06a2d2d..c5d3f2f 100644 --- a/stillpending.md +++ b/stillpending.md @@ -63,7 +63,7 @@ These are documented, deliberate, and mostly enforced. Listed so the deferred su - 🔵 **No server-side / streaming browse search** — `docs/plans/2026-05-28-lazy-browse-design.md:208`. - 🔵 **Alarm command surface is ack + query only** — no Clear/Disable/Enable/Silence/Shelve/Inhibit; matches the MXAccess alarm-client set. `Worker/MxAccess/AlarmCommandHandler.cs`, shelve/suppress out of scope per `docs/AlarmClientDiscovery.md:60-66`. - 🟡 **Dashboard EventsHub has no per-session ACL — still true on `main`, planned (epic Phase 4, not started).** Any authenticated dashboard user may still subscribe to any session group (`Dashboard/Hubs/EventsHub.cs` `TODO(per-session-acl)`). The enabling foundation (session `OwnerKeyId`) already merged in epic Phase 1; epic Phase 4 (Tasks 16–19) adds the gRPC session-owner gate, a session tag + group-to-tag config, and EventsHub per-session ACL with a hub-token tag claim. `docs/plans/2026-06-15-session-resilience.md` Phase 4. (See also §8.) -- 🔵 **Adopt the shared `ZB.MOM.WW.GalaxyRepository` library (cross-repo normalization) — not started.** The inline Galaxy-browse code under `src/ZB.MOM.WW.MxGateway.Server/Galaxy/**` (+ `Grpc/GalaxyRepositoryGrpcService.cs`/`GalaxyProtoMapper.cs`) is the source the shared lib was extracted from; HistorianGateway already consumes it as a `PackageReference @ 0.1.0`. Swapping to the package gives one Galaxy-browse implementation across both sidecars (same `galaxy_repository.v1` wire contract — no client change). Non-trivial: mxaccessgw has extra consumers the lib doesn't cover (alarm watch-list / `GetAlarmAttributesAsync`, dashboard projectors, constraint-authz). The gRPC-service authz-parity gate is **verified (2026-06-25): a wholesale swap to `MapZbGalaxyRepository()` is unsafe** — per-key browse-subtree filtering is baked into mxaccessgw's service body (`Grpc/GalaxyRepositoryGrpcService.cs`), so the service must keep a subtree-scoping hook (push the provider upstream, or wrap), not just rely on the interceptor. Full handoff: **`A2-galaxyrepository-adoption-handoff.md`** (repo root). Tracked cross-repo in HistorianGateway `pending.md` §A2 + scadaproj component normalization. +- ✅ **Adopt the shared `ZB.MOM.WW.GalaxyRepository` library (cross-repo normalization) — RESOLVED (2026-06-25, branch `feat/galaxyrepository-adoption`).** The inline Galaxy-browse code (`src/ZB.MOM.WW.MxGateway.Server/Galaxy/**` + `Grpc/GalaxyRepositoryGrpcService.cs`/`GalaxyProtoMapper.cs`, 27 files / −2959 LOC) is deleted; mxaccessgw now consumes `ZB.MOM.WW.GalaxyRepository` **0.2.0** (published to Gitea), same `galaxy_repository.v1` wire (no client change). The authz-parity gate was resolved by pushing the per-key browse-subtree filter **upstream** as an injectable `IGalaxyBrowseScopeProvider` (default no-op; HistorianGateway @ 0.1.0 unaffected); mxaccessgw supplies `GatewayBrowseScopeProvider` and switched `GatewayGrpcScopeResolver` to the lib proto types. Alarm-attribute discovery (`GetAlarmAttributesAsync`) was upstreamed too; the dashboard summary stays host-side (`DashboardGalaxySummaryProjector`). Lib 64 tests green; gateway 327 targeted green. Full record + caveats (NSSM config, pre-existing NU1903 + IntegrationTests `EventStreamService` breaks): **`A2-galaxyrepository-adoption-handoff.md`**. Remaining: cross-repo propagation (HistorianGateway `pending.md` §A2, scadaproj index) + opt-in live Galaxy-SQL validation. Full handoff: **`A2-galaxyrepository-adoption-handoff.md`** (repo root). Tracked cross-repo in HistorianGateway `pending.md` §A2 + scadaproj component normalization. ---