fix(site-runtime): seed republished overrides into Script Actors without firing scripts; alarms still evaluate (review 2026-10-21 01/SR-U1, decision M1)
HandleOverridesLoaded now sends children a site-local AttributeValueSeed (stream still gets the AttributeValueChanged). ScriptActor updates its snapshot, Conditional truth value and Expression last result (seed-only eval, never fires); AlarmActor evaluates it as a normal change.
This commit is contained in:
@@ -1831,24 +1831,32 @@ public class InstanceActor : ReceiveActor
|
||||
/// otherwise an "any-change" waiter (or a waiter whose target equals the unchanged
|
||||
/// value) would spuriously fire even though nothing actually changed.
|
||||
/// </param>
|
||||
private void PublishAndNotifyChildren(AttributeValueChanged changed, bool evaluateWaiters = true)
|
||||
/// <param name="seedOnly">
|
||||
/// Review 2026-10-21 01/SR-U1: children receive an <see cref="AttributeValueSeed"/> instead of
|
||||
/// the change — Script Actors adopt the value without launching a run, Alarm Actors evaluate
|
||||
/// it normally. The site stream still gets the ordinary change.
|
||||
/// </param>
|
||||
private void PublishAndNotifyChildren(
|
||||
AttributeValueChanged changed, bool evaluateWaiters = true, bool seedOnly = false)
|
||||
{
|
||||
// Publish to site-wide stream
|
||||
_streamManager?.PublishAttributeValueChanged(changed);
|
||||
|
||||
object toChildren = seedOnly ? new AttributeValueSeed(changed) : changed;
|
||||
|
||||
// Route the change to exactly the children interested in it (P2): the all-changes
|
||||
// bucket (Expression scripts/alarms + fail-open children) plus the children monitoring
|
||||
// this specific attribute. Children still gate on their own trigger config — routing
|
||||
// is a fan-out optimization, not the correctness check.
|
||||
foreach (var child in _allChangeChildren)
|
||||
{
|
||||
child.Tell(changed);
|
||||
child.Tell(toChildren);
|
||||
}
|
||||
if (_childrenByMonitoredAttribute.TryGetValue(changed.AttributeName, out var interested))
|
||||
{
|
||||
foreach (var child in interested)
|
||||
{
|
||||
child.Tell(changed);
|
||||
child.Tell(toChildren);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1942,9 +1950,10 @@ public class InstanceActor : ReceiveActor
|
||||
/// evaluated against the default for the actor's whole lifetime while <c>GetAttribute</c>
|
||||
/// answered with the override. Routing each changed value through
|
||||
/// <see cref="PublishAndNotifyChildren"/> re-seeds every child snapshot and the site stream in
|
||||
/// one place. Consequence, deliberately accepted: a ValueChange/Conditional script keyed on an
|
||||
/// overridden static attribute fires once as the actor starts — exactly as one keyed on a
|
||||
/// data-sourced attribute already fires on that attribute's first DCL value.
|
||||
/// one place. The children get it as an <see cref="AttributeValueSeed"/> (user decision M1):
|
||||
/// Script Actors adopt the value and their trigger state WITHOUT running — a restart must not
|
||||
/// re-fire ValueChange/Conditional/Expression scripts — while Alarm Actors evaluate it, so an
|
||||
/// alarm whose condition already holds raises.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Waiters are NOT evaluated: none can be waiting on a value the instance already held, and a
|
||||
@@ -2035,7 +2044,8 @@ public class InstanceActor : ReceiveActor
|
||||
new AttributeValueChanged(
|
||||
_instanceUniqueName, name, name, _attributes[name],
|
||||
_attributeQualities.GetValueOrDefault(name) ?? "Good", DateTimeOffset.UtcNow),
|
||||
evaluateWaiters: false);
|
||||
evaluateWaiters: false,
|
||||
seedOnly: true);
|
||||
}
|
||||
|
||||
_logger.LogDebug(
|
||||
|
||||
Reference in New Issue
Block a user