diff --git a/deferment.md b/deferment.md index 1a66d3cf..f27bff53 100644 --- a/deferment.md +++ b/deferment.md @@ -55,7 +55,7 @@ So everything below is **untracked work**. Nothing in it is a data-loss or outag |---|---|---| | **4 live gates blocked on hardware/licence** — ~~archreview R2-03 (VT Good→Bad on a data-fed rig)~~ ✅ *PASSED 2026-08-07*, ~~R2-10 (force breaker-OPEN)~~ ✅ *PASSED 2026-08-08 by fixing the structural gap (`DriverCapability.Initialize`)*, Galaxy `ScanStateProbeParityTests` (licence-gated), v2 GA exit criteria (~~FOCAS live-CNC smoke #54~~ *closed — live-validated on a real 31i-B 2026-06-25, see `docs/plans/2026-06-25-focas-pdu-v3-30i-b-support.md`*, OPC UA CTT pass, Ignition 8.3 cutover) | Cannot be closed from this box. Not decisions — inputs. *(The two archreview gates turned out to be closable from this box after all — §5.)* | §5 | | ~~**1 deferred live-verify task** — `2026-06-14-driver-reconfigure-while-faulted` Task 3~~ ✅ **PASSED 2026-08-07.** A faulted driver ("initialize failed: Connection refused") adopted a corrected config and connected while faulted, with the node's `startedAt` byte-identical before and after and `RestartCount=0` — no restart. | §5 | -| ~~**G-7 residual** — Sql / MTConnect / Calculation / OpcUaClient have no Contracts-side `Inspect`~~ ✅ **CLOSED 2026-08-09** (`2947a2b9` + `1469fbb4`, merge `0695f682`). All four now ship a Contracts-side `*TagDefinitionFactory.Inspect` **wrapping the runtime parse, never mirroring the AdminUI model** — Sql wraps the pre-existing `SqlEquipmentTagParser`; the MTConnect / OpcUaClient / Calculation blob readers were **moved** into their Contracts assemblies (the drivers now call them), so never-stricter-than-runtime is structural. `Driver.Calculation.Contracts` was created (no Contracts assembly existed). The reflection coverage guard was falsified (map entry removed → red naming `SqlTagDefinitionFactory`), and `TagConfigAuthoringDeployParityTests` pins authoring-accepts ⇒ deploy-accepts. **Galaxy is now the sole unmapped type, deliberately + symmetrically** (no typed authoring editor either). Found en route, left open: `MTConnectTagConfigModel` reads `dataType` leniently (misspelled name → silently `String`) while the runtime rejects it — authoring weaker than runtime, the *opposite* direction from G-7; documented inline in the parity tests. | ~~The deploy gate is weaker than the authoring gate.~~ Config arriving by CSV import or API now meets the same checks the modal applies. | §1.2 | +| ~~**G-7 residual** — Sql / MTConnect / Calculation / OpcUaClient have no Contracts-side `Inspect`~~ ✅ **CLOSED 2026-08-09** (`2947a2b9` + `1469fbb4`, merge `0695f682`). All four now ship a Contracts-side `*TagDefinitionFactory.Inspect` **wrapping the runtime parse, never mirroring the AdminUI model** — Sql wraps the pre-existing `SqlEquipmentTagParser`; the MTConnect / OpcUaClient / Calculation blob readers were **moved** into their Contracts assemblies (the drivers now call them), so never-stricter-than-runtime is structural. `Driver.Calculation.Contracts` was created (no Contracts assembly existed). The reflection coverage guard was falsified (map entry removed → red naming `SqlTagDefinitionFactory`), and `TagConfigAuthoringDeployParityTests` pins authoring-accepts ⇒ deploy-accepts. **Galaxy is now the sole unmapped type, deliberately + symmetrically** (no typed authoring editor either). ~~Found en route, left open: `MTConnectTagConfigModel` reads `dataType` leniently (misspelled name → silently `String`) while the runtime rejects it — authoring weaker than runtime, the *opposite* direction from G-7; documented inline in the parity tests.~~ ✅ Filed and FIXED same day as **#549** — and it was 6 models wide, not 1 (see the "2026-08-09 (later)" §9 entry); residuals are **#550**/**#551**. | ~~The deploy gate is weaker than the authoring gate.~~ Config arriving by CSV import or API now meets the same checks the modal applies. | §1.2 | | ~~**3 carried-open archreview findings** — the failure-visibility trio~~ **Update 2026-08-07 (truth sweep): mostly CLOSED.** 01/S-1 **FIXED** — `AddressSpaceApplier.cs:95-97` tallies swallowed removal-pass failures into `AddressSpaceApplyOutcome.FailedNodes` (comment cites 01/S-1). 03/S4 **FIXED** — `PrimaryGatePolicy.cs` default-denies on unknown role when a driver peer exists (doc-comment "Fixes archreview 03/S4"), live-proven R2-04 T15. 06/S-1 **PARTIAL** — fail-closed advise path + `galaxy.writes.unconfirmed` meter shipped in `GatewayGalaxyDataWriter.cs`; residual = gateway-side WriteComplete correlation (cross-repo, mxaccessgw). **Update 2026-08-09: the gateway half SHIPPED** — mxaccessgw main `b948e69` correlates the worker's `OnWriteComplete` onto the unary `WriteSecured` reply (`statuses[0]`; bounded wait default **1.5 s** — deliberately under OtOpcUa's 2 s Tier A Write budget, configurable via `MxGateway:Worker:WriteCompletionWaitMilliseconds`; timeout falls back to today's empty-statuses shape, still metered unconfirmed). **Update 2026-08-09 (later): 06/S-1 CLOSED — live-gated end to end, and the gate found + fixed three defects.** The user-authorized redeploy exposed (1) the correlation covered `WriteSecured` only while OtOpcUa's dominant no-login path emits plain `Write` — fixed gateway-side (worker `53f69cd`, all four unary write kinds, redeployed both hosts); (2) OtOpcUa's `StatusCodeMap.FromMxStatus` truncated the correlated failure row's MxStatus detail code to a byte (`1007 & 0xFF = 239` → OPC DA Good band), mapping a REFUSED write to Good — fixed with the not-IsSuccess-never-Good invariant + category routing (`SECURITY_ERROR → BadUserAccessDenied`); (3) the writer's borrow-from-subscription-registry optimization could never commit (a subscription handle is plain-advised; MXAccess refuses the no-login write `SECURITY_ERROR 1008` AND the advise upgrade `E_INVALIDARG` — one advise per HANDLE, not per attribute) — borrow path REMOVED, writer always owns a supervisory-advised handle. Live legs all PASSED on windev: happy write Good off confirmed `statuses[0]` + both meters flat; read-only refusal surfaces Bad-family + node-revert input; `WriteCompletionWaitMilliseconds=0` control → provisional Good + metered unconfirmed (override removed after). Pinned by `Live_correlated_write_statuses_confirm_commit_and_surface_refusal`, `Live_write_to_subscribed_tag_commits_via_own_handle`, `GatewayGalaxyWriteMeterLiveTests`, and the new `StatusCodeMapTests` rows. | ~~Residual is only the 06/S-1 live leg~~ **CLOSED 2026-08-09** — nothing remains. | §6.3 | | ~~**1 genuinely unexecuted plan** — `2026-05-29-adminui-followups`, **19 pending / 0 completed**~~ ✅ **CLOSED 2026-08-07 by the overlap audit it asked for — it was bookkeeping drift, not backlog.** All 19 tasks are shipped, superseded by v3, or moot; **no code was needed.** The warning was right and then some: WS1's whole *premise* died with v3 (driver-embedded tag lists in `DriverConfig` are no longer the runtime source of truth — the artifact populates `RawTags`), and its capability was delivered in the v3 shape (`TagConfigEditorMap` + `DeviceFormMap`, both verified **reachable**, not merely present). WS2/WS3 shipped; WS4's target files were deleted by v3 Batch 2 + #542. Per-task evidence in the `.tasks.json`. | §6.2 | | **11 other open plan tasks** — ~~5 akka-hosting partials (likely v3-superseded)~~ *(verified superseded 2026-08-07 — F8/F9/F10/F13/F14 marked completed; `Phase7Composer`/`ScriptedAlarmActor` deleted, evaluators/SDK sinks/security policies all production-wired)*, 4+1 followups-batch (open *by choice*), fixedtree Task 11, 1 manual smoke checklist | Small, and two of the groups are decisions already taken. | §6.2 | @@ -1280,6 +1280,41 @@ drivers), 3 carried-open archreview findings led by 01/S-1 (`AddressSpaceApplier --- +### 2026-08-09 (later) — #549 authoring strictness (6 models); NamespaceMap out of Contracts + +Follow-ups from the round below, again two isolated-worktree agents, merged `3a70a4e6` + `10c7a0e8`. +Consolidated verification on the merged tree: full build clean; 12 suites / 4,119 tests green +(AdminUI 1175, ControlPlane 261, Core.Abstractions 284, MTConnect 534, AbLegacy 219, TwinCAT 192, +FOCAS 327, OpcUaClient 143 + Browser 10, Modbus 346, S7 264, AbCip 364). + +- **#549 CLOSED — and the survey it demanded found the defect was 6 models wide, not 1.** The G-7 + round's "MTConnect authoring accepts a dataType the runtime rejects" generalised: **R2-11 Phase C + made every runtime TagConfig enum read strict** (`TagConfigJson.TryReadEnumStrict` rejects the + tag), so every lenient `GetEnum` authoring model was authoring-weaker-than-runtime — MTConnect, + Modbus (region/dataType/byteOrder), S7, AbCip, AbLegacy, TwinCAT, FOCAS. Mqtt and Sql were already + strict on both sides. Fixed one-authority: MTConnect's `Validate` consults the factory's exposed + `IsAcceptedDataTypeName`; the other five consult `TagConfigJson.DescribeInvalidEnum`, which + delegates to the runtime's own `ClassifyEnumText` (verdict AND wording from one implementation). + Deliberately not stricter than runtime: absent keys / non-string tokens stay accepted where the + runtime accepts them, each pinned. MTConnect also captures the raw JSON token — an unquoted + `"dataType": 8` throws in the runtime DTO deserialise, so authoring now rejects it too. +- **Survey residuals filed, not folded in:** **#550** (MTConnect `driverDataType` is a second, + runtime-preferred spelling the editor neither reads nor writes — a valid one silently overrides + what the editor displays; fixing needs both read-both-spellings AND normalise-on-save together) and + **#551** (4 gate-coverage gaps: `RawCsvImportModal`'s free-form TagConfigJson column meets only the + deploy gate; S7's unique absent-`dataType` hard-requirement warns nowhere; AbCip member `dataType` + + Modbus `stringByteOrder` checked by neither gate; the seven pre-#549 `Inspect` doc summaries + still claim "silently defaulted by the lenient runtime" — stale since R2-11). +- **NamespaceMap moved out of `OpcUaClient.Contracts`** into a new `Driver.OpcUaClient.Session` leaf + (merge `10c7a0e8`), so ControlPlane's G-7 reference no longer drags the OPC UA client SDK into its + dependency graph (before: five `OPCFoundation.*` packages transitively; after: none). The + alternative — Browser referencing the driver, the Mqtt #512 shape — lost on a measurement: AdminUI + references `.Browser`, so it would have pulled the driver runtime into the AdminUI graph, exactly + what #512 spent two extractions removing. The G-7 one-authority seam survives (`BuildRawPathTable` + still calls `OpcUaClientTagDefinitionFactory.TryReadNodeId`, now `.Session → .Contracts`). + `ContractsIsDependencyLightTests` pins it from both directions (Contracts manifest names no + `Opc.Ua*`; `.Session`'s must — so "none anywhere" cannot read as a pass), falsified red before trust. + ### 2026-08-09 — G-7 closed; RawBrowseModal race ported The two items §0.1 flagged as "highest-leverage runnable from here", executed in parallel by two