Merge R2-06 ServerHistorian fail-fast + 0.2.0 DataType (arch-review round 2) [PR #427]
v2-ci / build (push) Successful in 3m15s
v2-ci / unit-tests (push) Failing after 8m17s

Findings 06/S-11 (fail-fast ServerHistorianOptionsValidator, no UriFormatException
crash-loop), 06/U-7 (0.2.0 Boolean->Int1 explicit-presence + retype probe),
06/C-7 (keep-populating decision doc). T12 live VPN gate deferred. STATUS.md conflict
(R2-12 bookkeeping) resolved additively. Build clean.
This commit is contained in:
Joseph Doherty
2026-07-13 10:10:48 -04:00
18 changed files with 592 additions and 26 deletions
@@ -245,3 +245,11 @@ Every cited file:line was opened against the working tree at `f6eaa267`. **All t
| C-7 | **XS** (Task 10, ~5 min) | None | Comments/docs; deliberate keep-populating decision recorded |
**Total: ~1 hour of implementation across 3 commits, plus one operator VPN session for the live gate.** Suggested sequencing: Commit 1 (S-11) → Commit 2 (U-7) → Commit 3 (C-7) → Task 11 bookkeeping → Task 12 operator gate.
## Execution deviations (R2-06)
Executed 2026-07-13 on branch `r2/06-serverhistorian-failfast` (off master `1676c8f4`).
- **Commit granularity — per-task, not the plan's 3 grouped commits.** The plan text grouped the work into 3 commits (one per finding). The executor's operating method mandated one pathspec-scoped commit **per task** (with the task's `tasks.json` status flip in the same commit) for traceability/bisectability. Followed the per-task rule → 11 task commits (T1T11) instead of 3. No content difference; strictly finer history. Reversible (squashable at merge if 3 commits are still wanted).
- **Validator + wiring tests live in `Host.IntegrationTests` but are pure unit tests.** Per the plan (they mirror `LdapOptionsValidatorTests`, which lives there). They were verified green **filtered** (`--filter FullyQualifiedName~ServerHistorianOptionsValidatorTests`, 8/8) once during T4 — **before** a mid-run controller constraint arrived forbidding any run of `*.IntegrationTests` projects (a ~16 GB/run memory-leak in the heavy integration tests) and whole-solution `dotnet test`. After that constraint they were **not re-run**; the earlier filtered pass + a clean `dotnet build ZB.MOM.WW.OtOpcUa.slnx` (0 errors) are the standing evidence. The Historian.Gateway driver unit suite (a plain `.Tests` project) and the Runtime.Tests suite were run freely.
- **T12 (live VPN gate) → `deferred-live`.** Genuinely external infra (VPN to `wonder-sql-vd03` + a 0.2.0 gateway with `RuntimeDb:Enabled=true` and an API key carrying `historian:read`+`write`+`tags:write`). The code (T8: fixture env var + Boolean EnsureTags leg + retype probe) is complete and offline **skip-clean** (6 LiveIntegration tests, all skipped, 0 failed). The operator runs the suite on the VPN and feeds the observed retype policy back into `docs/Historian.md` + STATUS.md.
@@ -5,74 +5,96 @@
{
"id": "T1",
"subject": "RED: misconfig repro test — HistorianGatewayClientAdapter.Create with empty/malformed Endpoint must throw a named InvalidOperationException (currently UriFormatException; test MUST fail on current code)",
"status": "pending",
"status": "completed",
"blockedBy": []
},
{
"id": "T2",
"subject": "GREEN: adapter guard — Uri.TryCreate + InvalidOperationException naming ServerHistorian:Endpoint in HistorianGatewayClientAdapter.Create (defense in depth)",
"status": "pending",
"blockedBy": ["T1"]
"status": "completed",
"blockedBy": [
"T1"
]
},
{
"id": "T3",
"subject": "RED: ServerHistorianOptionsValidator unit tests (7 cases incl. the AlarmHistorian-enabled/ServerHistorian-disabled corner naming both sections)",
"status": "pending",
"blockedBy": ["T1"]
"status": "completed",
"blockedBy": [
"T1"
]
},
{
"id": "T4",
"subject": "GREEN: implement ServerHistorianOptionsValidator (OptionsValidatorBase, ctor IConfiguration, consumed = Enabled || AlarmHistorian:Enabled, fail on empty/non-absolute/non-http(s) Endpoint)",
"status": "pending",
"blockedBy": ["T3"]
"status": "completed",
"blockedBy": [
"T3"
]
},
{
"id": "T5",
"subject": "Wiring: AddValidatedOptions<ServerHistorianOptions, ServerHistorianOptionsValidator> in Program.cs hasDriver block + OptionsValidationException wiring test + residual alarm-only-mode startup warning",
"status": "pending",
"blockedBy": ["T4"]
"status": "completed",
"blockedBy": [
"T4"
]
},
{
"id": "T6",
"subject": "Warning parity: malformed-endpoint warning in ServerHistorianOptions.Validate() + Runtime.Tests case; commit 1 (S-11) after T1-T6 green",
"status": "pending",
"blockedBy": ["T4"]
"status": "completed",
"blockedBy": [
"T4"
]
},
{
"id": "T7",
"subject": "U-7 pin: GatewayTagProvisionerTests Boolean_definition_carries_explicit_Int1_presence (asserts HistorianDataType.Int1 AND HasDataType — 0.2.0 proto3-optional presence witness; pin, no RED phase)",
"status": "pending",
"status": "completed",
"blockedBy": []
},
{
"id": "T8",
"subject": "U-7 live leg: HISTGW_BOOL_SANDBOX_TAG in GatewayLiveFixture + EnsureTags_boolean_sandbox_provisions_as_Int1 + EnsureTags_type_change_outcome_is_observable retype probe (skip-clean offline; verify all-skip run)",
"status": "pending",
"blockedBy": ["T7"]
"status": "completed",
"blockedBy": [
"T7"
]
},
{
"id": "T9",
"subject": "U-7 docs: migration/assessment note (EnsureTags is upsert; pre-bump Float Booleans; three retype hypotheses; failed=N tally observability) in docs/Historian.md + CLAUDE.md HISTGW_BOOL_SANDBOX_TAG recipe update; commit 2 (U-7) after T7-T9",
"status": "pending",
"blockedBy": ["T8"]
"status": "completed",
"blockedBy": [
"T8"
]
},
{
"id": "T10",
"subject": "C-7: keep-populating decision + dead-Quality doc comments at GatewayHistorianValueWriter.cs:63, ContinuousHistorizationRecorder.GoodQuality, docs/Historian.md (cites 0.2.0 Contracts C-002); commit 3 (C-7)",
"status": "pending",
"status": "completed",
"blockedBy": []
},
{
"id": "T11",
"subject": "Bookkeeping: update archreview/plans/STATUS.md + 06-gateway-integrations.md prior-finding rows for S-11/U-7/C-7 when the work lands",
"status": "pending",
"blockedBy": ["T6", "T9", "T10"]
"status": "completed",
"blockedBy": [
"T6",
"T9",
"T10"
]
},
{
"id": "T12",
"subject": "OPERATOR GATE: run Category=LiveIntegration on the VPN against a 0.2.0 gateway (all 6 tests, incl. Boolean EnsureTags + retype probe); record the observed retype policy back into docs/Historian.md + STATUS.md; also discharges 06/U-2's full documented run",
"status": "pending",
"blockedBy": ["T9", "T11"]
"status": "deferred-live",
"blockedBy": [
"T9",
"T11"
],
"note": "needs live gateway over VPN; needs-user / serial live pass"
}
]
}
+6
View File
@@ -164,3 +164,9 @@ clean. **OPEN follow-ups:** (a) when a runner-reachable fleet + a real `tests/Se
add a fresh E2E workflow WITH a zero-match guard (fail if `Category=E2E` matches nothing); (b) S-2
option #2 — a fixture-backed integration leg (opc-plc `services:` block, `MIN_EXECUTED=1`) covering
what the unit leg cannot; (c) migrate the 3 xunit v2 holdouts once Akka ships an xunit.v3 TestKit.
### Round-2 remediation landed
| Plan | Findings | Branch | Commits | Status |
|---|---|---|---|---|
| **R2-06** | 06/S-11 (rank 7), 06/U-7 (rank 8), 06/C-7 | `r2/06-serverhistorian-failfast` (off `1676c8f4`) | `3a049a13``4640ecb1` (10 task commits) | **Code-complete, offline-verified.** S-11: fail-fast `ServerHistorianOptionsValidator` (consumer-gated on `ServerHistorian:Enabled` OR `AlarmHistorian:Enabled`, fails empty/non-http(s) Endpoint at startup) + `AddValidatedOptions` wiring + adapter defense-in-depth (`TryCreate`→named `InvalidOperationException`, no more raw `UriFormatException` crash-loop) + `Validate()` warning parity + alarm-only-mode startup warning. U-7: pinned Boolean→Int1 explicit-presence (`HasDataType`) + `HISTGW_BOOL_SANDBOX_TAG` live Boolean EnsureTags leg + retype probe (skip-clean offline) + upsert/retype migration note (`docs/Historian.md`). C-7: keep-populating decision + dead-`Quality` doc comments (0.2.0 C-002). **Live leg operator-gated (T12) — pending a VPN run on a 0.2.0 gateway** (records the observed in-place retype policy back into `docs/Historian.md`; also discharges 06/U-2's full documented run). |