diff --git a/src/ZB.MOM.WW.ScadaBridge.SiteRuntime/Scripts/ScriptCompilationService.cs b/src/ZB.MOM.WW.ScadaBridge.SiteRuntime/Scripts/ScriptCompilationService.cs
index af159e77..1d66655c 100644
--- a/src/ZB.MOM.WW.ScadaBridge.SiteRuntime/Scripts/ScriptCompilationService.cs
+++ b/src/ZB.MOM.WW.ScadaBridge.SiteRuntime/Scripts/ScriptCompilationService.cs
@@ -89,9 +89,21 @@ public class ScriptCompilationService
/// ScriptAnalysisService.DefaultOptions and
/// ScriptTrustPolicy.DefaultReferences already follow this pattern.
///
+ ///
+ ///
+ /// Static options alone are NOT enough: every script.Compile() binds the
+ /// transitive closure of these references, and Roslyn's default resolver
+ /// re-resolves that closure through MetadataReference.CreateFromFile on
+ /// EVERY compile (~74–105 fresh native metadata copies per compiled script,
+ /// confirmed live post-5a781c70 on 2026-08-12: 21 scripts held 6,640
+ /// AssemblyMetadata/PEReader/MetadataImageReference objects). The shared
+ /// memoizes those resolutions
+ /// process-wide; see its doc for the full mechanism.
+ ///
///
private static readonly ScriptOptions SharedScriptOptions = ScriptOptions.Default
.WithReferences(ScriptAssemblies)
+ .WithMetadataResolver(CachingScriptMetadataResolver.Instance)
.WithImports(
"System",
"System.Collections.Generic",
diff --git a/tests/ZB.MOM.WW.ScadaBridge.SiteRuntime.Tests/Scripts/ScriptCompilationServiceTests.cs b/tests/ZB.MOM.WW.ScadaBridge.SiteRuntime.Tests/Scripts/ScriptCompilationServiceTests.cs
index 782b03d4..5b8dff9d 100644
--- a/tests/ZB.MOM.WW.ScadaBridge.SiteRuntime.Tests/Scripts/ScriptCompilationServiceTests.cs
+++ b/tests/ZB.MOM.WW.ScadaBridge.SiteRuntime.Tests/Scripts/ScriptCompilationServiceTests.cs
@@ -251,4 +251,19 @@ public class ScriptCompilationServiceTests
// The options object itself is cached, so it must not be rebuilt either.
Assert.Same(first.CompiledScript.Options, second.CompiledScript.Options);
}
+
+ [Fact]
+ public void Compile_UsesProcessWideCachingMetadataResolver()
+ {
+ SiteScriptCompileCache.Clear();
+ var result = _service.Compile("resolver-pin", "return 41 + 1;");
+
+ Assert.True(result.IsSuccess);
+ // Without the shared caching resolver, EVERY compile re-resolves the
+ // transitive assembly closure via MetadataReference.CreateFromFile —
+ // ~74+ fresh native metadata copies per compiled script (2026-08-12 dump).
+ Assert.Same(
+ ZB.MOM.WW.ScadaBridge.ScriptAnalysis.CachingScriptMetadataResolver.Instance,
+ result.CompiledScript!.Options.MetadataResolver);
+ }
}