fix(scripting): quarantine a refusal behind a stuck orphan (fix round 2)
Ruling refinement on I1. Only timeouts are strikes, so a never-returning while(true), which times out once and is then only refused, was never quarantined, and that defeated E1. A refusal now quarantines immediately when the orphan it was refused behind has been executing for longer than ScriptingOptions.StuckOrphanQuarantineFactor x the script's timeout (default 10, so 20 s at the 2 s default; 0 or less disables it). It uses the same quarantine path: one Error, otopcua.script.quarantined with reason=stuck, Bad, lifted on source change or redeploy. A young orphan (a short overrun) is still a plain refusal. The guard stamps the run's start (Stopwatch timestamp) when the pool picks it up and clears it when the run really finishes. Tests: the 30-trigger while(true) regression (paced) now ends quarantined with reason "stuck", still with exactly one orphan and a healthy sibling. Positive control: a 2x overrun is refused, not quarantined, and evaluates again once the orphan completes. A factor of 0 disables the rule. Docs (ScriptEditor "Runtime safety", VirtualTags) and the seam remarks are updated. Refs #734 Claude-Session: https://claude.ai/code/session_01Ub9hGmYLQvn9XETzw7CMsT
This commit is contained in:
@@ -25,6 +25,9 @@ public sealed class ScriptingOptions
|
||||
/// <summary>Default <see cref="QuarantineAfterConsecutiveTimeouts"/>: 3.</summary>
|
||||
public const int DefaultQuarantineAfterConsecutiveTimeouts = 3;
|
||||
|
||||
/// <summary>Default <see cref="StuckOrphanQuarantineFactor"/>: 10.</summary>
|
||||
public const double DefaultStuckOrphanQuarantineFactor = 10;
|
||||
|
||||
/// <summary>
|
||||
/// Managed bytes one script run may allocate on its own thread (sampled with
|
||||
/// <see cref="GC.GetAllocatedBytesForCurrentThread"/> around the run) before the script is
|
||||
@@ -40,4 +43,14 @@ public sealed class ScriptingOptions
|
||||
/// throw) resets it. Default 3; values below 1 are treated as 1.
|
||||
/// </summary>
|
||||
public int QuarantineAfterConsecutiveTimeouts { get; set; } = DefaultQuarantineAfterConsecutiveTimeouts;
|
||||
|
||||
/// <summary>
|
||||
/// A refusal quarantines the script immediately when the abandoned run it was refused behind has been
|
||||
/// executing for longer than this many times the script's timeout (20 s at the 2 s default). This is
|
||||
/// what quarantines a permanently stuck script (<c>while(true)</c>): it times out once and is then
|
||||
/// only ever refused, so the timeout streak alone would never reach
|
||||
/// <see cref="QuarantineAfterConsecutiveTimeouts"/>. A young orphan (a short overrun) is still just a
|
||||
/// refusal. Zero or negative disables the rule. Default 10.
|
||||
/// </summary>
|
||||
public double StuckOrphanQuarantineFactor { get; set; } = DefaultStuckOrphanQuarantineFactor;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user