Review remediation: Lane E — Scripting runtime safety + scripted alarms (fable for E1/E4, opus for E2/E3; review fable) #734
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
From the 2026-10-21 Fable system review. Findings
R0x:IDrefer todocs/reviews/2026-10-21/0x-*.md(location, evidence, failure scenario, fix recipe). Plan:docs/superpowers/plans/2026-10-22-review-remediation.md.Lane E — Scripting runtime safety + scripted alarms (fable for E1/E4, opus for E2/E3; review fable)
Owns:
src/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting/**,Core.ScriptedAlarms/**,Core.AlarmHistorian/**Runtime/ScriptedAlarms/**,Runtime/VirtualTags/**Host/Engines/**src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Calculation/**Configuration/Validation/DraftValidator.csTests:
Core.Scripting.Tests,Core.ScriptedAlarms.Tests,Core.AlarmHistorian.Tests,Driver.Calculation.Tests,Runtime.Tests(VirtualTags/ScriptedAlarms folders)R03:1.1,R05:2,R07:L1,R07:P5):ScriptRunGuardin Core.Scripting, keyed per script instance."previous evaluation still running"plus a counter.otopcua_script_quarantined, cleared on redeploy or source change.CancellationTokeninto scripts.RoslynVirtualTagEvaluator/VirtualTagHostActor, andCalculationEvaluator.while(true)script on a 1 Hz trigger leaves ≤ 1 orphaned thread after 30 triggers (ThreadPool.ThreadCountdelta). Positive control: a healthy sibling keeps evaluating.R05:10,R05:11):System.Threadingwith an allow-list (CancellationToken,Interlocked,Volatile).System.Type.GetType/GetMethod*/InvokeMemberandSystem.Runtime.CompilerServices.RuntimeHelpers.GC.GetAllocatedBytesForCurrentThreadaround each run and quarantine on breach (Scripting:MaxAllocBytesPerRun, default 64 MB). Document that a pre-emptive OOM still needs an out-of-process runner, and file a follow-up issue for that.OTSCRIPTdiagnostic, and the Monaco editor parity test still matches.R05:1,3,6,7,9,12):_valueCacheinLoadAsync; move its write below the_disposedguard.Disposedrain (5 s, cancelable token).ScriptedAlarmSourcecatch logs at Warning.LocalDbStoreAndForwardSinkcached dead-letter counter, resynced with the queue counter.R02:H1,H3,P6,L3;R07:P4,H6,H8,U5):ScriptedAlarmHostActorreceives aFunc<(upstream, engine)>factory, building its engine per incarnation.OnAlarmCommand.DependencyMuxTagUpstreamSource.RetainOnly(loaded deps).VirtualTagActorper-tag failure-log rate limit.(value,status,ts)and replays it onRegisterInterest, with inputs recorded regardless of_running;ClearCompiledScriptsfromShutdownAsync;CalculationNoEffectiveTrigger.DependencyValueBatchhandler, then a laterApplyScriptedAlarmsloads cleanly.Lane gate: suites green. Wave 4 docker-dev gate: author a
while(true)virtual tag. The node stays responsive, the tag shows quarantined, and the thread count stays flat over 2 min.