diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Uns/IEffectiveNameGuard.cs b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Uns/IEffectiveNameGuard.cs
new file mode 100644
index 00000000..959d8acd
--- /dev/null
+++ b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Uns/IEffectiveNameGuard.cs
@@ -0,0 +1,61 @@
+namespace ZB.MOM.WW.OtOpcUa.AdminUI.Uns;
+
+///
+/// Which authoring surface a proposed effective name comes from. Used to word the collision
+/// error and to exclude the correct same-kind self-row on an edit (rename / override change).
+///
+public enum EffectiveNameSourceKind
+{
+ /// A (effective name =
+ /// DisplayNameOverride else the backing raw tag's Name).
+ Reference,
+
+ /// An equipment-bound (effective name = Name).
+ VirtualTag,
+
+ /// An equipment-bound (effective name = Name).
+ ScriptedAlarm,
+}
+
+///
+/// v3 Batch 3 authoring-time guard for the UNS effective-name uniqueness rule. Within an
+/// equipment the EFFECTIVE leaf name must be unique across its UnsTagReferences
+/// (DisplayNameOverride else the backing raw tag's Name), its VirtualTags
+/// (Name), and its ScriptedAlarms (Name) — the three share the equipment's
+/// UNS NodeId space ({EquipmentId}/{EffectiveName}). This is the authoring mirror of the
+/// deploy-time DraftValidator UnsEffectiveNameCollision rule; comparison is
+/// to match NodeId semantics and the validator.
+///
+///
+/// Injectable (registered Scoped, its own pooled context per call — the same pattern
+/// UnsTreeService uses). Consumed by UnsTreeService's reference / virtual-tag /
+/// scripted-alarm mutations, which call before persisting and surface a
+/// non-null result as the mutation's readable failure. The deploy gate remains the backstop for
+/// rename-induced collisions the authoring check never saw.
+///
+public interface IEffectiveNameGuard
+{
+ ///
+ /// Tests whether would collide with an existing
+ /// effective name within .
+ ///
+ /// The owning equipment's logical id.
+ /// The effective name to be authored (override else raw name for a reference; Name for a VT/alarm).
+ /// Which surface the proposed name is for (words the error; identifies the self-row kind to exclude).
+ ///
+ /// On an edit, the logical id of the row being changed (its UnsTagReferenceId /
+ /// VirtualTagId / ScriptedAlarmId) so it is excluded from the collision set;
+ /// on a create.
+ ///
+ /// Cancellation token.
+ ///
+ /// when the name is free; otherwise a readable error naming the
+ /// colliding existing source and the equipment.
+ ///
+ Task CheckAsync(
+ string equipmentId,
+ string proposedEffectiveName,
+ EffectiveNameSourceKind kind,
+ string? excludeSourceId,
+ CancellationToken ct = default);
+}