diff --git a/src/Client/ZB.MOM.WW.OtOpcUa.Client.CLI/Commands/ReadCommand.cs b/src/Client/ZB.MOM.WW.OtOpcUa.Client.CLI/Commands/ReadCommand.cs
index 4f42413b..f9678a2a 100644
--- a/src/Client/ZB.MOM.WW.OtOpcUa.Client.CLI/Commands/ReadCommand.cs
+++ b/src/Client/ZB.MOM.WW.OtOpcUa.Client.CLI/Commands/ReadCommand.cs
@@ -56,7 +56,7 @@ public class ReadCommand : CommandBase
{
// The SDK's Session.ReadValue surfaces a Bad node status (BadNodeIdUnknown,
// BadUserAccessDenied, ...) as an exception. A read command's job is to REPORT that status,
- // not crash with a stack trace (#577) — session-level failures still propagate.
+ // not crash with a stack trace — session-level failures still propagate.
await console.Output.WriteLineAsync($"Node: {NodeId}");
await console.Output.WriteLineAsync($"Value: ");
await console.Output.WriteLineAsync($"Status: {(StatusCode)ex.StatusCode} ({StatusCodes.GetBrowseName(ex.StatusCode)})");
diff --git a/src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/Adapters/ClientTelemetry.cs b/src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/Adapters/ClientTelemetry.cs
index 3f4fbccf..0e50aa4f 100644
--- a/src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/Adapters/ClientTelemetry.cs
+++ b/src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/Adapters/ClientTelemetry.cs
@@ -24,6 +24,7 @@ internal static class DisposeBridge
private static readonly TimeSpan Timeout = TimeSpan.FromSeconds(5);
/// Runs to completion (or the timeout), swallowing every failure.
+ /// The asynchronous work to run; it receives a token that is cancelled when the timeout elapses.
public static void Run(Func work)
{
using var cts = new CancellationTokenSource(Timeout);
diff --git a/src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/OpcUaClientService.cs b/src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/OpcUaClientService.cs
index a2350a89..c1d87129 100644
--- a/src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/OpcUaClientService.cs
+++ b/src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/OpcUaClientService.cs
@@ -462,7 +462,7 @@ public sealed class OpcUaClientService : IOpcUaClientService
ThrowIfDisposed();
ThrowIfNotConnected();
- // The server's ShelvingState child is SDK-created with a numeric namespace-0 id, so a synthesized
+ // The server's ShelvingState child is SDK-created with a numeric id in namespace 0, so a synthesized
// ".ShelvingState" node id never exists. The shelve methods are called on the condition
// object itself with the AlarmConditionType_ShelvingState_* method ids, which the server dispatches
// to the condition's ShelvingState methods.
@@ -884,6 +884,8 @@ public sealed class OpcUaClientService : IOpcUaClientService
catch { return false; }
}
+ /// Builds the event filter (select clauses for the alarm and condition fields) used by alarm subscriptions.
+ /// The populated .
internal static EventFilter CreateAlarmEventFilter()
{
var filter = new EventFilter();
diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/AkkaClusterOptions.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/AkkaClusterOptions.cs
index d1dc6cb5..2aa53426 100644
--- a/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/AkkaClusterOptions.cs
+++ b/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/AkkaClusterOptions.cs
@@ -58,7 +58,7 @@ public sealed class AkkaClusterOptions
/// . That fallback was
/// described here — and relied upon in the field — for a year before it was implemented; a
/// node configured only through the env var joined the cluster carrying no roles at all.
- /// See Gitea #532 and docs/deployments/wonder-app-vd03-makino-z-34184.md.
+ /// See docs/deployments/wonder-app-vd03-makino-z-34184.md.
///
///
/// The two sources normalize differently: values bound from Cluster:Roles are taken
diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/AkkaClusterOptionsValidator.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/AkkaClusterOptionsValidator.cs
index f68302eb..f622d9b1 100644
--- a/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/AkkaClusterOptionsValidator.cs
+++ b/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/AkkaClusterOptionsValidator.cs
@@ -44,7 +44,7 @@ public sealed class AkkaClusterOptionsValidator : OptionsValidatorBaseAddValidatedOptions (a plain AddSingleton), so constructor
/// dependencies are safe here. Both and
/// are optional (default ) so a caller
- /// with neither to hand — including every test in this file that pre-dates R3-04 — keeps
+ /// with neither to hand — including every test in this file that pre-dates the post-overlay guard — keeps
/// constructing this validator parameterlessly; the post-overlay guard below (choice 4) is
/// simply skipped when either is null.
///
@@ -116,7 +116,7 @@ public sealed class AkkaClusterOptionsValidator : OptionsValidatorBase
- /// R3-04 (choice 4, the residual divergence surface): Program.cs resolves
+ /// Post-overlay guard (choice 4, the residual divergence surface): Program.cs resolves
/// BEFORE the per-role appsettings overlay loads (the overlay's
/// filename depends on the answer), so if a role overlay — or a ${secret:} expansion —
/// ever set Cluster:Roles itself, that value would silently disagree with the
diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/ClusterBootstrapCoordinator.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/ClusterBootstrapCoordinator.cs
index cfd0754d..9df34be7 100644
--- a/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/ClusterBootstrapCoordinator.cs
+++ b/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/ClusterBootstrapCoordinator.cs
@@ -46,7 +46,9 @@ public sealed class ClusterBootstrapCoordinator : IHostedService
_log = log ?? throw new ArgumentNullException(nameof(log));
}
- ///
+ /// Starts the bootstrap join in the background when the bootstrap guard is enabled.
+ /// Not used; the join runs under the coordinator's own cancellation source.
+ /// A completed task; the join itself continues asynchronously.
public Task StartAsync(CancellationToken cancellationToken)
{
if (!_options.BootstrapGuard.Enabled)
@@ -59,7 +61,9 @@ public sealed class ClusterBootstrapCoordinator : IHostedService
return Task.CompletedTask;
}
- ///
+ /// Cancels the background join and waits for it to finish.
+ /// Not used; shutdown is driven by the coordinator's own cancellation source.
+ /// A task that represents the asynchronous operation.
public async Task StopAsync(CancellationToken cancellationToken)
{
await _cts.CancelAsync().ConfigureAwait(false);
diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/ClusterBootstrapGuard.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/ClusterBootstrapGuard.cs
index 38a69ac1..b84b2afa 100644
--- a/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/ClusterBootstrapGuard.cs
+++ b/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/ClusterBootstrapGuard.cs
@@ -63,7 +63,7 @@ public static class ClusterBootstrapGuard
///
/// Analyzes the configured seed list to decide this node's bootstrap role. The guard engages only
- /// for the Phase-6 pair shape (exactly two seeds, one of them this node); any other shape
+ /// for the pair shape (exactly two seeds, one of them this node); any other shape
/// (single-seed legacy site node, three+ seeds, self absent) returns
/// = false and the caller joins the configured seeds unchanged.
///
diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/ClusterRoleInfo.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/ClusterRoleInfo.cs
index 44833637..9992061a 100644
--- a/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/ClusterRoleInfo.cs
+++ b/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/ClusterRoleInfo.cs
@@ -23,7 +23,7 @@ public sealed class ClusterRoleInfo : IClusterRoleInfo, IDisposable
private readonly string? _clusterRole;
private readonly string? _clusterId;
private readonly object _lock = new();
- // R3-04 (01/C-9, census row 17) — the two dictionaries below have DIFFERENT correct comparers,
+ // The two dictionaries below have DIFFERENT correct comparers,
// and that split is deliberate, not an oversight (review finding, 2026-08-20).
//
// _membersByRole answers "every member carrying (a spelling of) this role" — OrdinalIgnoreCase,
@@ -53,7 +53,7 @@ public sealed class ClusterRoleInfo : IClusterRoleInfo, IDisposable
// sharing a bind IP) stay distinct. Production hosts have unique DNS names so the port
// suffix is harmless redundancy.
_localNode = CommonsNodeId.Parse($"{options.Value.PublicHostname}:{options.Value.Port}");
- // OrdinalIgnoreCase (Gitea #532): Cluster:Roles binds verbatim, so a node authored with 'Driver'
+ // OrdinalIgnoreCase: Cluster:Roles binds verbatim, so a node authored with 'Driver'
// used to answer HasRole("driver") false while gossiping a role Akka would still match against
// its own ordinal comparison — the node was a driver to the cluster and not to itself.
_localRoles = new HashSet(options.Value.Roles, StringComparer.OrdinalIgnoreCase);
@@ -128,7 +128,7 @@ public sealed class ClusterRoleInfo : IClusterRoleInfo, IDisposable
{
lock (_lock)
{
- // Cluster-002 (review R1 m6): seed only USABLE members. A member already Leaving / Exiting / Down
+ // Seed only USABLE members. A member already Leaving / Exiting / Down
// (or still Joining) at seed time is not one, and a departing member never fires a later
// MemberLeft / MemberExited / MemberDowned that HandleMemberEvent would drop it on — it would
// linger until MemberRemoved, the very delay the member-event handling exists to avoid. A
@@ -165,7 +165,7 @@ public sealed class ClusterRoleInfo : IClusterRoleInfo, IDisposable
set.Add(up.Member);
}
break;
- // Cluster-002: a leaving / exiting / downed member is no longer a usable member, so
+ // A leaving / exiting / downed member is no longer a usable member, so
// drop it as soon as that transition lands rather than waiting for MemberRemoved
// (several seconds later). MembersWithRole now has a production caller (the
// historization scope-peer check), which must not defer to a departing partner.
@@ -210,8 +210,8 @@ public sealed class ClusterRoleInfo : IClusterRoleInfo, IDisposable
// Record the event's own address. The leader used to be resolved by looking the address up
// in Cluster.State.Members, but that snapshot is published asynchronously and can lag the
- // event: a miss stored null and nothing re-ran, so RoleLeader(role) stayed null for good
- // (Gitea #590). Only the address is ever read, so no Member lookup is needed.
+ // event: a miss stored null and nothing re-ran, so RoleLeader(role) stayed null for good.
+ // Only the address is ever read, so no Member lookup is needed.
_roleLeaders[evt.Role] = evt.Leader;
if (evt.Leader is not null)
next = ToNodeId(evt.Leader);
diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/ConfigSourceOptionsValidator.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/ConfigSourceOptionsValidator.cs
index 165703c1..42294ea9 100644
--- a/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/ConfigSourceOptionsValidator.cs
+++ b/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/ConfigSourceOptionsValidator.cs
@@ -21,14 +21,18 @@ public sealed class ConfigSourceOptionsValidator : IValidateOptionsAddValidatedOptions (a plain AddSingleton), so a
/// constructor dependency is safe here. This validator's only use of configuration is this
/// node's roles, so it takes — the single role authority
- /// (archreview R3-04) — directly rather than .
+ /// — directly rather than .
///
+ /// The node's effective roles, used to decide which fetch settings are required.
public ConfigSourceOptionsValidator(EffectiveRoles effectiveRoles)
{
_effectiveRoles = effectiveRoles;
}
- ///
+ /// Validates the for this node's roles.
+ /// The options instance name (unused).
+ /// The options to validate.
+ /// A success result, or a failure listing every violated rule.
public ValidateOptionsResult Validate(string? name, ConfigSourceOptions options)
{
ArgumentNullException.ThrowIfNull(options);
@@ -92,8 +96,8 @@ public sealed class ConfigSourceOptionsValidator : IValidateOptionsTrue when is among , compared case-insensitively.
/// The role to test for.
+ /// when the role is present; otherwise .
public bool Has(string role) =>
Gossiped.Any(r => string.Equals(r, role, StringComparison.OrdinalIgnoreCase));
diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/MeshTransportOptionsValidator.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/MeshTransportOptionsValidator.cs
index d631c216..4eca6d84 100644
--- a/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/MeshTransportOptionsValidator.cs
+++ b/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/MeshTransportOptionsValidator.cs
@@ -23,7 +23,7 @@ public sealed class MeshTransportOptionsValidator : IValidateOptionsAddValidatedOptions (a plain AddSingleton), so constructor
/// dependencies are safe here. is still needed for the
/// ConfigServe:GrpcListenPort cross-read (the admin-side obligation below) —
- /// is the single role authority (archreview R3-04) this rule reads
+ /// is the single role authority this rule reads
/// roles through instead of re-deriving them.
///
/// The application configuration, read for ConfigServe:GrpcListenPort.
@@ -34,7 +34,10 @@ public sealed class MeshTransportOptionsValidator : IValidateOptions
+ /// Validates the for this node's roles.
+ /// The options instance name (unused).
+ /// The options to validate.
+ /// A success result, or a failure listing every violated rule.
public ValidateOptionsResult Validate(string? name, MeshTransportOptions options)
{
ArgumentNullException.ThrowIfNull(options);
@@ -71,7 +74,7 @@ public sealed class MeshTransportOptionsValidator : IValidateOptionsReturns true when is a cluster-scoped role — starts with
/// and carries a non-empty ClusterId suffix.
///
- /// The prefix test is case-insensitive (Gitea #532). It was ordinal, which meant a
+ /// The prefix test is case-insensitive. It was ordinal, which meant a
/// Cluster:Roles entry spelled Cluster-SITE-A — that path binds verbatim, unlike
/// the path, which lower-cases — classified as an ordinary role. The node
/// then resolved a ClusterId while still gossiping a cluster-scoped role,
@@ -33,6 +33,7 @@ public static class RoleParser
/// ClusterRoleInfo did not, and got the null.
///
/// The role name to test.
+ /// if the role is a cluster-scoped role.
public static bool IsClusterRole(string role) =>
role is not null
&& role.StartsWith(ClusterRolePrefix, StringComparison.OrdinalIgnoreCase)
@@ -47,9 +48,10 @@ public static class RoleParser
/// OTOPCUA_ROLES path (⇒ site-a) — and normalizing one side would silently rename
/// the Akka role of a running deployment (docker-dev's rig configures cluster-MAIN /
/// cluster-SITE-A verbatim today). Every consumer of a ClusterId therefore compares it
- /// case-insensitively; see the identity rule on AclScopeRef (Gitea #533).
+ /// case-insensitively; see the identity rule on AclScopeRef.
///
/// The cluster-scoped role name.
+ /// The ClusterId portion of the role, with the role prefix removed.
public static string ClusterIdFromRole(string role) => role[ClusterRolePrefix.Length..];
///
@@ -72,7 +74,7 @@ public static class RoleParser
/// cluster-scoped role (a legacy / single-mesh node).
///
///
- /// The single home for a derivation that was open-coded at four call sites before Gitea #532
+ /// The single home for a derivation that was open-coded at four call sites before
/// (ClusterRoleInfo, the ControlPlane singleton scope, SplitTopologyTransportValidator
/// and ConfigDbApiKeyStore) — each with its own view of whether the classification was
/// case-sensitive, which is how the mixed-case gap above survived in three of the four.
@@ -91,7 +93,7 @@ public static class RoleParser
///
///
///
- /// ⚠️ This fallback was documented for a year before it existed (Gitea #532).
+ /// ⚠️ This fallback was documented for a year before it existed.
/// AkkaClusterOptions.Roles' own XML doc and
/// both described it; only the validator implemented it, privately. A node configured solely
/// through OTOPCUA_ROLES — which drives every bit of Program.cs's conditional
@@ -114,7 +116,7 @@ public static class RoleParser
/// Either source names an unknown role.
///
/// Thin delegation to —
- /// the single role authority (archreview R3-04). is the type to
+ /// the single role authority. is the type to
/// reach for directly; this overload is retained so existing callers of the array-returning
/// shape keep compiling.
///
diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/ServiceCollectionExtensions.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/ServiceCollectionExtensions.cs
index 16f9e466..fda0c131 100644
--- a/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/ServiceCollectionExtensions.cs
+++ b/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/ServiceCollectionExtensions.cs
@@ -35,7 +35,7 @@ public static class ServiceCollectionExtensions
services.AddValidatedOptions(
configuration, AkkaClusterOptions.SectionName);
- // R3-04 (structural identity, choice 3): EffectiveRoles is resolved ONCE and registered as a
+ // Structural identity: EffectiveRoles is resolved ONCE and registered as a
// singleton, off the SAME IConfiguration this call received — not off a DI-resolved
// IConfiguration, which a plain ServiceCollection (as opposed to a WebApplicationBuilder) never
// registers. Every consumer (Program.cs's hasAdmin/hasDriver, the four role-scoped validators,
@@ -54,7 +54,7 @@ public static class ServiceCollectionExtensions
// otherwise; TryAddSingleton is a no-op on a real host, where it is already registered.
services.TryAddSingleton(configuration);
- // Gitea #532 (superseded by R3-04): apply the OTOPCUA_ROLES fallback the Roles property has
+ // Apply the OTOPCUA_ROLES fallback the Roles property has
// always DOCUMENTED, in the one place every consumer reads through. Before this, a node
// configured only through the env var — the knob that drives all of Program.cs's role wiring —
// bound an EMPTY Roles array and so joined the cluster role-less: invisible to
diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/SplitTopologyTransportValidator.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/SplitTopologyTransportValidator.cs
index 4429786b..a37de7be 100644
--- a/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/SplitTopologyTransportValidator.cs
+++ b/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/SplitTopologyTransportValidator.cs
@@ -14,7 +14,7 @@ namespace ZB.MOM.WW.OtOpcUa.Cluster;
/// Phase 2/3/5 dark switches keep their DPS branches alive, but DPS is mesh-wide — it delivers
/// nothing between a central node and a site node that no longer share a gossip ring. The
/// marker of the split topology is a cluster-{ClusterId} role
-/// (); those roles exist only post-Phase-6. A node that
+/// (); those roles exist only in the per-cluster mesh topology. A node that
/// carries one must use the mesh-crossing transports, and this validator refuses to
/// boot it otherwise — the alternative is a node that runs, listens, and silently delivers no
/// commands or telemetry, which has no stack trace and no failing node to point at.
@@ -44,16 +44,21 @@ public sealed class SplitTopologyTransportValidator : IValidateOptionsMeshTransport:Mode, Telemetry:Mode,
/// TelemetryDial:Mode) — not IClusterRoleInfo, whose implementation needs the
/// ActorSystem, which does not exist yet at ValidateOnStart time.
- /// is the single role authority (archreview R3-04) — this rule no
+ /// is the single role authority — this rule no
/// longer re-derives the effective, normalized role list itself.
///
+ /// The host configuration used to cross-read the transport-mode settings.
+ /// The node's effective, normalized role list.
public SplitTopologyTransportValidator(IConfiguration configuration, EffectiveRoles effectiveRoles)
{
_configuration = configuration;
_effectiveRoles = effectiveRoles;
}
- ///
+ /// Fails startup when a split-topology node is left on a transport mode that cannot work there.
+ /// The options instance name; unused.
+ /// The mesh transport options; unused because every input is cross-read from configuration.
+ /// A success result, or a failure naming the offending transport setting.
public ValidateOptionsResult Validate(string? name, MeshTransportOptions options)
{
// The options instance is intentionally unused: every value this rule depends on is cross-read
diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/TelemetryOptions.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/TelemetryOptions.cs
index fef3c386..779228b9 100644
--- a/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/TelemetryOptions.cs
+++ b/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/TelemetryOptions.cs
@@ -93,14 +93,18 @@ public sealed class TelemetryOptionsValidator : IValidateOptionsAddValidatedOptions (a plain AddSingleton), so a
/// constructor dependency is safe here. This validator's only use of configuration is this
/// node's roles, so it takes — the single role authority
- /// (archreview R3-04) — directly rather than .
+ /// — directly rather than .
///
+ /// The node's resolved roles, the single role authority.
public TelemetryOptionsValidator(EffectiveRoles effectiveRoles)
{
_effectiveRoles = effectiveRoles;
}
- ///
+ /// Validates the telemetry options against this node's roles.
+ /// The named options instance, or for the default.
+ /// The options to validate.
+ /// Success, or a failure listing every invalid setting.
public ValidateOptionsResult Validate(string? name, TelemetryOptions options)
{
ArgumentNullException.ThrowIfNull(options);
@@ -117,8 +121,8 @@ public sealed class TelemetryOptionsValidator : IValidateOptions
public sealed class TelemetryDialOptionsValidator : IValidateOptions
{
- ///
+ /// Validates the telemetry dial options.
+ /// The named options instance, or for the default.
+ /// The options to validate.
+ /// Success, or a failure listing every invalid setting.
public ValidateOptionsResult Validate(string? name, TelemetryDialOptions options)
{
ArgumentNullException.ThrowIfNull(options);
diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Browsing/CapturedTreeBrowseSession.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Browsing/CapturedTreeBrowseSession.cs
index 0ace043a..248d9ee1 100644
--- a/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Browsing/CapturedTreeBrowseSession.cs
+++ b/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Browsing/CapturedTreeBrowseSession.cs
@@ -56,7 +56,8 @@ public sealed class CapturedTreeBrowseSession(CapturedTree tree) : IBrowseSessio
return Task.FromResult>(attrs);
}
- ///
+ /// Drops this session's reference to the captured tree; the tree itself may be shared.
+ /// A completed .
public ValueTask DisposeAsync()
{
_tree = null; // drop this session's reference only — tree may be shared (coalescing)
@@ -68,7 +69,7 @@ public sealed class CapturedTreeBrowseSession(CapturedTree tree) : IBrowseSessio
///
/// Surfaces a leaf's driver-native type name as the dataType address field, so a
/// browse-commit can stamp the driver-typed TagConfig with the type the controller
- /// actually reported (Gitea #536).
+ /// actually reported.
///
/// The captured attribute.
/// The address fields, or null when the driver reported no native type.
@@ -80,7 +81,7 @@ public sealed class CapturedTreeBrowseSession(CapturedTree tree) : IBrowseSessio
/// reverse map would bind a 16-bit INT as a 32-bit DINT and read four bytes for two.
///
/// Null when the driver stamped nothing — the mapper then leaves the model's own default in
- /// place, which is exactly the pre-#536 behaviour rather than a guess.
+ /// place, which is exactly the previous behaviour rather than a guess.
///
///
private static IReadOnlyDictionary? NativeTypeFields(DriverAttributeInfo a)
diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Browsing/CapturingAddressSpaceBuilder.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Browsing/CapturingAddressSpaceBuilder.cs
index ceae69df..ff88850e 100644
--- a/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Browsing/CapturingAddressSpaceBuilder.cs
+++ b/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Browsing/CapturingAddressSpaceBuilder.cs
@@ -17,6 +17,8 @@ public sealed class CapturingAddressSpaceBuilder : IAddressSpaceBuilder
public bool Truncated;
public readonly int NodeCap = nodeCap;
public readonly Dictionary ById = new(StringComparer.Ordinal);
+ /// Reserves a slot for one more node, marking the capture truncated when the cap is reached.
+ /// when the node may be recorded; when the cap is reached.
public bool TryReserve()
{
if (Count >= NodeCap) { Truncated = true; return false; }
@@ -96,7 +98,10 @@ public sealed class CapturingAddressSpaceBuilder : IAddressSpaceBuilder
private sealed class CapturedVariableHandle(CapturedNode node) : IVariableHandle
{
+ ///
public string FullReference => node.Id;
+
+ ///
public IAlarmConditionSink MarkAsAlarmCondition(AlarmConditionInfo info)
{
node.IsAlarmMarked = true;
@@ -107,6 +112,7 @@ public sealed class CapturingAddressSpaceBuilder : IAddressSpaceBuilder
private sealed class NoopAlarmSink : IAlarmConditionSink
{
public static readonly NoopAlarmSink Instance = new();
+ ///
public void OnTransition(AlarmEventArgs args) { } // browse never delivers live transitions
}
}
diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Browsing/DiscoveryDriverBrowser.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Browsing/DiscoveryDriverBrowser.cs
index 6c12e808..acf34e10 100644
--- a/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Browsing/DiscoveryDriverBrowser.cs
+++ b/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Browsing/DiscoveryDriverBrowser.cs
@@ -12,7 +12,7 @@ public sealed class DiscoveryDriverBrowser : IUniversalDriverBrowser
/// 20 s per-call browse timeout, which only covers serving from memory.
public static readonly TimeSpan OpenTimeout = TimeSpan.FromSeconds(60);
- /// Bound on the cleanup ShutdownAsync (R2-01: no unbounded waits — a driver wedged
+ /// Bound on the cleanup ShutdownAsync (no unbounded waits — a driver wedged
/// in discovery may be equally wedged in shutdown).
public static readonly TimeSpan ShutdownTimeout = TimeSpan.FromSeconds(10);
@@ -46,7 +46,11 @@ public sealed class DiscoveryDriverBrowser : IUniversalDriverBrowser
public DiscoveryDriverBrowser(IDriverFactory driverFactory, ILogger logger)
: this(driverFactory, logger, OpenTimeout, ShutdownTimeout) { }
- // Test seam: short timeouts.
+ /// Test seam: constructor with explicit open and shutdown bounds.
+ /// Factory used to construct throwaway/capture driver instances.
+ /// Logger for shutdown-timeout warnings.
+ /// Upper bound on opening a capture driver and running discovery.
+ /// Upper bound on the cleanup shutdown of a capture driver.
internal DiscoveryDriverBrowser(IDriverFactory driverFactory, ILogger logger,
TimeSpan openTimeout, TimeSpan shutdownTimeout)
{
@@ -111,6 +115,10 @@ public sealed class DiscoveryDriverBrowser : IUniversalDriverBrowser
}
}
+ /// Builds the in-flight de-duplication key for a capture request.
+ /// The driver type name.
+ /// The driver configuration JSON.
+ /// A key combining the lower-cased driver type and a hash of the configuration.
internal static string CaptureKey(string driverType, string configJson) =>
driverType.ToLowerInvariant() + ":" +
Convert.ToHexString(System.Security.Cryptography.SHA256.HashData(
@@ -195,6 +203,10 @@ public sealed class DiscoveryDriverBrowser : IUniversalDriverBrowser
}
}
+ /// Applies the driver-specific browse-time patch (if any) to the configuration JSON.
+ /// The driver type name.
+ /// The driver configuration JSON.
+ /// The patched configuration JSON, or the input unchanged when the driver type has no patch.
internal static string PatchForBrowse(string driverType, string configJson)
{
if (!BrowsePatches.TryGetValue(driverType, out var patch)) return configJson;
diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Messages/Deploy/ApplyAck.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Messages/Deploy/ApplyAck.cs
index 2431bd41..b032d4ea 100644
--- a/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Messages/Deploy/ApplyAck.cs
+++ b/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Messages/Deploy/ApplyAck.cs
@@ -13,7 +13,7 @@ public enum ApplyAckOutcome { Applied, Failed }
/// Why the apply failed; null on .
/// The dispatch's correlation id.
///
-/// Gitea #601: when the node applied the deployment (the revision advanced) but
+/// when the node applied the deployment (the revision advanced) but
/// its OPC UA address-space apply was DEGRADED — swallowed per-node sink faults, a failed rebuild, or no
/// outcome reported in time — so the served address space may be partial. Only meaningful with
/// . Trailing and defaulted so an ack from a node built before
@@ -30,7 +30,7 @@ public sealed record ApplyAck(
string? DegradedReason = null)
{
///
- /// Gitea #601 — prefix of NodeDeploymentState.FailureReason on an Applied row whose
+ /// The prefix of NodeDeploymentState.FailureReason on an Applied row whose
/// address-space apply was degraded. Shared by central (which writes it from the ack) and a node with
/// a ConfigDb (which writes its own row), so both spell the row identically.
///
diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Messages/Deploy/DeployTimings.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Messages/Deploy/DeployTimings.cs
index 1f0866de..fc245f93 100644
--- a/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Messages/Deploy/DeployTimings.cs
+++ b/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Messages/Deploy/DeployTimings.cs
@@ -11,11 +11,11 @@ public static class DeployTimings
/// (ConfigPublishCoordinator.DefaultApplyDeadline).
public static readonly TimeSpan ApplyDeadline = TimeSpan.FromMinutes(2);
- /// Gitea #601 — how far inside a node's held Applied ack is released
+ /// How far inside a node's held Applied ack is released
/// when the address-space outcome has not arrived, leaving room for transport latency.
public static readonly TimeSpan ApplyOutcomeSafetyMargin = TimeSpan.FromSeconds(15);
- /// Gitea #601 — the node's default apply-outcome timeout: −
+ /// The node's default apply-outcome timeout: −
/// (105 s). Long enough that a slow-but-clean rebuild of a large
/// address space is not labelled degraded, short enough to ack before central gives up.
/// The timer is armed after the artifact read and driver reconcile, so if those alone took
diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Messages/Drivers/DriverHealthChanged.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Messages/Drivers/DriverHealthChanged.cs
index 7aaea711..c22b012b 100644
--- a/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Messages/Drivers/DriverHealthChanged.cs
+++ b/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Messages/Drivers/DriverHealthChanged.cs
@@ -38,7 +38,7 @@ namespace ZB.MOM.WW.OtOpcUa.Commons.Messages.Drivers;
/// so a driver-only node has no ConfigDb connection to write rows to. This channel already reaches
/// /hosts, already survives the mesh split via the Phase 5 gRPC telemetry stream, and already
/// replays a last-value snapshot on every (re)subscribe — so per-host state re-primes after a
-/// reconnect without a durable store. The table was dropped; see Gitea #521.
+/// reconnect without a durable store. The table was dropped.
/// Value-equality caveat. A record's generated Equals compares this list by
/// REFERENCE, so two DriverHealthChanged carrying equal-but-distinct lists are not equal.
/// Nothing dedups on record equality — DriverInstanceActor keeps its own flattened fingerprint
@@ -48,13 +48,13 @@ namespace ZB.MOM.WW.OtOpcUa.Commons.Messages.Drivers;
/// What this driver skipped when it last built its tag table, from
/// ISkippedTagReporter.GetSkippedTags(); null when the driver does not implement that
/// capability. All twelve production drivers do.
-/// Why this is on the health snapshot (Gitea #541). A skip happens at init, is not an
+/// Why this is on the health snapshot. A skip happens at init, is not an
/// error, and does not stop the driver's OTHER tags from reading — so State stays Healthy,
/// ErrorCount5Min stays 0, LastError stays null and LastSuccessfulReadUtc keeps
-/// advancing while the driver ignores part or all of its configuration. On the Makino in Gitea #540
+/// advancing while the driver ignores part or all of its configuration. On the Makino
/// that combination held while 20 of 22 tags were ignored. This field is what contradicts it.
/// Additive, never a state change. See ISkippedTagReporter for why a skipped tag
-/// must not latch the driver non-Healthy — it would satisfy both halves of the #527 comms-loss
+/// must not latch the driver non-Healthy — it would satisfy both halves of the comms-loss
/// conjunction and reconnect-loop a healthy device.
/// Value-equality caveat — the same one carries: the
/// record's generated Equals compares the sample list by REFERENCE.
@@ -62,7 +62,7 @@ namespace ZB.MOM.WW.OtOpcUa.Commons.Messages.Drivers;
///
/// Operator-facing driver diagnostics from IDriverDiagnostics.GetDiagnostics() (e.g. S7's
/// negotiated PDU size), bounded and in ordinal key order; null when the driver does not implement the
-/// capability (Gitea #640). Additive, never a state change. Same by-reference value-equality caveat as
+/// capability. Additive, never a state change. Same by-reference value-equality caveat as
/// .
///
public sealed record DriverHealthChanged(
diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Messages/Mesh/MeshTransportDtoMapper.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Messages/Mesh/MeshTransportDtoMapper.cs
index 4fe43816..dd3f2d6b 100644
--- a/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Messages/Mesh/MeshTransportDtoMapper.cs
+++ b/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Messages/Mesh/MeshTransportDtoMapper.cs
@@ -216,7 +216,7 @@ public static class MeshTransportDtoMapper
if (message.FailureReason is not null)
proto.FailureReason = message.FailureReason;
- // #601 — additive fields; a clean ack leaves both unset, byte-identical to a pre-#601 encode.
+ // Additive fields; a clean ack leaves both unset, byte-identical to an encode that predates these fields.
proto.Degraded = message.Degraded;
if (message.DegradedReason is not null)
proto.DegradedReason = message.DegradedReason;
@@ -255,7 +255,7 @@ public static class MeshTransportDtoMapper
outcome,
proto.HasFailureReason ? proto.FailureReason : null,
CorrelationId.Parse(proto.CorrelationId),
- // #601 — absent on an old node's ack, which decodes as false/null: today's clean ack.
+ // Absent on an old node's ack, which decodes as false/null: today's clean ack.
Degraded: proto.Degraded,
DegradedReason: proto.HasDegradedReason ? proto.DegradedReason : null);
}
diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Observability/OtOpcUaTelemetry.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Observability/OtOpcUaTelemetry.cs
index 2d8f4dc8..2d31f973 100644
--- a/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Observability/OtOpcUaTelemetry.cs
+++ b/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Observability/OtOpcUaTelemetry.cs
@@ -77,7 +77,7 @@ public static class OtOpcUaTelemetry
public const string PollOverrunDriverInstanceTag = "driver_instance";
///
- /// Session/publish-level events of an OPC UA Client driver instance (#276): the operator's view of a
+ /// Session/publish-level events of an OPC UA Client driver instance: the operator's view of a
/// flapping upstream. Tagged (driver_instance) +
/// event = publish_response|notification|data_change|bad_status_value|publish_error|
/// keepalive_miss|keepalive_recovered|stale_notification_dropped.
diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Commons/OpcUa/AlarmConditionSnapshot.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Commons/OpcUa/AlarmConditionSnapshot.cs
index 1330ce2c..e1813824 100644
--- a/src/Core/ZB.MOM.WW.OtOpcUa.Commons/OpcUa/AlarmConditionSnapshot.cs
+++ b/src/Core/ZB.MOM.WW.OtOpcUa.Commons/OpcUa/AlarmConditionSnapshot.cs
@@ -19,15 +19,15 @@ namespace ZB.MOM.WW.OtOpcUa.Commons.OpcUa;
///
/// Status code of the condition's source data (Part 9 ConditionType.Quality). Carried so a
/// comms-lost native source can report a non-Good condition instead of the accidentally-Good
-/// default (issue #477). It is a pure annotation — it never alters Active/Acked/Retain. Defaults to
+/// default. It is a pure annotation — it never alters Active/Acked/Retain. Defaults to
/// so scripted-alarm callers (which stay Good in v1) need not supply it.
-/// Carries the full sub-code since #519, so a comms-lost condition reports
+/// Carries the full sub-code, so a comms-lost condition reports
/// rather than a bare .
///
///
/// For , the UTC instant the engine expires the shelve; otherwise
/// null. Carried so the node's Part 9 ShelvingState.UnshelveTime reports the real remaining
-/// time instead of the SDK's ~24.8-day ceiling (Gitea #597). A timed snapshot without it (a caller that
+/// time instead of the SDK's ~24.8-day ceiling. A timed snapshot without it (a caller that
/// predates the field) falls back to that ceiling — the engine still owns the expiry either way.
///
public sealed record AlarmConditionSnapshot(
diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Commons/OpcUa/DeferredAddressSpaceSink.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Commons/OpcUa/DeferredAddressSpaceSink.cs
index 0c592bb1..4ee8e917 100644
--- a/src/Core/ZB.MOM.WW.OtOpcUa.Commons/OpcUa/DeferredAddressSpaceSink.cs
+++ b/src/Core/ZB.MOM.WW.OtOpcUa.Commons/OpcUa/DeferredAddressSpaceSink.cs
@@ -44,6 +44,7 @@ public sealed class DeferredAddressSpaceSink : IOpcUaAddressSpaceSink, ISurgical
public void WriteAlarmCondition(string alarmNodeId, AlarmConditionSnapshot state, DateTime sourceTimestampUtc, AddressSpaceRealm realm)
=> _inner.WriteAlarmCondition(alarmNodeId, state, sourceTimestampUtc, realm);
+ ///
public void WriteAlarmQuality(string alarmNodeId, uint statusCode, DateTime sourceTimestampUtc, AddressSpaceRealm realm)
=> _inner.WriteAlarmQuality(alarmNodeId, statusCode, sourceTimestampUtc, realm);
@@ -52,7 +53,7 @@ public sealed class DeferredAddressSpaceSink : IOpcUaAddressSpaceSink, ISurgical
=> _inner.MaterialiseAlarmCondition(alarmNodeId, equipmentNodeId, displayName, alarmType, severity, realm, isNative);
///
- // Forward the WP4 multi-notifier wiring to the inner sink. Without this the native-alarm fan-out to the
+ // Forward the multi-notifier wiring to the inner sink. Without this the native-alarm fan-out to the
// referencing equipment folders ships INERT on every driver-role host (actors inject THIS wrapper, not the
// inner SdkAddressSpaceSink) — the F10b / PR#423 forwarding trap the reflection guard exists to catch.
public void WireAlarmNotifiers(string alarmNodeId, AddressSpaceRealm alarmRealm, IReadOnlyList notifierFolderNodeIds, AddressSpaceRealm notifierFolderRealm)
@@ -100,7 +101,7 @@ public sealed class DeferredAddressSpaceSink : IOpcUaAddressSpaceSink, ISurgical
=> _inner is ISurgicalAddressSpaceSink surgical
&& surgical.UpdateFolderDisplayName(folderNodeId, displayName, realm);
- // R2-07 T7 — surgical remove forwards. Same capability-sniffing pattern as UpdateTagAttributes /
+ // Surgical remove forwards. Same capability-sniffing pattern as UpdateTagAttributes /
// UpdateFolderDisplayName: forward when the inner sink supports the surgical capability; return false
// otherwise (before the real SdkAddressSpaceSink is swapped in, or any non-surgical inner) so the caller
// (AddressSpaceApplier) falls back to a full rebuild. Without these forwards the surgical remove path is
diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Commons/OpcUa/IOpcUaAddressSpaceSink.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Commons/OpcUa/IOpcUaAddressSpaceSink.cs
index 498cd59e..7c69c314 100644
--- a/src/Core/ZB.MOM.WW.OtOpcUa.Commons/OpcUa/IOpcUaAddressSpaceSink.cs
+++ b/src/Core/ZB.MOM.WW.OtOpcUa.Commons/OpcUa/IOpcUaAddressSpaceSink.cs
@@ -11,14 +11,14 @@ public interface IOpcUaAddressSpaceSink
/// Write a Variable node's current value + status code + source timestamp.
/// The OPC UA node ID of the variable.
/// The value to write.
- /// The full OPC UA status code, sub-code included. Passed to the SDK verbatim
- /// (#519): a driver's reaches the client as
+ /// The full OPC UA status code, sub-code included. Passed to the SDK verbatim, so
+ /// a driver's reaches the client as
/// BadNoCommunication, not as a generic . Use
/// ' named constants rather than a literal.
/// The source timestamp in UTC.
/// Which of the two v3 namespaces ( device tree or
/// equipment tree) the lives in — the sink
- /// resolves the namespace index from the realm rather than parsing it out of the id string. WP3's fan-out
+ /// resolves the namespace index from the realm rather than parsing it out of the id string. The fan-out
/// calls this once per NodeId (raw + every referencing UNS node) with the matching realm.
void WriteValue(string nodeId, object? value, uint statusCode, DateTime sourceTimestampUtc, AddressSpaceRealm realm);
@@ -81,11 +81,11 @@ public interface IOpcUaAddressSpaceSink
/// the condition was materialised under).
void WriteAlarmCondition(string alarmNodeId, AlarmConditionSnapshot state, DateTime sourceTimestampUtc, AddressSpaceRealm realm);
- /// #477 — annotate a materialised condition's source-data quality OUT OF BAND from any alarm
+ /// Annotate a materialised condition's source-data quality OUT OF BAND from any alarm
/// transition (used by the driver-connectivity path: comms lost → ,
/// restored → ). Sets ONLY the condition's Quality — never
/// Active/Acked/Severity/Retain (a comms-lost active alarm stays active) — and fires one Part 9 event
- /// only on a change to the status code itself — since #519 that includes a move between two Bad
+ /// only on a change to the status code itself — that now includes a move between two Bad
/// sub-codes, which genuinely changes what a subscriber reads. A no-op for an unmaterialised /
/// non-condition node.
/// The condition node id (RawPath for a native alarm).
@@ -172,9 +172,9 @@ public interface IOpcUaAddressSpaceSink
/// The namespace realm the variable (and its ) live
/// in. A historized UNS reference node registers the SAME as its backing
/// raw node — both NodeIds map to one tagname — so this call carries the shared tagname per realm.
- /// Gitea #610 review (R6): when true and the node is SCALAR, a client write
+ /// When true and the node is SCALAR, a client write
/// of an array value is refused inline (BadTypeMismatch, nothing dispatched) because the owning
- /// driver's scalar encoder cannot take one (Modbus / S7). False (default) keeps the pre-#610 behaviour — the
+ /// driver's scalar encoder cannot take one (Modbus / S7). False (default) keeps the earlier behaviour — the
/// write is dispatched and the driver decides (OpcUaClient forwards it to the upstream server, Galaxy encodes
/// it). Fixed at creation; an existing node keeps the value it was created with.
void EnsureVariable(string variableNodeId, string? parentFolderNodeId, string displayName, string dataType, bool writable, AddressSpaceRealm realm, string? historianTagname = null, bool isArray = false, uint? arrayLength = null, bool refuseArrayOnScalar = false);
@@ -186,7 +186,7 @@ public interface IOpcUaAddressSpaceSink
void RebuildAddressSpace();
///
- /// Gitea #641 — mark the END of one address-space apply pass (after , if
+ /// Mark the END of one address-space apply pass (after , if
/// one ran, and every materialise / notifier-wiring call that followed it). An SDK-backed sink carries each
/// native condition's live Part 9 state across a rebuild and restores it when the same condition is
/// re-materialised; this is where it reports the ONE condition event per restored condition (now that
@@ -229,7 +229,7 @@ public interface IOpcUaAddressSpaceSink
}
/// One value write within a batch.
-/// Same semantics per field as (#519: status verbatim).
+/// Same semantics per field as (status verbatim).
public readonly record struct ValueWrite(
string NodeId, object? Value, uint StatusCode, DateTime SourceTimestampUtc, AddressSpaceRealm Realm);
@@ -237,7 +237,7 @@ public readonly record struct ValueWrite(
/// node's current VALUE is retained and only +
/// change.
/// The OPC UA node id of the variable, within .
-/// The full status code, sub-code included, passed to the SDK verbatim (#519).
+/// The full status code, sub-code included, passed to the SDK verbatim.
/// The source timestamp in UTC.
/// Which of the two v3 namespaces lives in.
public readonly record struct StatusWrite(
@@ -279,6 +279,7 @@ public sealed class NullOpcUaAddressSpaceSink : IOpcUaAddressSpaceSink
///
public void WriteAlarmCondition(string alarmNodeId, AlarmConditionSnapshot state, DateTime sourceTimestampUtc, AddressSpaceRealm realm) { }
+ ///
public void WriteAlarmQuality(string alarmNodeId, uint statusCode, DateTime sourceTimestampUtc, AddressSpaceRealm realm) { }
///
diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Commons/OpcUa/OpcUaStatus.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Commons/OpcUa/OpcUaStatus.cs
index f2797331..5e7352b6 100644
--- a/src/Core/ZB.MOM.WW.OtOpcUa.Commons/OpcUa/OpcUaStatus.cs
+++ b/src/Core/ZB.MOM.WW.OtOpcUa.Commons/OpcUa/OpcUaStatus.cs
@@ -2,7 +2,7 @@ namespace ZB.MOM.WW.OtOpcUa.Commons.OpcUa;
///
/// The handful of OPC UA status codes the engine layer needs to spell, plus the Part 4 severity
-/// classification helpers that replaced the retired OpcUaQuality 3-state enum (Gitea #519).
+/// classification helpers that replaced the retired OpcUaQuality 3-state enum.
///
/// Why these are bare uint literals. Commons and Runtime deliberately do not
/// reference the OPC UA SDK — exists precisely so the Runtime
@@ -10,11 +10,11 @@ namespace ZB.MOM.WW.OtOpcUa.Commons.OpcUa;
/// decision the driver assemblies make, and it carries the same single failure mode: nothing checks
/// the literal against the name. StatusCodeParityTests closes it by reflection, and it can
/// only see a named constant — so add codes here rather than writing a literal at a call
-/// site, exactly as Gitea #497 concluded for the drivers.
+/// site, exactly as for the drivers.
///
/// Severity, not equality, is what callers should branch on. A status code's top two
/// bits carry its Part 4 severity (00 = Good, 01 = Uncertain, 10/11 = Bad); everything below is the
-/// sub-code. Before #519 the engine collapsed every value onto a 3-state enum here and the node
+/// sub-code. Previously the engine collapsed every value onto a 3-state enum here and the node
/// manager re-expanded Bad to a bare 0x80000000, so no driver's sub-code ever reached
/// a client. The sub-code now rides through to the SDK verbatim, and any code that only needs the
/// bucket uses / / rather than
diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Configuration/Entities/ApiKey.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Configuration/Entities/ApiKey.cs
index b7c32a2f..90b33a12 100644
--- a/src/Core/ZB.MOM.WW.OtOpcUa.Configuration/Entities/ApiKey.cs
+++ b/src/Core/ZB.MOM.WW.OtOpcUa.Configuration/Entities/ApiKey.cs
@@ -1,7 +1,7 @@
namespace ZB.MOM.WW.OtOpcUa.Configuration.Entities;
///
-/// One OPC UA data-plane API key. The OPC UA data plane is machine-to-machine (#520), so a
+/// One OPC UA data-plane API key. The OPC UA data plane is machine-to-machine, so a
/// consuming system authenticates with a key rather than an LDAP user, and the key resolves to a
/// that rows grant permissions to.
///
@@ -22,7 +22,7 @@ namespace ZB.MOM.WW.OtOpcUa.Configuration.Entities;
/// ⚠️ Revocation requires a deploy. Phase 4 leaves driver-only nodes with no ConfigDb, so
/// keys reach them inside the deployment artifact exactly as rows do.
/// Setting to false takes effect on a node only once a new artifact reaches
-/// it. This is an operational sharp edge, not an oversight — see the #520 design doc.
+/// it. This is an operational sharp edge, not an oversight.
///
///
public sealed class ApiKey
diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Configuration/OtOpcUaConfigDbContext.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Configuration/OtOpcUaConfigDbContext.cs
index 166345fb..c45161e4 100644
--- a/src/Core/ZB.MOM.WW.OtOpcUa.Configuration/OtOpcUaConfigDbContext.cs
+++ b/src/Core/ZB.MOM.WW.OtOpcUa.Configuration/OtOpcUaConfigDbContext.cs
@@ -41,7 +41,7 @@ public sealed class OtOpcUaConfigDbContext(DbContextOptionsGets the DbSet of node ACLs.
public DbSet NodeAcls => Set();
- /// OPC UA data-plane API keys (#520). One principal per key; secret stored as a peppered hash.
+ /// OPC UA data-plane API keys. One principal per key; secret stored as a peppered hash.
public DbSet ApiKeys => Set();
/// Gets the DbSet of configuration audit logs.
public DbSet ConfigAuditLogs => Set();
@@ -563,14 +563,14 @@ public sealed class OtOpcUaConfigDbContext(DbContextOptions ScriptedAlarms { get; init; } = [];
/// User-authored scripts (shared by VirtualTags, ScriptedAlarms, and Calculation tags). Drives
- /// the WP7 Calculation gates: scriptId existence + the calc→calc dependency-cycle check.
+ /// the Calculation gates: scriptId existence + the calc→calc dependency-cycle check.
public IReadOnlyList