fix(galaxyrepository): P3 review fixes 003-008 - cycle re-rooting, scaled stale threshold, notifier ordering, DI tests, Pkcs 9.0.21 pin, snapshot docs; bump 0.2.2 (scadaproj#47)

Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
This commit is contained in:
Joseph Doherty
2026-11-09 14:52:00 -05:00
parent 90b691af84
commit 362ba8d75f
12 changed files with 396 additions and 38 deletions
+13 -13
View File
@@ -10,7 +10,7 @@
| Last reviewed | 2026-11-09 |
| Reviewer | Claude |
| Commit reviewed | `91fb2b4` |
| Open findings | 6 |
| Open findings | 0 |
## Summary
@@ -171,7 +171,7 @@ Resolved 2026-11-09 in the commit tagged `(scadaproj#42)` (GalaxyRepository `0.2
| Severity | Low |
| Priority | P3 |
| Category | Correctness & logic bugs |
| Status | Open |
| Status | Resolved |
| Location | `ZB.MOM.WW.GalaxyRepository/src/ZB.MOM.WW.GalaxyRepository/GalaxyHierarchyIndex.cs:106` |
**Description**
@@ -203,7 +203,7 @@ reached (break each cycle at its lowest gobject id) and log a warning naming the
**Resolution**
_Unresolved._
Resolved 2026-11-09 in the commit tagged `(scadaproj#47)` (GalaxyRepository `0.2.2`, unpublished) — `GalaxyHierarchyIndex.Build` now walks reachability from key 0 after bucketing and re-roots any node that lies on a parent cycle (lowest gobject id first; nodes hanging off the cycle become reachable with it). Regression test `ReviewP3Tests.ParentCycle_members_are_reachable_from_the_root_browse` (A→B→C→A plus a leaf; failed without the fix). Not done: the recommended warning log (the index is a static pure builder with no logger).
### GalaxyRepository-004 — Stale threshold is hard-coded at 5 min regardless of the refresh interval, and stale projection defeats the memo
@@ -212,7 +212,7 @@ _Unresolved._
| Severity | Low |
| Priority | P3 |
| Category | Error handling & resilience |
| Status | Open |
| Status | Resolved |
| Location | `ZB.MOM.WW.GalaxyRepository/src/ZB.MOM.WW.GalaxyRepository/GalaxyHierarchyCache.cs:19` (projection at `:55-64`, `:357`) |
**Description**
@@ -244,7 +244,7 @@ instance, so a status-only projection does not invalidate it.
**Resolution**
_Unresolved._
Resolved 2026-11-09 in the commit tagged `(scadaproj#47)` (GalaxyRepository `0.2.2`, unpublished) — the threshold is now `max(5 min, 3 x DashboardRefreshIntervalSeconds)`, read from an optional `IOptions<GalaxyRepositoryOptions>` constructor parameter (DI supplies it; existing callers keep the 5 min floor). The projected-Stale record is memoized per source entry, so `Current` returns the same instance across reads and the projector memos stay warm. Tests `SlowRefreshInterval_does_not_report_Stale_before_three_intervals` and `Stale_projection_returns_the_same_instance_so_projector_memos_stay_warm`.
### GalaxyRepository-005 — Deploy-notifier bootstrap can deliver an older event after a newer one
@@ -253,7 +253,7 @@ _Unresolved._
| Severity | Low |
| Priority | P3 |
| Category | Concurrency & thread safety |
| Status | Open |
| Status | Resolved |
| Location | `ZB.MOM.WW.GalaxyRepository/src/ZB.MOM.WW.GalaxyRepository/GalaxyDeployNotifier.cs:53` |
**Description**
@@ -286,7 +286,7 @@ duplicate case, where the same event arrives both via `Publish` and via bootstra
**Resolution**
_Unresolved._
Resolved 2026-11-09 in the commit tagged `(scadaproj#47)` (GalaxyRepository `0.2.2`, unpublished) — took the reader-side option: `SubscribeAsync` skips any event whose `Sequence` is not greater than the last one yielded, which also drops the duplicate-delivery case (reading `_latest` before registering would instead lose an event published in between). Test `Subscriber_never_yields_an_older_event_after_a_newer_one` (failed without the fix).
### GalaxyRepository-006 — No test builds the container from `AddZbGalaxyRepository`
@@ -295,7 +295,7 @@ _Unresolved._
| Severity | Low |
| Priority | P3 |
| Category | Testing coverage |
| Status | Open |
| Status | Resolved |
| Location | `ZB.MOM.WW.GalaxyRepository/src/ZB.MOM.WW.GalaxyRepository/DependencyInjection/GalaxyRepositoryServiceCollectionExtensions.cs:31` |
**Description**
@@ -332,7 +332,7 @@ Add container-building tests that:
**Resolution**
_Unresolved._
Resolved 2026-11-09 in the commit tagged `(scadaproj#47)` (GalaxyRepository `0.2.2`, unpublished) — new container-building tests in `ReviewP3Tests`: a real `Host` with `AddGrpc()` + `AddZbGalaxyRepository` resolves the repository, notifier, snapshot store, cache (singleton), hosted refresh service and (via `ActivatorUtilities`) `GalaxyRepositoryGrpcService`; starts and stops against an unreachable SQL Server with the cache settling to `Unavailable`; and a host `IGalaxyBrowseScopeProvider` registered before or after the call is the one resolved. Guard test, no behaviour change.
### GalaxyRepository-007 — Shipped package pulls a vulnerable `System.Security.Cryptography.Pkcs` 9.0.4
@@ -341,7 +341,7 @@ _Unresolved._
| Severity | Low |
| Priority | P3 |
| Category | Packaging, dependencies & project layout |
| Status | Open |
| Status | Resolved |
| Location | `ZB.MOM.WW.GalaxyRepository/Directory.Packages.props:9` |
**Description**
@@ -365,7 +365,7 @@ version. Publish this as a patch release.
**Resolution**
_Unresolved._
Resolved 2026-11-09 in the commit tagged `(scadaproj#47)` (GalaxyRepository `0.2.2`, unpublished) — `System.Security.Cryptography.Pkcs` pinned to the patched `9.0.21` in `Directory.Packages.props` and referenced directly from the library csproj (as scadaproj#22 did for Secrets/Auth). Verified: restore resolves `Pkcs/9.0.21` with no NU1902/NU1903. Consumers see the fix only after the `0.2.2` release is published.
### GalaxyRepository-008 — Stale documentation: `SnapshotCachePath` default, csproj comment, consumer matrix
@@ -374,7 +374,7 @@ _Unresolved._
| Severity | Low |
| Priority | P3 |
| Category | Documentation & XML docs |
| Status | Open |
| Status | Resolved |
| Location | `ZB.MOM.WW.GalaxyRepository/src/ZB.MOM.WW.GalaxyRepository/GalaxyRepositoryOptions.cs:47` |
**Description**
@@ -406,4 +406,4 @@ silently does not exist on that host, and nothing logs that persistence is off.
**Resolution**
_Unresolved._
Resolved 2026-11-09 in the commit tagged `(scadaproj#47)` (GalaxyRepository `0.2.2`, unpublished) — `SnapshotCachePath` XML doc now says an empty path disables persistence; `GalaxyHierarchySnapshotStore` logs once at Information when `PersistSnapshot` is true but the path is empty (test `Snapshot_store_logs_once_when_persistence_is_enabled_but_path_is_empty`); stale csproj comment removed; CLAUDE.md consumer matrix updated (HistorianGateway + mxaccessgw).