Files
lmxopcua/tests
Joseph Doherty 51022c3952 fix(v3-batch4): reassert skips historian re-record + scripted-alarm redeploy recovery (reassert review M1/M2/LOW-3)
M1 (MEDIUM): the VirtualTag re-assert re-published a stale last value with a
fresh deploy-time timestamp, and VirtualTagHostActor.OnResult recorded it to
the IHistoryWriter for Historize=true plans — every deploy would append an
artificial historian sample (BadInternalError if the last state was Bad) that
never corresponded to a real evaluation. Inert today (NullHistoryWriter) but a
data-quality bug once a VT history sink binds. Fix: EvaluationResult carries an
IsReassert flag (default false), set true in VirtualTagActor.OnReassertValue;
OnResult still PUBLISHES the AttributeValueUpdate (to repair the reset node) but
SKIPS _history.Record when IsReassert. Regression test
VirtualTagHostActorTests.Reassert_of_a_historized_vtag_publishes_but_does_not_re_record_to_the_historian
(fails before — count=2 — passes after: count stays 1).

LOW-3: corrected the ordering comments in VirtualTagActor.OnReassertValue and
VirtualTagHostActor.OnApply. ApplyVirtualTags goes to the VirtualTag HOST, not
the publish actor; the ordering holds because the re-assert reaches the publish
actor via a multi-hop chain (host -> child ReassertValue -> child -> parent
EvaluationResult -> OnResult -> publish actor) and thus lands AFTER
RebuildAddressSpace in the shared publish actor's FIFO mailbox.

M2 (CONFIRMED REAL — reported as scoped follow-up, not fixed here): the same
redeploy-reset race is latent for Part 9 scripted-alarm condition nodes. A
full-rebuild deploy clears + re-materialises them fresh
(OtOpcUaNodeManager.RebuildAddressSpace clears _alarmConditions @2160;
MaterialiseAlarmCondition recreates normal state), but the engine reload does
NOT re-emit an unchanged-active condition: ScriptedAlarmEngine.LoadAsync ->
EvaluatePredicateToStateAsync (@546-552) computes ApplyPredicate(seed, true)
where seed is the persisted state from the DB-backed EfAlarmConditionStateStore,
yielding EmissionKind.None, which ScriptedAlarmHostActor.OnEngineEmission (@292)
filters. Net: an active alarm with static dependencies under-reports until its
next real transition on a full-rebuild deploy. The fix is larger/riskier than
the VT case (touches the Core engine emission contract and must publish ONLY the
OPC UA node state, NOT the alerts topic, to avoid duplicate AVEVA history rows —
the alarm analogue of M1), so per review guidance it is deferred as a scoped
follow-up rather than forced into this commit.

Claude-Session: https://claude.ai/code/session_01LVneM3eh1UtJxEisFXgmox
2026-07-16 14:33:05 -04:00
..