docs: record Galaxy library adoption (0.2.0) complete + caveats

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.
This commit is contained in:
Joseph Doherty
2026-06-25 12:36:15 -04:00
parent 662dd1b958
commit 0f6a607fa1
3 changed files with 53 additions and 12 deletions
+51 -10
View File
@@ -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
+1 -1
View File
@@ -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/`.
+1 -1
View File
@@ -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 1619) 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.
---