Files
lmxopcua/archreview/plans/R2-03-vt-failure-quality-plan.md.tasks.json
T
Joseph Doherty 1e02661598 chore(gates): close adminui-followups by audit; R2-03 T11/T12 + reconfigure Task 3 PASSED, R2-10 re-diagnosed
The two items deferment.md §0.1 named as the remaining untracked engineering.

adminui-followups (19 pending / 0 completed) — CLOSED by the overlap audit the plan
itself asks for first, with no code written. All 19 tasks are shipped, superseded by
v3, or moot. WS1's premise had also expired: it assumed driver-embedded tag lists in
DriverConfig are the runtime source of truth, which v3 ended (the artifact populates
RawTags). Its capability shipped in the v3 shape — TagConfigEditorMap + DeviceFormMap,
both verified reachable from RawTagModal/DeviceModal rather than merely present.

Live gates on docker-dev (image rebuilt: the rig was running a build 3 h older than the
last code commit; parity asserted per container plus a positive check that the symbols
under test are in the shipped DLLs):

- R2-03 T11 PASSED — build 0 errors, Runtime.Tests 555/555, RoslynVirtualTagEvaluator 20/20.
- R2-03 T12 PASSED — VT 42 -> 0x80020000 -> 42, staged WITHOUT a deployment (a Modbus
  register flipped on the fixture) so the VT child survived; a deploy-driven trip creates
  a fresh child with no last value, which is correct-by-design and masked the result twice.
  P7 confirmed: two byte-identical redeploys, zero disturbance.
- driver-reconfigure Task 3 PASSED — a faulted driver adopted a corrected config and
  connected with the node's startedAt unchanged and RestartCount=0.
- R2-10 T10 still OPEN, but the recorded cause was wrong. Not ConnectionBackoff
  throttling (the breaker opens on 2 failures / 30 s): the dead-endpoint failure mode
  never enters the resilience pipeline. IDriver.InitializeAsync is not invoker-wrapped
  (DriverInstanceActor.cs:707) and ModbusDriver swallows per-tag failures in BOTH
  directions, so even the plan's fallback trigger cannot work. Live-proven: a driver
  timing out every read against a blackhole reported ConsecutiveFailures=0 while the
  panel read "All targets healthy."

Finding from T12: the Bad publish's documented "carrying the last-known value" is
unobservable by any client — subscription and a raw Read service call both return NULL.
The actor is correct; every existing failure assertion checked StatusCode alone, so the
value half was asserted nowhere. Adds
VirtualTagActorTests.The_Bad_result_carries_the_last_known_value, which proves the actor
emits it. The documentation is what overstates.
2026-08-07 06:28:22 -04:00

106 lines
5.7 KiB
JSON

{
"planPath": "archreview/plans/R2-03-vt-failure-quality-plan.md",
"tasks": [
{
"id": "R2-03-T1",
"subject": "S13 stale-Good repro test at host level (RED \u2014 must fail on f6eaa267): fail-after-success evaluator, publish probe sees Good then expects Bad AttributeValueUpdate",
"status": "completed",
"blockedBy": []
},
{
"id": "R2-03-T2",
"subject": "Add OpcUaQuality Quality (default Good) to VirtualTagActor.EvaluationResult; bridge passes result.Quality into AttributeValueUpdate; historize maps Bad to 0x80020000u BadInternalError (RED bridge tests then GREEN)",
"status": "completed",
"blockedBy": [
"R2-03-T1"
]
},
{
"id": "R2-03-T3",
"subject": "VirtualTagActor failure/throw publishes Bad EvaluationResult once per transition, gated on all dependencyRefs arrived; recovery bypasses value-dedup; update Evaluator_failure_publishes_ScriptLogEntry_warning expectation (turns T1 green)",
"status": "completed",
"blockedBy": [
"R2-03-T2"
]
},
{
"id": "R2-03-T4",
"subject": "S13 transition-semantics unit guards: second consecutive failure publishes nothing; same-value recovery republishes Good; evaluator-threw path also degrades",
"status": "completed",
"blockedBy": [
"R2-03-T3"
]
},
{
"id": "R2-03-T5",
"subject": "S13 cold-start suppression guards: missing-dep failure publishes no Bad; failure after all deps arrived publishes Bad (inputs-ready gate)",
"status": "completed",
"blockedBy": [
"R2-03-T3"
]
},
{
"id": "R2-03-T6",
"subject": "S12 sequential pin: Evaluate_after_ClearCompiledScripts_recompiles_and_succeeds (green baseline the T8 retry-loop restructure must not regress)",
"status": "completed",
"blockedBy": []
},
{
"id": "R2-03-T7",
"subject": "S12 concurrent stress repro (RED): 300 compiled evaluations racing ClearCompiledScripts, WaitAsync-bounded, assert zero disposed-reason failures",
"status": "completed",
"blockedBy": []
},
{
"id": "R2-03-T8",
"subject": "S12 fix: retry-once on ObjectDisposedException in RoslynVirtualTagEvaluator.Evaluate (re-fetch via GetOrCompile; no retry when _disposed or second hit); update xmldoc (turns T7 green)",
"status": "completed",
"blockedBy": [
"R2-03-T6",
"R2-03-T7"
]
},
{
"id": "R2-03-T9",
"subject": "P7 wiring-guard rewrite (RED): ApplyVirtualTags clears the compile cache only when the expression set changes \u2014 identical redeploy must NOT clear; expression edit must; shared-expression removal must not",
"status": "completed",
"blockedBy": [
"R2-03-T3"
]
},
{
"id": "R2-03-T10",
"subject": "P7 fix: gate the OnApply ClearCompiledScripts on HashSet.SetEquals of desired vs _planByVtag expression sets (ordinal), with explanatory comment (turns T9 green)",
"status": "completed",
"blockedBy": [
"R2-03-T9"
]
},
{
"id": "R2-03-T11",
"subject": "Full-suite sweep (Runtime.Tests whole project + RoslynVirtualTagEvaluatorTests + solution build) and STATUS.md / tasks.json bookkeeping",
"status": "completed",
"blockedBy": [
"R2-03-T4",
"R2-03-T5",
"R2-03-T8",
"R2-03-T10"
],
"note": "integration/full sweep; serialized heavy pass (build clean, Runtime.Tests 372/372, RoslynVirtualTagEvaluatorTests 20/20 verified locally; whole-solution/cross-suite sweep deferred per controller memory constraint)",
"openNote": "STILL OPEN \u2014 live-blocked on a data-less rig: every materialised node reads Bad_WaitingForInitialData because no driver data flows, so a VT cannot be staged Good->Bad. Needs a reachable driver fixture feeding the VT. Unit-verified meanwhile.",
"closureNote": "PASSED 2026-08-07. Solution build 0 errors; Runtime.Tests 555/555 (3 deliberate EquipmentDeviceBindingRetired skips); RoslynVirtualTagEvaluatorTests 20/20. Note the suite lives in Host.IntegrationTests, which a solution-wide --filter does not reach. Evidence: docs/plans/2026-08-07-adminui-followups-and-live-gates.md \u00a72."
},
{
"id": "R2-03-T12",
"subject": "Live-/run gate on docker-dev (rebuild BOTH centrals): author working VT, break it, Client.CLI read shows StatusCode Bad with last value, fix and read Good; P7 no-recompile spot-check on identical redeploy",
"status": "completed",
"blockedBy": [
"R2-03-T11"
],
"note": "docker-dev live-/run; serial live pass",
"openNote": "STILL OPEN \u2014 same blocker as T11. Confirmed still open 2026-07-27 (deferment.md \u00a78.5).",
"closureNote": "PASSED 2026-08-07 on docker-dev (both centrals rebuilt; image parity asserted per container). Good(42) -> Bad(0x80020000 BadInternalError) -> Good(42), staged WITHOUT a deployment by flipping a Modbus holding register on the fixture, so the VT child survived the transition (a deploy-driven trip creates a fresh child with no last value \u2014 correct per the inputs-ready gate). P7: two byte-identical redeploys (identical revisionHash) caused zero disturbance, Ever-went-BAD=0. FINDING: the Bad publish does NOT carry the last-known value to the client (subscription AND raw Read both return NULL), contradicting VirtualTagActor.cs:22 and this plan STATUS row. The actor is correct \u2014 new test VirtualTagActorTests.The_Bad_result_carries_the_last_known_value proves it emits 42; the value is lost in the OPC UA layer. No existing test asserted that half. Evidence: docs/plans/2026-08-07-adminui-followups-and-live-gates.md \u00a73."
}
],
"lastUpdated": "2026-08-07"
}