24 KiB
Cluster 08 — Galaxy Repository
Audited doc: docs/GalaxyRepository.md
Verified against: src/ZB.MOM.WW.MxGateway.Server/Galaxy/**, src/ZB.MOM.WW.MxGateway.Contracts/Protos/galaxy_repository.proto
Date: 2026-06-03
DOC: docs/GalaxyRepository.md
LINES: 3–4
CLAIM: The SQL Server database is named ZB.
CLAIM_TYPE: config-key
VERDICT: accurate
EVIDENCE: GalaxyRepositoryOptions.cs:17 (DefaultConnectionString = "Server=localhost;Database=ZB;...")
CODE_AREA: gr.conn
SEVERITY: low
PROPOSED_FIX: flag only
DOC: docs/GalaxyRepository.md
LINES: 3–4
CLAIM: The database is a SQL Server database.
CLAIM_TYPE: config-key
VERDICT: accurate
EVIDENCE: GalaxyRepository.cs:1 (using Microsoft.Data.SqlClient;); GalaxyRepositoryOptions.cs:17
CODE_AREA: gr.conn
SEVERITY: low
PROPOSED_FIX: flag only
DOC: docs/GalaxyRepository.md
LINES: 30–31
CLAIM: The service is defined in src/ZB.MOM.WW.MxGateway.Contracts/Protos/galaxy_repository.proto under package galaxy_repository.v1.
CLAIM_TYPE: path
VERDICT: accurate
EVIDENCE: galaxy_repository.proto:3 (package galaxy_repository.v1;)
CODE_AREA: gr.proto
SEVERITY: low
PROPOSED_FIX: flag only
DOC: docs/GalaxyRepository.md
LINES: 35–39
CLAIM: TestConnection returns { ok: bool } after a SELECT 1. Does not throw on SQL failure — returns ok = false.
CLAIM_TYPE: behavior-rule
VERDICT: accurate
EVIDENCE: GalaxyRepository.cs:20–32 (catches SqlException and InvalidOperationException, returns false); galaxy_repository.proto:43–45 (TestConnectionReply { bool ok = 1; })
CODE_AREA: gr.proto
SEVERITY: low
PROPOSED_FIX: flag only
DOC: docs/GalaxyRepository.md
LINES: 36
CLAIM: GetLastDeployTime returns the cached galaxy.time_of_last_deploy. Served from the shared hierarchy cache; refreshed in the background.
CLAIM_TYPE: behavior-rule
VERDICT: wrong
EVIDENCE: GalaxyRepositoryGrpcService.cs:42–62 — GetLastDeployTime calls WaitForCacheBootstrap then reads cache.Current, not repository directly. The underlying SQL is SELECT time_of_last_deploy FROM galaxy (GalaxyRepository.cs:40) but it is served from cache, not direct SQL. The doc correctly says "served from cache" in the inline column. However the inline description says "Served from the shared hierarchy cache; refreshed in the background" which is accurate for the RPC handler — but the SQL column itself (galaxy.time_of_last_deploy) is an internal SQL column name, not a table.column phrasing. No actual error; accurate.
CODE_AREA: gr.sql
SEVERITY: low
PROPOSED_FIX: flag only
DOC: docs/GalaxyRepository.md
LINES: 38–39
CLAIM: WatchDeployEvents is server-streaming. The server emits the current state immediately on subscribe.
CLAIM_TYPE: behavior-rule
VERDICT: accurate
EVIDENCE: galaxy_repository.proto:33 (rpc WatchDeployEvents ... returns (stream DeployEvent)); GalaxyDeployNotifier.cs:58–63 (bootstrap emit on subscribe)
CODE_AREA: gr.proto
SEVERITY: low
PROPOSED_FIX: flag only
DOC: docs/GalaxyRepository.md
LINES: 39
CLAIM: BrowseChildren returns the direct children of one parent object (or root objects when parent is unset). Includes a per-child has_children hint so UIs can draw expand triangles without an extra round trip. Served from cache.
CLAIM_TYPE: behavior-rule
VERDICT: accurate
EVIDENCE: galaxy_repository.proto:175–190 (BrowseChildrenReply with child_has_children repeated bool); GalaxyRepositoryGrpcService.cs:112–168
CODE_AREA: gr.proto
SEVERITY: low
PROPOSED_FIX: flag only
DOC: docs/GalaxyRepository.md
LINES: 42–43
CLAIM: The server defaults omitted page size to 1000 objects and caps every page at 5000 objects (for DiscoverHierarchy).
CLAIM_TYPE: behavior-rule
VERDICT: accurate
EVIDENCE: GalaxyRepositoryGrpcService.cs:27–28 (DefaultDiscoverPageSize = 1000, MaxDiscoverPageSize = 5000)
CODE_AREA: gr.proto
SEVERITY: low
PROPOSED_FIX: flag only
DOC: docs/GalaxyRepository.md
LINES: 83–86
CLAIM: BrowseChildren default page size is 500; the server caps any requested size at 5000. Page tokens encode (cache_sequence, parent_id, filter_signature, offset).
CLAIM_TYPE: behavior-rule
VERDICT: wrong
EVIDENCE: GalaxyRepositoryGrpcService.cs:29 (DefaultBrowsePageSize = 500) — default is accurate. Cap of 5000 is accurate (comment "MaxBrowsePageSize reuses MaxDiscoverPageSize (5000)"). However the token encoding claim is inaccurate: the actual token format is sequence:filterSignature:offset (GalaxyRepositoryGrpcService.cs:295–302, FormatPageToken). parent_id is embedded inside filterSignature as a component (GalaxyBrowseProjector.cs:266 builder.Append("parent=").Append(parentId...)) — it is NOT a separate named field in the token. Describing it as (cache_sequence, parent_id, filter_signature, offset) implies four independent fields; the wire encoding has three fields with parent_id folded into the signature hash.
CODE_AREA: gr.proto
SEVERITY: medium
PROPOSED_FIX: Change "Page tokens encode (cache_sequence, parent_id, filter_signature, offset)" to "Page tokens encode sequence:filterSignature:offset; parent_id is incorporated into filterSignature along with the other filter parameters."
DOC: docs/GalaxyRepository.md
LINES: 97–98
CLAIM: Missing metadata:read scope returns PermissionDenied.
CLAIM_TYPE: behavior-rule
VERDICT: accurate
EVIDENCE: GatewayGrpcScopeResolver.cs:23–27 (all five Galaxy request types map to GatewayScopes.MetadataRead); GatewayScopes.cs:11
CODE_AREA: gr.proto
SEVERITY: low
PROPOSED_FIX: flag only
DOC: docs/GalaxyRepository.md
LINES: 118–119
CLAIM: GalaxyHierarchyRefreshService ticks every MxGateway:Galaxy:DashboardRefreshIntervalSeconds seconds (default 30).
CLAIM_TYPE: config-key
VERDICT: accurate
EVIDENCE: GalaxyRepositoryOptions.cs:28–29 (DashboardRefreshIntervalSeconds { get; init; } = 30); GalaxyHierarchyRefreshService.cs:18 (TimeSpan.FromSeconds(Math.Max(1, options.Value.DashboardRefreshIntervalSeconds)))
CODE_AREA: gr.conn
SEVERITY: low
PROPOSED_FIX: flag only
DOC: docs/GalaxyRepository.md
LINES: 120
CLAIM: Each tick queries the cheap SELECT time_of_last_deploy FROM galaxy first.
CLAIM_TYPE: behavior-rule
VERDICT: accurate
EVIDENCE: GalaxyRepository.cs:40 ("SELECT time_of_last_deploy FROM galaxy"); GalaxyHierarchyCache.cs:117 (GetLastDeployTimeAsync called first before deciding whether to run heavy queries)
CODE_AREA: gr.sql
SEVERITY: low
PROPOSED_FIX: flag only
DOC: docs/GalaxyRepository.md
LINES: 150–152
CLAIM: The snapshot file is written atomically — a temp file plus rename — so a crash mid-write cannot corrupt the snapshot.
CLAIM_TYPE: behavior-rule
VERDICT: accurate
EVIDENCE: GalaxyHierarchySnapshotStore.cs:74–81 (writes to _path + ".tmp" then File.Move(..., overwrite: true))
CODE_AREA: gr.conn
SEVERITY: low
PROPOSED_FIX: flag only
DOC: docs/GalaxyRepository.md
LINES: 178–179
CLAIM: GalaxyDeployNotifier maintains a private bounded channel per subscriber. The bound is 16 events with DropOldest.
CLAIM_TYPE: behavior-rule
VERDICT: accurate
EVIDENCE: GalaxyDeployNotifier.cs:18 (SubscriberQueueCapacity = 16); GalaxyDeployNotifier.cs:49–53 (BoundedChannelOptions with FullMode = BoundedChannelFullMode.DropOldest)
CODE_AREA: gr.proto
SEVERITY: low
PROPOSED_FIX: flag only
DOC: docs/GalaxyRepository.md
LINES: 386–387
CLAIM: Default connection string is Server=localhost;Database=ZB;Integrated Security=True;TrustServerCertificate=True;Encrypt=False;
CLAIM_TYPE: config-key
VERDICT: accurate
EVIDENCE: GalaxyRepositoryOptions.cs:17 (exact match)
CODE_AREA: gr.conn
SEVERITY: low
PROPOSED_FIX: flag only
DOC: docs/GalaxyRepository.md
LINES: 387
CLAIM: MxGateway:Galaxy:CommandTimeoutSeconds default is 60. Applies to all three RPCs.
CLAIM_TYPE: config-key
VERDICT: wrong
EVIDENCE: GalaxyRepositoryOptions.cs:22 (CommandTimeoutSeconds { get; init; } = 60) — default is accurate. However "Applies to all three RPCs" is stale: there are five RPCs (TestConnection, GetLastDeployTime, DiscoverHierarchy, WatchDeployEvents, BrowseChildren), not three. CommandTimeoutSeconds applies to the SQL commands in GalaxyRepository.cs which backs TestConnection, GetLastDeployTime, GetHierarchyAsync, and GetAttributesAsync. The doc says "all three RPCs" presumably counting only the original three before BrowseChildren was added.
CODE_AREA: gr.conn
SEVERITY: medium
PROPOSED_FIX: Change "Applies to all three RPCs" to "Applies to all SQL commands issued by the repository (used by TestConnection, GetLastDeployTime, and the hierarchy/attributes queries backing DiscoverHierarchy and BrowseChildren)."
DOC: docs/GalaxyRepository.md
LINES: 388–389
CLAIM: MxGateway:Galaxy:PersistSnapshot default is true.
CLAIM_TYPE: config-key
VERDICT: accurate
EVIDENCE: GalaxyRepositoryOptions.cs:40 (PersistSnapshot { get; init; } = true)
CODE_AREA: gr.conn
SEVERITY: low
PROPOSED_FIX: flag only
DOC: docs/GalaxyRepository.md
LINES: 389–390
CLAIM: MxGateway:Galaxy:SnapshotCachePath default is C:\ProgramData\MxGateway\galaxy-snapshot.json.
CLAIM_TYPE: config-key
VERDICT: accurate
EVIDENCE: GalaxyRepositoryOptions.cs:32–33 (DefaultSnapshotCachePath = @"C:\ProgramData\MxGateway\galaxy-snapshot.json")
CODE_AREA: gr.conn
SEVERITY: low
PROPOSED_FIX: flag only
DOC: docs/GalaxyRepository.md
LINES: 403–404
CLAIM: "All four Galaxy RPCs (including WatchDeployEvents) require the metadata:read API-key scope."
CLAIM_TYPE: rpc/proto
VERDICT: wrong
EVIDENCE: GatewayGrpcScopeResolver.cs:23–27 — all five Galaxy RPCs require metadata:read: TestConnectionRequest, GetLastDeployTimeRequest, DiscoverHierarchyRequest, WatchDeployEventsRequest, and BrowseChildrenRequest. The service has five RPCs (galaxy_repository.proto:21–39), not four. BrowseChildren was added after the original four but the authorization section was not updated.
CODE_AREA: gr.proto
SEVERITY: high
PROPOSED_FIX: Change "All four Galaxy RPCs" to "All five Galaxy RPCs" (or explicitly list all five: TestConnection, GetLastDeployTime, DiscoverHierarchy, WatchDeployEvents, BrowseChildren).
DOC: docs/GalaxyRepository.md
LINES: 378
CLAIM: "GalaxyRepositoryGrpcService (src/ZB.MOM.WW.MxGateway.Server/Grpc/GalaxyRepositoryGrpcService.cs) implements the five RPCs."
CLAIM_TYPE: path
VERDICT: accurate
EVIDENCE: GalaxyRepositoryGrpcService.cs (file exists at that path); implements all five overrides: TestConnection, GetLastDeployTime, DiscoverHierarchy, WatchDeployEvents, BrowseChildren
CODE_AREA: gr.proto
SEVERITY: low
PROPOSED_FIX: flag only
DOC: docs/GalaxyRepository.md
LINES: 327
CLAIM: Architecture diagram shows DiscoverHierarchy, GetLastDeployTime, BrowseChildren -> IGalaxyHierarchyCache.Current (WatchDeployEvents -> IGalaxyDeployNotifier, TestConnection -> GalaxyRepository direct SQL).
CLAIM_TYPE: behavior-rule
VERDICT: accurate
EVIDENCE: GalaxyRepositoryGrpcService.cs:33–39 (TestConnection → repository.TestConnectionAsync), :42–62 (GetLastDeployTime → cache.Current), :64–110 (DiscoverHierarchy → cache.Current), :112–168 (BrowseChildren → cache.Current), :171–200 (WatchDeployEvents → notifier.SubscribeAsync)
CODE_AREA: gr.proto
SEVERITY: low
PROPOSED_FIX: flag only
DOC: docs/GalaxyRepository.md
LINES: 346–350
CLAIM: "GalaxyRepository (src/ZB.MOM.WW.MxGateway.Server/Galaxy/GalaxyRepository.cs) holds the SQL. Both HierarchySql and AttributesSql walk template-derivation and package-derivation chains via recursive CTEs."
CLAIM_TYPE: path
VERDICT: accurate
EVIDENCE: GalaxyRepository.cs:117–164 (HierarchySql with template_chain CTE); GalaxyRepository.cs:176–251 (AttributesSql with deployed_package_chain CTE)
CODE_AREA: gr.sql
SEVERITY: low
PROPOSED_FIX: flag only
DOC: docs/GalaxyRepository.md
LINES: 347–348
CLAIM: "HierarchySql still matches the OtOpcUa original; AttributesSql does not — it additionally enumerates built-in primitive attributes."
CLAIM_TYPE: behavior-rule
VERDICT: accurate
EVIDENCE: GalaxyRepository.cs:9–14 (doc comment confirming this); GalaxyRepository.cs:166–175 (comment on AttributesSql confirming divergence)
CODE_AREA: gr.sql
SEVERITY: low
PROPOSED_FIX: flag only
DOC: docs/GalaxyRepository.md
LINES: 269–270
CLAIM: Configured (dynamic) attributes are stored in the Galaxy dynamic_attribute table.
CLAIM_TYPE: term
VERDICT: accurate
EVIDENCE: GalaxyRepository.cs:196 (INNER JOIN dynamic_attribute da ON da.package_id = dpc.package_id)
CODE_AREA: gr.sql
SEVERITY: low
PROPOSED_FIX: flag only
DOC: docs/GalaxyRepository.md
LINES: 272–273
CLAIM: Built-in attributes are stored in attribute_definition and reached through primitive_instance.
CLAIM_TYPE: term
VERDICT: accurate
EVIDENCE: GalaxyRepository.cs:214–218 (INNER JOIN primitive_instance pi ON pi.package_id = dpc.package_id / INNER JOIN attribute_definition ad ON ad.primitive_definition_id = pi.primitive_definition_id)
CODE_AREA: gr.sql
SEVERITY: low
PROPOSED_FIX: flag only
DOC: docs/GalaxyRepository.md
LINES: 283–284
CLAIM: The configured-attribute category allow-list is mx_attribute_category IN (2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 24).
CLAIM_TYPE: behavior-rule
VERDICT: accurate
EVIDENCE: GalaxyRepository.cs:203 (AND da.mx_attribute_category IN (2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 24))
CODE_AREA: gr.sql
SEVERITY: low
PROPOSED_FIX: flag only
DOC: docs/GalaxyRepository.md
LINES: 283–285
CLAIM: No category filter applies to built-in rows (attribute_definition); only the _-prefixed-name and .Description exclusions apply.
CLAIM_TYPE: behavior-rule
VERDICT: accurate
EVIDENCE: GalaxyRepository.cs:221–223 (AND ad.attribute_name NOT LIKE '[_]%' and NOT LIKE '%.Description' — no mx_attribute_category filter for the built-in branch)
CODE_AREA: gr.sql
SEVERITY: low
PROPOSED_FIX: flag only
DOC: docs/GalaxyRepository.md
LINES: 285–287
CLAIM: "is_historized / is_alarm are always false for built-in rows."
CLAIM_TYPE: behavior-rule
VERDICT: accurate
EVIDENCE: GalaxyRepository.cs:236–248 — both is_historized and is_alarm use CASE WHEN r.src_pri = 0 AND EXISTS (...) — built-in rows have src_pri = 1 so both expressions evaluate to 0 (false).
CODE_AREA: gr.sql
SEVERITY: low
PROPOSED_FIX: flag only
DOC: docs/GalaxyRepository.md
LINES: 288–290
CLAIM: "When a configured attribute and a built-in attribute resolve to the same reference, the configured attribute wins."
CLAIM_TYPE: behavior-rule
VERDICT: accurate
EVIDENCE: GalaxyRepository.cs:225–228 — ROW_NUMBER() OVER (PARTITION BY c.gobject_id, c.attribute_name ORDER BY c.src_pri, c.depth) — src_pri = 0 for configured rows, src_pri = 1 for built-ins, so configured attributes are ranked first.
CODE_AREA: gr.sql
SEVERITY: low
PROPOSED_FIX: flag only
DOC: docs/GalaxyRepository.md
LINES: 420–422
CLAIM: Dashboard /dashboard/galaxy page with object-category and top-template breakdowns.
CLAIM_TYPE: path
VERDICT: wrong
EVIDENCE: GalaxyPage.razor:1 — the page route is @page "/galaxy", not /dashboard/galaxy. The Blazor app is mounted without a /dashboard prefix (DashboardEndpointRouteBuilderExtensions.cs:86, MapRazorComponents<App>()). The Galaxy page is at /galaxy, not /dashboard/galaxy. The home page at @page "/" is the dashboard overview, not at /dashboard.
CODE_AREA: gr.proto
SEVERITY: high
PROPOSED_FIX: Change /dashboard/galaxy to /galaxy and /dashboard to / throughout the Dashboard Surface section (lines 419–421). The Blazor router has no /dashboard prefix.
DOC: docs/GalaxyRepository.md
LINES: 419–420
CLAIM: "An overview card on /dashboard showing connectivity status..."
CLAIM_TYPE: path
VERDICT: wrong
EVIDENCE: DashboardHome.razor:1 — @page "/". The home/overview page is at /, not /dashboard.
CODE_AREA: gr.proto
SEVERITY: high
PROPOSED_FIX: Change /dashboard to / in the Dashboard Surface section.
DOC: docs/GalaxyRepository.md
LINES: 369–375
CLAIM: "GalaxyBrowseProjector (src/ZB.MOM.WW.MxGateway.Server/Galaxy/GalaxyBrowseProjector.cs) projects one level of children out of an immutable cache entry. Memoizes the filtered child list per cache-entry instance so repeated paging is an O(pageSize) slice rather than an O(siblings) filter scan. The memo is keyed on the cache entry reference, so a new entry from the background refresh makes the stale memo unreachable and it is collected with it. DashboardBrowseService wraps this projector to drive the dashboard's lazy-expand tree."
CLAIM_TYPE: behavior-rule
VERDICT: accurate
EVIDENCE: GalaxyBrowseProjector.cs:20–22 (ConditionalWeakTable keyed on GalaxyHierarchyCacheEntry); DashboardBrowseService.cs:55 (GalaxyBrowseProjector.ProjectChildren called inside DashboardBrowseService)
CODE_AREA: gr.proto
SEVERITY: low
PROPOSED_FIX: flag only
DOC: docs/GalaxyRepository.md
LINES: 110–111
CLAIM: "IGalaxyHierarchyCache (src/ZB.MOM.WW.MxGateway.Server/Galaxy/GalaxyHierarchyCache.cs) — every DiscoverHierarchy and GetLastDeployTime request reads from this cache."
CLAIM_TYPE: path
VERDICT: accurate
EVIDENCE: GalaxyHierarchyCache.cs (file at that path); GalaxyRepositoryGrpcService.cs:46–62 (GetLastDeployTime reads from cache); :69–110 (DiscoverHierarchy reads from cache)
CODE_AREA: gr.proto
SEVERITY: low
PROPOSED_FIX: flag only
DOC: docs/GalaxyRepository.md
LINES: 445–447
CLAIM: "Integration tests live in src/ZB.MOM.WW.MxGateway.IntegrationTests/Galaxy/GalaxyRepositoryLiveTests.cs. Set MXGATEWAY_RUN_LIVE_GALAXY_TESTS=1 (and optionally MXGATEWAY_LIVE_GALAXY_CONN) to run them."
CLAIM_TYPE: path
VERDICT: accurate
EVIDENCE: GalaxyRepositoryLiveTests.cs (file exists at that path); LiveGalaxyRepositoryFactAttribute.cs:9 (EnableVariableName = "MXGATEWAY_RUN_LIVE_GALAXY_TESTS"); LiveGalaxyRepositoryFactAttribute.cs:11 (ConnectionStringVariableName = "MXGATEWAY_LIVE_GALAXY_CONN")
CODE_AREA: gr.proto
SEVERITY: low
PROPOSED_FIX: flag only
DOC: docs/GalaxyRepository.md
LINES: 365–367
CLAIM: "GalaxyProtoMapper (src/ZB.MOM.WW.MxGateway.Server/Grpc/GalaxyProtoMapper.cs) converts row models to proto messages. Used by the cache during refresh to materialize the reply once."
CLAIM_TYPE: path
VERDICT: accurate
EVIDENCE: GalaxyProtoMapper.cs (file at that path); GalaxyHierarchyCache.cs:223 (BuildObjects → GalaxyProtoMapper.MapObject)
CODE_AREA: gr.proto
SEVERITY: low
PROPOSED_FIX: flag only
DOC: docs/GalaxyRepository.md
LINES: 212–261
CLAIM: The GalaxyObject, GalaxyAttribute, DiscoverHierarchyRequest, and DiscoverHierarchyReply message field numbers and types as shown in the "Reply shape" proto block (field numbers 1–12 for GalaxyAttribute, 1–12 for DiscoverHierarchyRequest, etc.).
CLAIM_TYPE: rpc/proto
VERDICT: accurate
EVIDENCE: galaxy_repository.proto:110–191 (all field numbers and types match the doc's code block)
CODE_AREA: gr.proto
SEVERITY: low
PROPOSED_FIX: flag only
DOC: docs/GalaxyRepository.md
LINES: 399–400
CLAIM: Dashboard "displays only non-secret fields: server, database, integrated security, encrypt, and trust-server-certificate. It never displays user id, password, access token, or arbitrary unparsed connection string text."
CLAIM_TYPE: behavior-rule
VERDICT: unverifiable
EVIDENCE: GalaxyPage.razor:129 (DashboardDisplay.Text(GalaxyConnectionStringDisplay())); GalaxyPage.razor:193–196 delegates to DashboardConnectionStringDisplay.GalaxyRepositoryConnectionString. The actual display logic lives in DashboardConnectionStringDisplay which was not found in this audit scope. The behavior is asserted plausibly consistent with "never display user id, password" but the implementation of DashboardConnectionStringDisplay was not directly verified.
CODE_AREA: gr.conn
SEVERITY: low
PROPOSED_FIX: flag only — verify DashboardConnectionStringDisplay filters fields as claimed.
DOC: docs/GalaxyRepository.md
LINES: N/A — not covered in doc
CLAIM: GAP — GalaxyHierarchyCache projects Status to Stale when LastSuccessAt is more than 5 minutes old (regardless of the stored status), via ProjectStatus with StaleThreshold = TimeSpan.FromMinutes(5).
CLAIM_TYPE: behavior-rule
VERDICT: gap
EVIDENCE: GalaxyHierarchyCache.cs:22 (StaleThreshold = TimeSpan.FromMinutes(5)); GalaxyHierarchyCache.cs:474–488 (ProjectStatus method)
CODE_AREA: gr.proto
SEVERITY: medium
PROPOSED_FIX: Add a note under "Hierarchy Cache" that the cache also auto-degrades to Stale status when more than 5 minutes have elapsed since the last successful refresh, independent of the stored entry status. This matters for operators diagnosing why a Healthy entry flips to Stale without a SQL failure.
DOC: docs/GalaxyRepository.md
LINES: N/A — not covered in doc
CLAIM: GAP — WatchDeployEvents emits a bootstrap event even on a snapshot-restore (from on-disk data), not only from live SQL queries. GalaxyHierarchyCache.TryRestoreFromDiskAsync calls _notifier.Publish after restoring.
CLAIM_TYPE: behavior-rule
VERDICT: gap
EVIDENCE: GalaxyHierarchyCache.cs:315–320 (_notifier.Publish called from TryRestoreFromDiskAsync)
CODE_AREA: gr.proto
SEVERITY: low
PROPOSED_FIX: Note under "Deploy Notifications" or "On-disk snapshot" that restoring the snapshot also publishes a deploy event so WatchDeployEvents subscribers receive a bootstrap event even when SQL is unreachable at startup.
DOC: docs/GalaxyRepository.md
LINES: N/A — not covered in doc
CLAIM: GAP — GalaxyHierarchyRefreshService runs an initial RefreshAsync immediately on startup (before starting the periodic timer), so the first load happens at process start, not after the first tick of DashboardRefreshIntervalSeconds.
CLAIM_TYPE: behavior-rule
VERDICT: gap
EVIDENCE: GalaxyHierarchyRefreshService.cs:22–37 (initial await cache.RefreshAsync before PeriodicTimer is created)
CODE_AREA: gr.proto
SEVERITY: low
PROPOSED_FIX: Add a note under "Hierarchy Cache" that the first refresh runs immediately at gateway startup and does not wait for the first timer tick.
DOC: docs/GalaxyRepository.md
LINES: N/A — not covered in doc
CLAIM: GAP — The HierarchySql category filter (td.category_id IN (1, 3, 4, 10, 11, 13, 17, 24, 26)) and the specific category IDs mapped to names (WinPlatform=1, AppEngine=3, InTouchViewApp=4, UserDefined=10, FieldReference=11, Area=13, DIObject=17, DDESuiteLinkClient=24, OPCClient=26) are not documented anywhere in GalaxyRepository.md.
CLAIM_TYPE: behavior-rule
VERDICT: gap
EVIDENCE: GalaxyRepository.cs:161 (HierarchySql WHERE clause with category IDs); GalaxyHierarchyCache.cs:461–472 (ResolveCategoryName method mapping each ID to a name)
CODE_AREA: gr.sql
SEVERITY: medium
PROPOSED_FIX: Add a table of the filtered category IDs and their names (WinPlatform, AppEngine, InTouchViewApp, etc.) to the doc. Operators need to know which object types are included — an AppEngine that doesn't appear in browse results is hard to diagnose without this list.
DOC: docs/GalaxyRepository.md
LINES: N/A — not covered in doc
CLAIM: GAP — The AttributesSql uses the data_type table to resolve data_type_name (LEFT JOIN data_type dt ON dt.mx_data_type = r.mx_data_type). The Galaxy table name data_type is not mentioned in the doc.
CLAIM_TYPE: term
VERDICT: gap
EVIDENCE: GalaxyRepository.cs:249 (LEFT JOIN data_type dt ON dt.mx_data_type = r.mx_data_type)
CODE_AREA: gr.sql
SEVERITY: low
PROPOSED_FIX: flag only
DOC: docs/GalaxyRepository.md
LINES: N/A — not covered in doc
CLAIM: GAP — The HierarchySql uses the tables gobject and template_definition, and maps parent_gobject_id using CASE WHEN g.contained_by_gobject_id = 0 THEN g.area_gobject_id ELSE g.contained_by_gobject_id END. This parent resolution logic (area_gobject_id fallback) is not mentioned.
CLAIM_TYPE: behavior-rule
VERDICT: gap
EVIDENCE: GalaxyRepository.cs:138–142 (parent_gobject_id CASE expression); tables referenced: gobject (line 158), template_definition (line 159)
CODE_AREA: gr.sql
SEVERITY: low
PROPOSED_FIX: flag only