f1ef95f4da
Neither Test Run form implemented IDisposable, so navigating away mid-run left _runCts uncancelled: the sandbox kept the run alive until its own internal script timeout, and the eventual continuation touched a torn-down component's _running/_runResult fields. TemplateScriptDialog already got this right — both forms now mirror it exactly: @implements IDisposable plus a Dispose() that cancels + disposes _runCts. ScriptAnalysisService.RunInSandboxAsync is marked virtual (test-only seam, mirroring the existing virtual on DebugStreamService.StartStreamAsync/ StopStream) so a TaskCompletionSource-gated stub can hold a run open deterministically. New bUnit tests (ScriptFormSandboxDisposalTests) prove, for both forms: disposing mid-run cancels the token the sandbox observed, and the cancelled continuation resolves with no unhandled exception and no _runResult/ _running mutation. Proof of failure: reverting only the two .razor changes (keeping the virtual seam) reproduces all 4 failures — the token is never cancelled and the no-op assertions time out.