diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/GalaxyDriver.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/GalaxyDriver.cs
index 75f01621..65a199de 100644
--- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/GalaxyDriver.cs
+++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/GalaxyDriver.cs
@@ -584,6 +584,15 @@ public sealed class GalaxyDriver
// ===== ITagDiscovery (PR 4.1) =====
+ ///
+ /// Run-once: fetches the full Galaxy hierarchy inline and
+ /// streams the complete node set within a single awaited call — there is no FOCAS-style
+ /// background cache that fills in after connect. Galaxy is a heavy network driver, so the
+ /// bounded post-connect retry loop is deliberately avoided; re-discovery on Galaxy
+ /// redeploy is handled separately via + the deploy-event watcher.
+ ///
+ public DiscoveryRediscoverPolicy RediscoverPolicy => DiscoveryRediscoverPolicy.Once;
+
///
public async Task DiscoverAsync(IAddressSpaceBuilder builder, CancellationToken cancellationToken)
{
diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus/ModbusDriver.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus/ModbusDriver.cs
index 8f38e5f2..4d8ad138 100644
--- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus/ModbusDriver.cs
+++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus/ModbusDriver.cs
@@ -261,6 +261,13 @@ public sealed class ModbusDriver
// ---- ITagDiscovery ----
+ ///
+ /// Run-once: emits the complete node set synchronously from
+ /// the configured tag table in a single pass — nothing fills in asynchronously after
+ /// connect, so a single discovery pass is sufficient.
+ ///
+ public DiscoveryRediscoverPolicy RediscoverPolicy => DiscoveryRediscoverPolicy.Once;
+
/// Discovers tags and builds the OPC UA address space.
/// Address space builder.
/// Cancellation token.
diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7/S7Driver.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7/S7Driver.cs
index a400afd0..c135b1f1 100644
--- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7/S7Driver.cs
+++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7/S7Driver.cs
@@ -1133,6 +1133,13 @@ public sealed class S7Driver
// ---- ITagDiscovery ----
+ ///
+ /// Run-once: emits the complete node set synchronously from
+ /// the configured tag table in a single pass — nothing fills in asynchronously after
+ /// connect, so a single discovery pass is sufficient.
+ ///
+ public DiscoveryRediscoverPolicy RediscoverPolicy => DiscoveryRediscoverPolicy.Once;
+
/// Discovers tags and builds the OPC UA address space.
/// Address space builder.
/// Cancellation token.
diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/AbLegacyDriverTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/AbLegacyDriverTests.cs
index d684915c..ee0266ab 100644
--- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/AbLegacyDriverTests.cs
+++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/AbLegacyDriverTests.cs
@@ -18,6 +18,17 @@ public sealed class AbLegacyDriverTests
drv.DriverInstanceId.ShouldBe("drv-1");
}
+ ///
+ /// Verifies AbLegacy opts into run-once post-connect re-discovery — it discovers its
+ /// complete node set synchronously from config, with no FOCAS-style background cache fill.
+ ///
+ [Fact]
+ public void RediscoverPolicy_is_Once()
+ {
+ var drv = new AbLegacyDriver(new AbLegacyDriverOptions(), "drv-1");
+ drv.RediscoverPolicy.ShouldBe(DiscoveryRediscoverPolicy.Once);
+ }
+
/// Verifies that InitializeAsync with devices assigns family profiles.
[Fact]
public async Task InitializeAsync_with_devices_assigns_family_profiles()