docs(deferment): register fourth round — #556/#557/#558 closed (Inspect trim + structural classification, OpcUaClient presence-shadows alias, guard consolidation); residual #559 filed

This commit is contained in:
Joseph Doherty
2026-08-10 03:16:15 -04:00
parent be15ed160a
commit 15e65c86d0
+51 -1
View File
@@ -158,7 +158,7 @@ Every gap below is in the **authoring/dispatch layer, not driver runtime code**.
| G-1 | **`DriverConfigModal.razor` has no `Calculation` case** — falls to the `default` "No typed config form" warning; no `CalculationDriverForm.razor` exists | `DriverConfigModal.razor:34-72`, default arm `:69-71` | `CalculationDriverOptions.RunTimeout` (`CalculationDriverOptions.cs:19`) is **unauthorable via the UI**. Create/rename still work, so degraded — not a hard block. **This is the same class as the Sql picker defect, one modal further downstream.** | ✅ Fixed — `CalculationDriverForm.razor` |
| G-2 | **`DeviceModal.razor` is missing Sql, MTConnect, Calculation** | `DeviceModal.razor:42-74`, default arm `:71-73` | Mitigated: Sql/MTConnect hold connection at the **driver** level, and Test Connect still works via `BuildMergedProbeConfig` (`:180-184`). | ✅ Fixed — declared **single-connection** rather than given hollow device forms |
| G-3 | **`DriverConfigModal.razor:77` tells Sql/MTConnect operators the wrong thing** — the single-connection branch is hardcoded to `Galaxy or Mqtt`, so Sql/MTConnect users are told the endpoint lives on the *device* when they just authored it on the driver | `DriverConfigModal.razor:77`, `:86-88` | Actively misleading, compounds G-2. | ✅ Fixed — `DeviceFormMap.IsSingleConnection` |
| G-4 | **No parity test guards `DriverConfigModal` or `DeviceModal`** — `RawDriverTypeDialogCoverageTests`/`…ParityTests` assert `DriverTypeNames` ↔ **picker** parity only | — | **This is exactly why G-1 and G-2 survived review.** The guard pattern exists and simply was not extended to the two downstream dispatch maps. | ✅ Fixed — `DriverFormMapParityTests` + `DriverDispatchMapParityTests` |
| G-4 | **No parity test guards `DriverConfigModal` or `DeviceModal`** — `RawDriverTypeDialogCoverageTests`/`…ParityTests` assert `DriverTypeNames` ↔ **picker** parity only | — | **This is exactly why G-1 and G-2 survived review.** The guard pattern exists and simply was not extended to the two downstream dispatch maps. | ✅ Fixed — `DriverFormMapParityTests` + `DriverDispatchMapParityTests` (since #558/`7482cf0c` the browse-commit fact lives in `RawBrowseCommitMapperParityTests`; `DriverDispatchMapParityTests` guards CsvColumnMap only) |
| G-5 | **`CsvColumnMap` has no typed columns for Sql, MQTT, MTConnect** | `CsvColumnMap.cs:340-452` | CSV import/export degrades to the raw `TagConfigJson` fallback while the 7 older drivers get typed columns. | ✅ Fixed — CSV maps for Sql/Mqtt/MTConnect |
| G-6 | **`RawBrowseCommitMapper` has no Sql branch** — falls to the generic `WriteSingleKey("address", …)` whose comment claims "Browsable drivers are all handled above" | `RawBrowseCommitMapper.cs:121-141`, fallback `:145` | Untrue since `SqlDriverBrowser` was registered (`EndpointRouteBuilderExtensions.cs:83`). Same failure mode the MTConnect branch (`:135-139`) was added to avoid: typed editor opens empty, blanks the field on save. | ✅ Fixed — **and at the browser end too**, which the audit missed |
| G-7 | **`EquipmentTagConfigInspector` covers only 6 driver types** | `:24-29` | Pre-existing (OpcUaClient + Galaxy absent too); no new driver was added. **The audit understated it:** the AdminUI's `TagConfigValidator` covers **11 of 12**, so the *deploy* gate is weaker than the *authoring* gate — and config arriving by CSV import or API only meets the deploy gate. | ✅ **CLOSED 2026-08-09** — MQTT wired + reflection guard (2026-07-28), then the four remaining drivers gained Contracts-side `Inspect` wrapping their runtime parses (merge `0695f682`). The gate now covers **11 of 12** — parity with the AdminUI validator; Galaxy is the sole unmapped type on both, symmetrically. See §9 |
@@ -1280,6 +1280,56 @@ drivers), 3 carried-open archreview findings led by 01/S-1 (`AddressSpaceApplier
---
### 2026-08-10 (fourth round) — #556 + #557 + #558 closed: Inspect trims and classifies; the last alias and the last hand-list fall
The three residuals of the #554/#555 round, three isolated-worktree Opus agents, merged `7482cf0c`
(#558), `93a05828` (#557 OpcUaClient half), `be15ed16` (#556 + #557 Mqtt half). Consolidated
verification on the merged tree: full build clean; 14 suites / 4,583 tests green (AdminUI 1460,
ControlPlane 261, Modbus 395, S7 302, AbCip 409, AbLegacy 243, TwinCAT 219, FOCAS 355, Mqtt 686,
OpcUaClient 143, OpcUaClient.Browser 10, Galaxy.Browser 21, Sql.Browser 79).
- **#556 CLOSED** — all seven Contracts factory `Inspect`s `TrimStart()` before the `[0] != '{'`
framing check, and each classifies its findings **structurally** into two lists — `rejections`
(the parse would refuse) and `advisories` (accepted, worth flagging) — with the "rejected blob
never draws zero warnings" backstop gating on `rejections.Count == 0`, not the combined count
(an advisory can no longer satisfy the backstop vacuously; no string matching anywhere). The fix
surfaced a discovery: **MQTT's runtime mappers uniquely also carry the framing gate**, so for
MQTT a leading-whitespace blob is genuinely refused at runtime and its trimmed Inspect reports a
real rejection — accept-gate-first symmetry holds. The other six runtimes tolerate the whitespace
(`JsonDocument.Parse` skips it), a cross-driver served-behavior inconsistency deliberately NOT
changed here (an Inspect-wording issue must not change served behavior) — **filed as #559**.
FOCAS's backstop is unreachable today (no unnamed refusal exists); pinned as a count relation so
it fails the day one appears, rather than deleted.
- **#557 CLOSED, both halves.** *OpcUaClient:* legacy `NodeId` was readable at runtime but the
editor dropped it — `OpcUaClientTagConfigModel` gained `NodeIdKeys` + `ReadNodeId` implementing
the runtime's **presence-shadows** semantics (`TryGetProperty("nodeId") || TryGetProperty("NodeId")`,
then one ValueKind/non-blank check) — deliberately NOT MTConnect's `FirstNonBlank`: a
present-but-blank `nodeId` short-circuits the `||`, blocks fallback, and the blob is REJECTED —
blank-beside-valid-legacy must refuse, and now does in both layers. `ToJson` drops aliases keyed
off the SAME array (#550 pattern). The `TagConfigNonEnumParityTests` OpcUaClient exemption is
removed — the anti-vacuity control (`Every_deliberately_stricter_entry_is_still_exactly_that`)
is what would have caught it going stale. *Mqtt:* the union parse verdict is the **decided
contract**, not a bug — `FromSparkplugTagConfig` hardcodes `PayloadFormat.Json` and never reads
the `payloadFormat` key, so a typo'd value on a sparkplug-shaped blob is genuinely servable and
refusing it would be stricter than runtime. Mitigation: a **mode-dependency advisory** on the
accepted blob (advisories ride accepted blobs by design, so the rejection backstop never trips).
- **#558 CLOSED** — one guard per seam: the browse-commit dispatch fact moved out of
`DriverDispatchMapParityTests` (now CsvColumnMap-only) into `RawBrowseCommitMapperParityTests`
(now 6 facts, the sole guard for that seam; subsumption proven under the same neuters before the
old fact was deleted). The hand-maintained `NonBrowsableDriverTypes` list is gone — the browsable
set derives from the real production `AddAdminUI()` registration, the non-derivable universal
arm covered by `DriverTypeNames.All` enumeration + `GenericKeyExemptions` + a
contradiction-refuting fact. **Ordinal (not IgnoreCase) membership compare is load-bearing** — a
case-only typo in a browser key is exactly the drift the guard exists to catch. Galaxy/OpcUaClient
browser `DriverType` literals became `DriverTypeNames` constants (Galaxy's "GalaxyMxGateway"
exact-matched before substitution), closing the last string-literal channel into the derived guard.
**Residual filed:** **#559** — align MQTT's runtime whitespace handling with the fleet (or declare
the framing gate the contract). The authoring-vs-deploy-gate drift arc (#549 → #550/#551 →
#552/#553 → #554/#555 → #556/#557/#558) is otherwise closed end to end.
---
### 2026-08-10 (third round) — #554 + #555 closed: authoring consults the parse; the cited guard now exists
The two residuals of the #552/#553 round, again two isolated-worktree Opus agents, merged `7cfe7b4a`