fix(virtual-tags): resolve High code-review finding (Core.VirtualTags-001)
OnScriptSetVirtualTag updated the value cache, notified observers, and recorded history for the written path but never scheduled a cascade for tags depending on that path. This contradicts docs/VirtualTags.md, which states ctx.SetVirtualTag writes "still participate in change-trigger cascades": a change-triggered virtual tag reading a script-written tag went stale until an unrelated trigger fired. OnScriptSetVirtualTag now launches a fire-and-forget CascadeAsync for the written path, mirroring OnUpstreamChange. The cascade is scheduled rather than invoked inline because the callback runs inside EvaluateInternalAsync while the non-reentrant _evalGate semaphore is held. Added regression test SetVirtualTag_within_script_cascades_to_dependents_of_the_written_tag. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -315,6 +315,14 @@ public sealed class VirtualTagEngine : IDisposable
|
||||
_valueCache[path] = snap;
|
||||
NotifyObservers(path, snap);
|
||||
if (_tags[path].Definition.Historize) _history.Record(path, snap);
|
||||
|
||||
// A cross-tag write must participate in the change-trigger cascade, exactly
|
||||
// like an upstream delta — any change-triggered tag that reads this path
|
||||
// would otherwise go stale until an unrelated trigger fires (see
|
||||
// docs/VirtualTags.md, VirtualTagContext section). Fire-and-forget: this
|
||||
// callback runs inside EvaluateInternalAsync with the non-reentrant
|
||||
// _evalGate held, so the cascade must be scheduled, not invoked inline.
|
||||
_ = CascadeAsync(path, CancellationToken.None);
|
||||
}
|
||||
|
||||
private void NotifyObservers(string path, DataValueSnapshot value)
|
||||
|
||||
Reference in New Issue
Block a user