diff --git a/docs/plans/2026-06-07-virtualtag-script-memory.md.tasks.json b/docs/plans/2026-06-07-virtualtag-script-memory.md.tasks.json
index 6d368b54..169bf54a 100644
--- a/docs/plans/2026-06-07-virtualtag-script-memory.md.tasks.json
+++ b/docs/plans/2026-06-07-virtualtag-script-memory.md.tasks.json
@@ -1,9 +1,9 @@
{
"planPath": "docs/plans/2026-06-07-virtualtag-script-memory.md",
"tasks": [
- {"id": 1, "subject": "Task 1: A0 — extract script types into lean Roslyn-free assembly", "status": "pending", "classification": "high-risk"},
- {"id": 2, "subject": "Task 2: A0 measurement gate — re-run probe, confirm ~11x drop", "status": "pending", "classification": "small", "blockedBy": [1]},
- {"id": 3, "subject": "Task 3: A — passthrough fast-path in evaluator", "status": "pending", "classification": "small", "blockedBy": [1]},
+ {"id": 1, "subject": "Task 1: A0 — extract script types into lean Roslyn-free assembly", "status": "completed", "classification": "high-risk"},
+ {"id": 2, "subject": "Task 2: A0 measurement gate — re-run probe, confirm ~11x drop", "status": "completed", "classification": "small", "blockedBy": [1]},
+ {"id": 3, "subject": "Task 3: A — passthrough fast-path in evaluator", "status": "completed", "classification": "small", "blockedBy": [1]},
{"id": 4, "subject": "Task 4: warn-only deploy guardrail", "status": "pending", "classification": "standard", "blockedBy": [1]},
{"id": 5, "subject": "Task 5: live docker-dev verification (1036-vtag overlay, no OOM)", "status": "pending", "classification": "standard", "blockedBy": [1, 3, 4]}
],
diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting.Abstractions/PassthroughScript.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting.Abstractions/PassthroughScript.cs
new file mode 100644
index 00000000..c89a1fa7
--- /dev/null
+++ b/src/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting.Abstractions/PassthroughScript.cs
@@ -0,0 +1,29 @@
+namespace ZB.MOM.WW.OtOpcUa.Core.Scripting;
+
+using System.Text.RegularExpressions;
+
+///
+/// Classifies the trivial "mirror" VirtualTag script shape return ctx.GetTag("X").Value;,
+/// which can be evaluated by returning the dependency value directly — no Roslyn compilation.
+/// Narrow, exact pattern: any near-miss returns false and falls through to the Roslyn path.
+///
+public static partial class PassthroughScript
+{
+ // ^ \s* return \s+ ctx . GetTag ( "X" ) . Value ; \s* $ (whitespace-tolerant around tokens)
+ [GeneratedRegex(@"^\s*return\s+ctx\s*\.\s*GetTag\s*\(\s*""([^""]+)""\s*\)\s*\.\s*Value\s*;\s*$")]
+ private static partial Regex MirrorRegex();
+
+ /// True if is the mirror passthrough shape; outputs the referenced tag.
+ /// The VirtualTag script source to classify.
+ /// On success, the tag reference captured from the mirror shape; otherwise empty.
+ /// if the source is the mirror passthrough shape; otherwise .
+ public static bool TryMatch(string? source, out string tagRef)
+ {
+ tagRef = string.Empty;
+ if (string.IsNullOrEmpty(source)) return false;
+ var m = MirrorRegex().Match(source);
+ if (!m.Success) return false;
+ tagRef = m.Groups[1].Value;
+ return true;
+ }
+}
diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.Host/Engines/RoslynVirtualTagEvaluator.cs b/src/Server/ZB.MOM.WW.OtOpcUa.Host/Engines/RoslynVirtualTagEvaluator.cs
index 33bd98fb..4ec0049e 100644
--- a/src/Server/ZB.MOM.WW.OtOpcUa.Host/Engines/RoslynVirtualTagEvaluator.cs
+++ b/src/Server/ZB.MOM.WW.OtOpcUa.Host/Engines/RoslynVirtualTagEvaluator.cs
@@ -50,6 +50,18 @@ public sealed class RoslynVirtualTagEvaluator : IVirtualTagEvaluator, IDisposabl
if (_disposed) return VirtualTagEvalResult.Failure("evaluator disposed");
if (string.IsNullOrWhiteSpace(expression)) return VirtualTagEvalResult.Failure("empty expression");
+ // A — passthrough fast-path: the mirror shape `return ctx.GetTag("X").Value;` needs no
+ // Roslyn. Narrow exact pattern; near-misses fall through to the compiled path below.
+ // Semantics are byte-identical to the compiled mirror: a present dependency returns its
+ // value (Ok), and an absent one makes GetTag yield a Bad snapshot whose .Value is null,
+ // so the script returns null — Ok(null) here too.
+ if (PassthroughScript.TryMatch(expression, out var passthroughRef))
+ {
+ return dependencies.TryGetValue(passthroughRef, out var ptValue)
+ ? VirtualTagEvalResult.Ok(ptValue)
+ : VirtualTagEvalResult.Ok(null);
+ }
+
ScriptEvaluator evaluator;
try
{
diff --git a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting.Tests/PassthroughScriptTests.cs b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting.Tests/PassthroughScriptTests.cs
new file mode 100644
index 00000000..466788bf
--- /dev/null
+++ b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting.Tests/PassthroughScriptTests.cs
@@ -0,0 +1,79 @@
+using Shouldly;
+using Xunit;
+using ZB.MOM.WW.OtOpcUa.Core.Scripting;
+
+namespace ZB.MOM.WW.OtOpcUa.Core.Scripting.Tests;
+
+///
+/// Unit tests for — the Roslyn-free classifier that recognises
+/// the trivial mirror shape return ctx.GetTag("X").Value;. The pattern must be narrow:
+/// any near-miss (arithmetic, a different member, multi-statement, a different method) falls
+/// through so the Roslyn path stays authoritative.
+///
+public sealed class PassthroughScriptTests
+{
+ /// The canonical mirror shape matches and captures the referenced tag.
+ [Fact]
+ public void Matches_canonical_mirror_and_captures_tag()
+ {
+ PassthroughScript.TryMatch("return ctx.GetTag(\"a\").Value;", out var tag).ShouldBeTrue();
+ tag.ShouldBe("a");
+ }
+
+ /// A dotted / hierarchical tag reference is captured verbatim.
+ [Fact]
+ public void Captures_dotted_tag_reference()
+ {
+ PassthroughScript.TryMatch("return ctx.GetTag(\"Area1.Machine.Speed\").Value;", out var tag)
+ .ShouldBeTrue();
+ tag.ShouldBe("Area1.Machine.Speed");
+ }
+
+ /// Whitespace around every token is tolerated.
+ [Fact]
+ public void Tolerates_surrounding_and_inner_whitespace()
+ {
+ PassthroughScript.TryMatch(" return ctx . GetTag( \"a\" ) . Value ; ", out var tag)
+ .ShouldBeTrue();
+ tag.ShouldBe("a");
+ }
+
+ /// Arithmetic on the mirror value is NOT a passthrough.
+ [Fact]
+ public void Rejects_arithmetic_on_value()
+ {
+ PassthroughScript.TryMatch("return (int)ctx.GetTag(\"a\").Value + 1;", out _).ShouldBeFalse();
+ }
+
+ /// Reading a different member (StatusCode) is NOT a passthrough.
+ [Fact]
+ public void Rejects_other_member_access()
+ {
+ PassthroughScript.TryMatch("return ctx.GetTag(\"a\").StatusCode;", out _).ShouldBeFalse();
+ }
+
+ /// A multi-statement body is NOT a passthrough.
+ [Fact]
+ public void Rejects_multi_statement_body()
+ {
+ PassthroughScript.TryMatch("var x = ctx.GetTag(\"a\").Value; return x;", out _).ShouldBeFalse();
+ }
+
+ /// A different method name is NOT a passthrough.
+ [Fact]
+ public void Rejects_different_method()
+ {
+ PassthroughScript.TryMatch("return ctx.GetVirtualTag(\"a\").Value;", out _).ShouldBeFalse();
+ }
+
+ /// Null / empty / whitespace input is rejected.
+ [Theory]
+ [InlineData(null)]
+ [InlineData("")]
+ [InlineData(" ")]
+ public void Rejects_null_or_blank(string? source)
+ {
+ PassthroughScript.TryMatch(source, out var tag).ShouldBeFalse();
+ tag.ShouldBe(string.Empty);
+ }
+}
diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/RoslynVirtualTagEvaluatorTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/RoslynVirtualTagEvaluatorTests.cs
index 25046078..520fa4ba 100644
--- a/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/RoslynVirtualTagEvaluatorTests.cs
+++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/RoslynVirtualTagEvaluatorTests.cs
@@ -1,3 +1,5 @@
+using System.Collections;
+using System.Reflection;
using Microsoft.Extensions.Logging.Abstractions;
using Shouldly;
using Xunit;
@@ -95,4 +97,135 @@ public sealed class RoslynVirtualTagEvaluatorTests
result.Success.ShouldBeFalse();
result.Reason!.ShouldContain("disposed");
}
+
+ // ── A — passthrough fast-path: the mirror shape `return ctx.GetTag("X").Value;`
+ // is answered directly from the dependency value, skipping Roslyn compilation. ──
+
+ /// Mirror passthrough returns the dependency value verbatim without compiling.
+ [Fact]
+ public void Passthrough_returns_dependency_value_without_compiling()
+ {
+ using var sut = new RoslynVirtualTagEvaluator(NullLogger.Instance);
+
+ var result = sut.Evaluate(
+ virtualTagId: "vt-mirror",
+ expression: "return ctx.GetTag(\"a\").Value;",
+ dependencies: new Dictionary { ["a"] = 42 });
+
+ result.Success.ShouldBeTrue(result.Reason);
+ result.Value.ShouldBe(42);
+ }
+
+ /// The passthrough fast-path returns the raw object reference, not a re-boxed copy —
+ /// proof the value flowed straight through without round-tripping a Roslyn script run.
+ [Fact]
+ public void Passthrough_returns_same_object_reference_as_dependency()
+ {
+ using var sut = new RoslynVirtualTagEvaluator(NullLogger.Instance);
+ var payload = new object();
+
+ var result = sut.Evaluate(
+ virtualTagId: "vt-ref",
+ expression: "return ctx.GetTag(\"a\").Value;",
+ dependencies: new Dictionary { ["a"] = payload });
+
+ result.Success.ShouldBeTrue(result.Reason);
+ result.Value.ShouldBeSameAs(payload);
+ }
+
+ /// Whitespace-tolerant mirror shapes still take the passthrough fast-path.
+ [Fact]
+ public void Passthrough_whitespace_variants_match()
+ {
+ using var sut = new RoslynVirtualTagEvaluator(NullLogger.Instance);
+
+ var result = sut.Evaluate(
+ virtualTagId: "vt-ws",
+ expression: " return ctx . GetTag( \"a\" ) . Value ; ",
+ dependencies: new Dictionary { ["a"] = 7 });
+
+ result.Success.ShouldBeTrue(result.Reason);
+ result.Value.ShouldBe(7);
+ }
+
+ /// A near-miss (arithmetic on the mirror value) falls through to Roslyn and still works.
+ [Fact]
+ public void Non_passthrough_falls_through_to_Roslyn()
+ {
+ using var sut = new RoslynVirtualTagEvaluator(NullLogger.Instance);
+
+ var result = sut.Evaluate(
+ virtualTagId: "vt-plus1",
+ expression: "return (int)ctx.GetTag(\"a\").Value + 1;",
+ dependencies: new Dictionary { ["a"] = 42 });
+
+ result.Success.ShouldBeTrue(result.Reason);
+ result.Value.ShouldBe(43);
+ }
+
+ /// Passthrough with an absent dependency yields the SAME result the Roslyn path
+ /// produces: GetTag returns a Bad snapshot whose .Value is null, so the script
+ /// returns null and the evaluator wraps it as Ok(null) (success, null value).
+ [Fact]
+ public void Passthrough_missing_dependency_matches_Roslyn_behaviour()
+ {
+ using var sut = new RoslynVirtualTagEvaluator(NullLogger.Instance);
+ const string mirror = "return ctx.GetTag(\"missing\").Value;";
+
+ // Roslyn baseline: same source, but force a near-miss that compiles, to capture the
+ // not-found semantics independently. Here we just assert the mirror's own missing-dep
+ // result equals the documented Ok(null) shape.
+ var passthrough = sut.Evaluate("vt-miss", mirror, new Dictionary());
+
+ passthrough.Success.ShouldBeTrue(passthrough.Reason);
+ passthrough.Value.ShouldBeNull();
+ passthrough.Reason.ShouldBeNull();
+ }
+
+ /// Cross-check: the equivalent Roslyn-compiled read of a missing dependency
+ /// produces exactly the same Ok(null) result, proving the fast-path is byte-identical.
+ [Fact]
+ public void Roslyn_missing_dependency_also_returns_Ok_null()
+ {
+ using var sut = new RoslynVirtualTagEvaluator(NullLogger.Instance);
+ // `(object?)...Value` forces the compiled path (not the mirror shape) but reads the same
+ // missing tag; result must match the passthrough missing-dep result above.
+ var result = sut.Evaluate(
+ "vt-miss-roslyn",
+ "return (object?)ctx.GetTag(\"missing\").Value;",
+ new Dictionary());
+
+ result.Success.ShouldBeTrue(result.Reason);
+ result.Value.ShouldBeNull();
+ result.Reason.ShouldBeNull();
+ }
+
+ /// Decisive proof the fast-path skips Roslyn: the compiled-script cache (which every
+ /// Roslyn evaluation populates via GetOrAdd) stays EMPTY after a mirror evaluation, then
+ /// grows to one entry once a genuine (non-mirror) expression forces compilation.
+ [Fact]
+ public void Passthrough_does_not_populate_the_compiled_script_cache()
+ {
+ using var sut = new RoslynVirtualTagEvaluator(NullLogger.Instance);
+
+ // Mirror shape — must take the fast-path, leaving the Roslyn cache untouched.
+ sut.Evaluate("vt-mirror", "return ctx.GetTag(\"a\").Value;",
+ new Dictionary { ["a"] = 1 });
+ CompiledCacheCount(sut).ShouldBe(0);
+
+ // Non-mirror shape — must compile, populating exactly one cache entry.
+ sut.Evaluate("vt-real", "return (int)ctx.GetTag(\"a\").Value + 1;",
+ new Dictionary { ["a"] = 1 });
+ CompiledCacheCount(sut).ShouldBe(1);
+ }
+
+ /// Reads the count of the private compiled-script cache via reflection.
+ private static int CompiledCacheCount(RoslynVirtualTagEvaluator sut)
+ {
+ var field = typeof(RoslynVirtualTagEvaluator)
+ .GetField("_cache", BindingFlags.Instance | BindingFlags.NonPublic);
+ field.ShouldNotBeNull();
+ var cache = (ICollection)field.GetValue(sut)!;
+ return cache.Count;
+ }
}