fix(code-review): resolve Batch 2 open findings (AbCip, AbLegacy, Galaxy, FOCAS)

- Driver.AbCip.Contracts-001: parse 'writable' from TagConfig JSON (default true) instead of hardcoding
- Driver.AbCip.Contracts-002/-003: Dt type comment; drop dead [Display]/[Range] annotations
- Driver.AbCip.Contracts-004: dedicated AbCipEquipmentTagParser test class (+15)
- Driver.AbCip-017: document Tick severity Low-fallback on Bad severity read
- Driver.AbLegacy.Contracts-002/-003/-004: isArray-scalar remarks (+tests), MaxTagBytes/ForFamily docs
- Driver.Galaxy.Browser-003 + Driver.Galaxy.Contracts-003: extract ResolveApiKey -> GalaxySecretRef (dedup)
- Driver.Galaxy-019: cache buffered-interval only on Ok + ILogger warnings + ClassifyIntervalReply (+tests)
- Driver.FOCAS.Contracts-002: thread WriteIdempotent through DiscoverAsync (+test)
This commit is contained in:
Joseph Doherty
2026-06-20 22:43:36 -04:00
parent 3cc6a5f30d
commit ab57e53b92
26 changed files with 577 additions and 220 deletions
+23 -19
View File
@@ -7,7 +7,7 @@
| Review date | 2026-06-19 |
| Commit reviewed | `7286d320` |
| Status | Reviewed |
| Open findings | 1 |
| Open findings | 0 |
## Checklist coverage
@@ -83,7 +83,7 @@ observation + single `_ownedRepositoryClient`, the `ResolveApiKey`
estimate, the O(1) `SubscriptionRegistry` indices, and the `ReinitializeAsync`
equivalent-config gate all survive intact.
One new finding (Driver.Galaxy-019, Low, Open-deferred). No Critical / High /
One new finding (Driver.Galaxy-019, Low, Resolved 2026-06-20). No Critical / High /
Medium new findings. Category results:
| # | Category | Result |
@@ -91,12 +91,12 @@ Medium new findings. Category results:
| 1 | Correctness & logic bugs | No new issues found |
| 2 | OtOpcUa conventions | No new issues found |
| 3 | Concurrency & thread safety | No new issues found |
| 4 | Error handling & resilience | Driver.Galaxy-019 |
| 4 | Error handling & resilience | Driver.Galaxy-019 (Resolved) |
| 5 | Security | No new issues found (vendoring retired; no secret logging; `ResolveApiKey` warns on cleartext) |
| 6 | Performance & resource management | No new issues found |
| 7 | Design-document adherence | No new issues found |
| 8 | Code organization & conventions | No new issues found |
| 9 | Testing coverage | No new issues found (31 suites; `GatewayGalaxySubscriber` untestable per Driver.Galaxy-019) |
| 9 | Testing coverage | No new issues found (31 suites; `GatewayGalaxySubscriber` now has its first unit test — `GatewayGalaxySubscriberClassifyTests` for the Driver.Galaxy-019 classifier) |
| 10 | Documentation & comments | No new issues found |
## Findings
@@ -441,7 +441,7 @@ the existing csproj works correctly.
| Severity | Low |
| Category | Error handling & resilience |
| Location | `Runtime/GatewayGalaxySubscriber.cs:89-100` |
| Status | Open |
| Status | Resolved |
**Description:** `GatewayGalaxySubscriber.EnsureSessionIntervalAsync` applies
the session-level `SetBufferedUpdateInterval` command and then caches the
@@ -479,17 +479,21 @@ extract the reply→outcome classification into a pure internal helper
the sealed, internal-ctor `MxGatewaySession``GatewayGalaxySubscriber`
currently has zero unit tests for exactly this reason.
**Resolution:** Deferred 2026-06-19the fix is small but cannot be landed
with a real TDD red→green cycle in this module: the behaviour lives entirely
behind the sealed `MxGatewaySession` (no public/internal ctor; the same
constraint that forced every other gw call onto an injectable `IGalaxy*` seam),
so there is no way to drive a synthetic `MxCommandReply` through
`EnsureSessionIntervalAsync` without first refactoring out the pure-classifier
helper the recommendation calls for. That refactor plus giving the subscriber a
logger is a low-risk but non-trivial change to the production gw session path,
and the underlying intent (is caching on `MxaccessFailure` deliberate "the
gateway processed it, don't retry" behaviour, or an oversight?) is a design
question better confirmed against the gateway contract than guessed at in a
review sweep. Tracked for a follow-up that lands the classifier extraction +
its unit tests together. Impact is bounded to a sub-optimal (not broken)
publish cadence that self-heals on reconnect.
**Resolution:** Resolved 2026-06-20extracted the pure classifier
`internal static bool ClassifyIntervalReply(ProtocolStatusCode? code) => code == ProtocolStatusCode.Ok`
and routed `EnsureSessionIntervalAsync` through it, so `_lastAppliedIntervalMs`
is now set **only** on `Ok`. `MxaccessFailure` (COM-side set did not apply), any
other unexpected code, and a missing status all leave the cache untouched, so the
requested cadence is re-attempted on the next subscribe rather than permanently
pinned at the gateway default after a transient hiccup. The subscriber gained an
optional `ILogger? logger = null` ctor parameter (defaulting to `NullLogger.Instance`),
threaded through from `BuildProductionRuntimeAsync`'s `_logger` at the one
`new GatewayGalaxySubscriber(_ownedMxSession, _logger)` call site; it now emits a
`Warning` on both the `MxaccessFailure` soft-failure path and the unexpected-code
early-return path (no secret/key is logged), so the comment's promised signal exists.
Regression coverage: new `GatewayGalaxySubscriberClassifyTests`
(`tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/`) — the subscriber's first
unit test — pins `Ok → true`, `MxaccessFailure → false`, `Unspecified → false`,
`null → false`. The sealed `MxGatewaySession` ctor that previously blocked a TDD
cycle is now sidestepped because the classifier is a pure static helper testable in
isolation.