docs(config): correct OtOpcUa draft-validation description

The C# DraftValidator/DraftSnapshot has NO live caller in OtOpcUa src/ (verified
repo-wide) — it is dormant complement code. The enforced pre-publish draft
validation runs DB-side in the sp_ValidateDraft stored procedure (Status='Draft'
-> sp_PublishGeneration lifecycle). Reframe across current-state/SPEC/GAPS/README/
CLAUDE.md from 'runtime draft validation' + a false publish-pipeline caller to
'dormant managed validator; enforcement is DB-side'. Out-of-scope conclusion
for ZB.MOM.WW.Configuration is unchanged.
This commit is contained in:
Joseph Doherty
2026-06-01 10:13:29 -04:00
parent e47ecacb0d
commit fbf0f23e76
5 changed files with 48 additions and 35 deletions
+9 -7
View File
@@ -64,12 +64,14 @@ three apps already do this; they do it with three private copies of the same plu
`AuditLogOptions` (payload caps, retention bounds), and ScadaBridge's `Node` topology rules
(gRPC port ≠ remoting port, seed nodes must not target the gRPC port) all stay where they
live. Only the *plumbing they sit on* is shared; the *rules* are theirs.
- **OtOpcUa's runtime draft/snapshot validation** (`DraftValidator` + `DraftSnapshot`). This is
**not** options/config validation at all — it is managed pre-publish validation of an operator's
*configuration draft* (UNS segment regex, EquipmentId derivation, cross-cluster namespace
binding, reservation pre-flight), run in the publish pipeline against database rows, not against
`IConfiguration`. It shares only a *philosophy* (return every failure in one pass) with this
component and is **out of scope** for the shared library. It stays entirely in OtOpcUa.
- **OtOpcUa's draft/generation-content validation** (the dormant C# `DraftValidator` /
`DraftSnapshot`, plus the live DB stored procedure `sp_ValidateDraft` it was designed to
complement). This is **not** options/config validation at all — it is pre-publish validation of an
operator's *configuration draft content* (UNS segment regex, EquipmentId derivation, cross-cluster
namespace binding, reservation pre-flight) against database rows, not against `IConfiguration`;
enforcement lives DB-side in `sp_ValidateDraft` and the managed `DraftValidator` has **no live
caller** in `src/` today. It shares only a *philosophy* (return every failure in one pass) with
this component and is **out of scope** for the shared library. It stays entirely in OtOpcUa.
## 1. `IValidateOptions` base — `OptionsValidatorBase<TOptions>`
@@ -179,7 +181,7 @@ security / gRPC-port keys when the node is `Central` or `Site` respectively) wit
| Project | Current state | Primary gaps | What normalizes |
|---|---|---|---|
| **OtOpcUa** | **No options validation at all** — options bound with bare `.Bind()` (`LdapOptions`, `OpcUa`); zero `IValidateOptions` / `ValidateOnStart` in the repo. Only validator is `DraftValidator` (runtime draft/snapshot, **out of scope**). | No startup validation of `Ldap` / `OpcUa` sections — a bad value fails opaquely on first use. | *Optional* adoption: add `OptionsValidatorBase` subclasses + `AddValidatedOptions` for the sections worth guarding. `DraftValidator`/`DraftSnapshot` stay per-project untouched. Lightest consumer. |
| **OtOpcUa** | **No options validation at all** — options bound with bare `.Bind()` (`LdapOptions`, `OpcUa`); zero `IValidateOptions` / `ValidateOnStart` in the repo. The only validation-shaped type is the dormant C# `DraftValidator` (draft/generation content; real enforcement is DB-side `sp_ValidateDraft`) — **out of scope**. | No startup validation of `Ldap` / `OpcUa` sections — a bad value fails opaquely on first use. | *Optional* adoption: add `OptionsValidatorBase` subclasses + `AddValidatedOptions` for the sections worth guarding. `DraftValidator`/`DraftSnapshot` stay per-project untouched. Lightest consumer. |
| **MxGateway** | One large `GatewayOptionsValidator : IValidateOptions<GatewayOptions>` (~360 LOC, 9 sub-validators, private `AddIfBlank`/`AddIfNotPositive`/`AddIfInvalidPath` helpers); wired via `AddGatewayConfiguration` (`AddOptions().BindConfiguration().ValidateOnStart()`). | Hand-rolled accumulation + helpers duplicate the base; bespoke DI wiring duplicates `AddValidatedOptions`. | `GatewayOptionsValidator``OptionsValidatorBase<GatewayOptions>` (delete the `List<string>`/tail/helpers; keep the domain rules); `AddGatewayConfiguration``AddValidatedOptions<GatewayOptions, GatewayOptionsValidator>`. Domain rules unchanged. |
| **ScadaBridge** | **Heaviest.** Four per-module `*OptionsValidator : IValidateOptions<T>` (Cluster / Security / HealthMonitoring / AuditLog) each with their own `List<string>` accumulation, wired through bespoke `AddXxx` extensions; **plus** a raw-config pre-Akka `StartupValidator`. | Four copies of the accumulation plumbing + bespoke DI wiring; `StartupValidator` open-codes the preflight envelope. | Each `*OptionsValidator``OptionsValidatorBase<T>`; each module's `AddXxx``AddValidatedOptions`; `StartupValidator``ConfigPreflight` (byte-compatible message, §4). Domain rules unchanged. |