Files
mxaccessgw/docs/audit/fragments/08-galaxy.md
T

522 lines
24 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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: 34
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: 34
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: 3031
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: 3539
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:2032 (catches `SqlException` and `InvalidOperationException`, returns false); galaxy_repository.proto:4345 (`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:4262 — `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: 3839
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:5863 (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:175190 (`BrowseChildrenReply` with `child_has_children` repeated bool); GalaxyRepositoryGrpcService.cs:112168
CODE_AREA: gr.proto
SEVERITY: low
PROPOSED_FIX: flag only
---
DOC: docs/GalaxyRepository.md
LINES: 4243
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:2728 (`DefaultDiscoverPageSize = 1000`, `MaxDiscoverPageSize = 5000`)
CODE_AREA: gr.proto
SEVERITY: low
PROPOSED_FIX: flag only
---
DOC: docs/GalaxyRepository.md
LINES: 8386
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:295302, `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: 9798
CLAIM: Missing `metadata:read` scope returns `PermissionDenied`.
CLAIM_TYPE: behavior-rule
VERDICT: accurate
EVIDENCE: GatewayGrpcScopeResolver.cs:2327 (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: 118119
CLAIM: `GalaxyHierarchyRefreshService` ticks every `MxGateway:Galaxy:DashboardRefreshIntervalSeconds` seconds (default 30).
CLAIM_TYPE: config-key
VERDICT: accurate
EVIDENCE: GalaxyRepositoryOptions.cs:2829 (`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: 150152
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:7481 (writes to `_path + ".tmp"` then `File.Move(..., overwrite: true)`)
CODE_AREA: gr.conn
SEVERITY: low
PROPOSED_FIX: flag only
---
DOC: docs/GalaxyRepository.md
LINES: 178179
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:4953 (`BoundedChannelOptions` with `FullMode = BoundedChannelFullMode.DropOldest`)
CODE_AREA: gr.proto
SEVERITY: low
PROPOSED_FIX: flag only
---
DOC: docs/GalaxyRepository.md
LINES: 386387
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: 388389
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: 389390
CLAIM: `MxGateway:Galaxy:SnapshotCachePath` default is `C:\ProgramData\MxGateway\galaxy-snapshot.json`.
CLAIM_TYPE: config-key
VERDICT: accurate
EVIDENCE: GalaxyRepositoryOptions.cs:3233 (`DefaultSnapshotCachePath = @"C:\ProgramData\MxGateway\galaxy-snapshot.json"`)
CODE_AREA: gr.conn
SEVERITY: low
PROPOSED_FIX: flag only
---
DOC: docs/GalaxyRepository.md
LINES: 403404
CLAIM: "All four Galaxy RPCs (including `WatchDeployEvents`) require the `metadata:read` API-key scope."
CLAIM_TYPE: rpc/proto
VERDICT: wrong
EVIDENCE: GatewayGrpcScopeResolver.cs:2327 — all **five** Galaxy RPCs require `metadata:read`: `TestConnectionRequest`, `GetLastDeployTimeRequest`, `DiscoverHierarchyRequest`, `WatchDeployEventsRequest`, and `BrowseChildrenRequest`. The service has five RPCs (galaxy_repository.proto:2139), 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:3339 (TestConnection → repository.TestConnectionAsync), :4262 (GetLastDeployTime → cache.Current), :64110 (DiscoverHierarchy → cache.Current), :112168 (BrowseChildren → cache.Current), :171200 (WatchDeployEvents → notifier.SubscribeAsync)
CODE_AREA: gr.proto
SEVERITY: low
PROPOSED_FIX: flag only
---
DOC: docs/GalaxyRepository.md
LINES: 346350
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:117164 (`HierarchySql` with `template_chain` CTE); GalaxyRepository.cs:176251 (`AttributesSql` with `deployed_package_chain` CTE)
CODE_AREA: gr.sql
SEVERITY: low
PROPOSED_FIX: flag only
---
DOC: docs/GalaxyRepository.md
LINES: 347348
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:914 (doc comment confirming this); GalaxyRepository.cs:166175 (comment on AttributesSql confirming divergence)
CODE_AREA: gr.sql
SEVERITY: low
PROPOSED_FIX: flag only
---
DOC: docs/GalaxyRepository.md
LINES: 269270
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: 272273
CLAIM: Built-in attributes are stored in `attribute_definition` and reached through `primitive_instance`.
CLAIM_TYPE: term
VERDICT: accurate
EVIDENCE: GalaxyRepository.cs:214218 (`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: 283284
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: 283285
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:221223 (`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: 285287
CLAIM: "`is_historized` / `is_alarm` are always `false` for built-in rows."
CLAIM_TYPE: behavior-rule
VERDICT: accurate
EVIDENCE: GalaxyRepository.cs:236248 — 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: 288290
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:225228 — `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: 420422
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 419421). The Blazor router has no `/dashboard` prefix.
---
DOC: docs/GalaxyRepository.md
LINES: 419420
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: 369375
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:2022 (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: 110111
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:4662 (GetLastDeployTime reads from cache); :69110 (DiscoverHierarchy reads from cache)
CODE_AREA: gr.proto
SEVERITY: low
PROPOSED_FIX: flag only
---
DOC: docs/GalaxyRepository.md
LINES: 445447
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: 365367
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: 212261
CLAIM: The `GalaxyObject`, `GalaxyAttribute`, `DiscoverHierarchyRequest`, and `DiscoverHierarchyReply` message field numbers and types as shown in the "Reply shape" proto block (field numbers 112 for `GalaxyAttribute`, 112 for `DiscoverHierarchyRequest`, etc.).
CLAIM_TYPE: rpc/proto
VERDICT: accurate
EVIDENCE: galaxy_repository.proto:110191 (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: 399400
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:193196 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:474488 (`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:315320 (`_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:2237 (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:461472 (`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:138142 (parent_gobject_id CASE expression); tables referenced: `gobject` (line 158), `template_definition` (line 159)
CODE_AREA: gr.sql
SEVERITY: low
PROPOSED_FIX: flag only