docs(src): fix CommentChecker findings across src/ — 1,032 → 5 (all false positives)

Comment/XML-doc changes only, no code logic:
- strip project-tracking references (WP*, P*-*, row-NN, #NN, Task N) from
  comments, keeping the explanatory rationale
- add missing summaries, <param>, <returns>, <typeparam> and <inheritdoc/>
- replace misused <inheritdoc/> with real docs; where <inheritdoc/> was
  combined with useful remarks, move the rationale into // comments
- drop stale replicationActor <param> on DeploymentManagerActor ctor

Remaining 5 are false positives (CWE-1236 x2, failures-1, year-0001, M365).
Build 0 errors / 0 warnings.
This commit is contained in:
Joseph Doherty
2026-10-07 14:12:25 -04:00
parent 3a217b9484
commit 0ffd4bdbea
217 changed files with 1224 additions and 695 deletions
@@ -21,8 +21,8 @@ namespace ZB.MOM.WW.ScadaBridge.CentralUI.Services;
/// is hit the service appends a <c># Capped at … rows. Use the CLI for larger
/// exports.</c> footer line so an operator can tell a truncated download from
/// a complete one. The header row contains the same 24 columns, in the same
/// order, as the ManagementService CLI's CSV export (arch-review 2026-08-19
/// P6-7 — the two CSV writers were found to have drifted and are kept aligned).
/// order, as the ManagementService CLI's CSV export (the two CSV writers
/// had drifted and are kept aligned).
/// </para>
/// </summary>
public interface IAuditLogExportService
@@ -162,7 +162,7 @@ public sealed class AuditLogExportService : IAuditLogExportService
// ─────────────────────────────────────────────────────────────────────
// CSV helpers — 24 columns, kept aligned with the ManagementService CLI's
// CSV export (arch-review 2026-08-19 P6-7)
// CSV export
// ─────────────────────────────────────────────────────────────────────
/// <summary>The 24 column names, in <see cref="AuditEventView"/> declaration order.</summary>
@@ -179,8 +179,7 @@ public sealed class AuditLogExportService : IAuditLogExportService
/// cleanly on another. Free-text columns are run through
/// <see cref="NeedsFormulaNeutralization"/> before RFC 4180 quoting so a
/// leading <c>=</c>/<c>+</c>/<c>-</c>/<c>@</c>/TAB/CR can't be interpreted as
/// a formula or DDE trigger by a spreadsheet application (CWE-1236;
/// arch-review 2026-08-19 P6-7). GUID/enum/numeric/date/bool columns this
/// a formula or DDE trigger by a spreadsheet application (CWE-1236). GUID/enum/numeric/date/bool columns this
/// formatter generates itself are never neutralized.
/// </summary>
/// <param name="evt">The audit event view to format as a CSV row.</param>
@@ -70,6 +70,7 @@ public sealed class AuditLogQueryService : IAuditLogQueryService
/// </summary>
/// <param name="scopeFactory">Factory used to open a fresh DI scope per query.</param>
/// <param name="healthAggregator">Central health aggregator for KPI backlog data.</param>
/// <param name="auth">Optional authentication state provider used to resolve the current user.</param>
public AuditLogQueryService(
IServiceScopeFactory scopeFactory,
ICentralHealthAggregator healthAggregator, AuthenticationStateProvider? auth = null)
@@ -8,10 +8,13 @@ namespace ZB.MOM.WW.ScadaBridge.CentralUI.Services;
public sealed class AuthorizedManualFailoverService(AuthenticationStateProvider auth, IManualFailoverService backend)
{
/// <summary>Fails over central only for a currently authorized administrator.</summary>
/// <returns>The failover outcome message, or <c>null</c> when none was reported.</returns>
public async Task<string?> FailOverCentralAsync()
=> await backend.FailOverCentralAsync(await auth.RequireRoleAsync(Roles.Administrator));
/// <summary>Fails over a site only for a currently authorized administrator.</summary>
/// <param name="siteId">The site identifier to fail over.</param>
/// <returns>The outcome of the site failover.</returns>
public async Task<SiteFailoverOutcome> FailOverSiteAsync(string siteId)
=> await backend.FailOverSiteAsync(siteId, await auth.RequireRoleAsync(Roles.Administrator));
}
@@ -12,6 +12,7 @@ public enum BindingConnectionState { None, Single, Mixed }
/// <summary>A member of a <see cref="BindingGroup"/>: the row plus its tail relative to the group.</summary>
public sealed record BindingGroupMember(BindingRow Row, string MemberTail)
{
/// <summary>True when the row has no bound (or a non-positive) connection id.</summary>
public bool IsUnbound => Row.ConnectionId is null or <= 0;
/// <summary>Effective reference: override if set, else the default.</summary>
public string? EffectiveReference => string.IsNullOrWhiteSpace(Row.Override) ? Row.DefaultReference : Row.Override;
@@ -32,6 +33,7 @@ public sealed record BindingGroup(
int? CommonConnectionId,
int UnboundCount)
{
/// <summary>True when this group holds the instance's direct (non-module) attributes.</summary>
public bool IsInstanceGroup => Key == BindingGroupBuilder.InstanceGroupKey;
}
@@ -44,12 +46,14 @@ public static class BindingGroupBuilder
{
public const string InstanceGroupKey = "(instance)";
/// <summary>Groups the data-sourced rows by module and infers each group's shared connection and object prefix.</summary>
/// <param name="rows">The data-sourced flattened attributes with their current binding state.</param>
/// <param name="knownConnectionIds">
/// The site's data connection ids. When supplied, a row whose connection id is not
/// in the set is treated as UNBOUND — a binding to a deleted connection renders as
/// "— none —" and the deploy gate rejects it, so the pills must agree.
/// </param>
/// <returns>One <see cref="BindingGroup"/> per module (or the instance group).</returns>
public static IReadOnlyList<BindingGroup> Build(IEnumerable<BindingRow> rows, IReadOnlyCollection<int>? knownConnectionIds = null)
{
if (knownConnectionIds is not null)
@@ -153,6 +157,10 @@ public static class BindingGroupBuilder
/// Save path is an upsert and a removal could not persist anyway. Surrounding
/// whitespace and a trailing '.' are tolerated.
/// </summary>
/// <param name="group">The group whose members are rewritten.</param>
/// <param name="connectionId">The connection to bind, or null to keep each member's current connection.</param>
/// <param name="objectPath">The object path prefix, or blank to clear the overrides.</param>
/// <returns>One change per group member.</returns>
public static IReadOnlyList<BindingChange> ApplyObject(BindingGroup group, int? connectionId, string? objectPath)
{
var obj = (objectPath ?? string.Empty).Trim().TrimEnd('.');
@@ -12,6 +12,11 @@ namespace ZB.MOM.WW.ScadaBridge.CentralUI.Services;
public sealed class ConfigurationCommandService(IServiceProvider services, AuthenticationStateProvider auth)
{
/// <summary>Runs an existing audited instance operation with fresh scope and an atomic audit commit.</summary>
/// <typeparam name="T">The result type of the operation.</typeparam>
/// <param name="instanceId">The existing instance id, or null when creating.</param>
/// <param name="createSiteId">The target site id when creating an instance.</param>
/// <param name="operation">The operation to run, given the instance service and the acting username.</param>
/// <returns>The operation's result.</returns>
public async Task<T> ExecuteInstanceAsync<T>(int? instanceId, int? createSiteId,
Func<ZB.MOM.WW.ScadaBridge.TemplateEngine.Services.InstanceService, string, Task<T>> operation)
{
@@ -36,6 +41,8 @@ public sealed class ConfigurationCommandService(IServiceProvider services, Authe
}
/// <summary>Executes one command through the same guarded boundary as the management actor.</summary>
/// <param name="command">The configuration command to execute.</param>
/// <returns>The command's result, if any.</returns>
public async Task<object?> ExecuteAsync(object command)
{
var principal = (await auth.GetAuthoritativeStateAsync()).User;
@@ -52,6 +59,9 @@ public sealed class ConfigurationCommandService(IServiceProvider services, Authe
public static class ConfigurationCommandExtensions
{
/// <summary>Submits a database-only configuration mutation with current server-side authority.</summary>
/// <param name="services">The service provider used to resolve the command service.</param>
/// <param name="command">The configuration command to execute.</param>
/// <returns>The command's result, if any.</returns>
public static Task<object?> ExecuteConfigurationAsync(this IServiceProvider services, object command)
=> services.GetRequiredService<ConfigurationCommandService>().ExecuteAsync(command);
}
@@ -15,7 +15,7 @@ namespace ZB.MOM.WW.ScadaBridge.CentralUI.Services;
/// NAME keys back to <c>DataConnection.Id</c> using the same site repository the
/// design page loads from. Health reports key connections by <c>Name</c>, the
/// page renders nodes by id, so the join is per-site Name → Id; doing it per-site
/// avoids cross-site name collisions (two sites can each own a "PLC-1").
/// avoids cross-site name collisions (two sites can each own a "MainPlc").
/// </remarks>
public sealed class ConnectionHealthQueryService : IConnectionHealthQueryService
{
@@ -59,6 +59,8 @@ public sealed class FlowDebugService : IFlowDebugService
/// the run itself at <c>timeoutSeconds + RunGrace</c> (see <c>FlowDebugRunner.RunMethodAsync</c>),
/// so the client must wait at least that long or it cuts the run off before the server does.
/// </summary>
/// <param name="timeoutSeconds">The method's own timeout in seconds.</param>
/// <returns>The Ask timeout to use for the run.</returns>
public static TimeSpan RunAskTimeoutFor(int timeoutSeconds)
{
var t = TimeSpan.FromSeconds(Math.Max(1, timeoutSeconds)) + FlowDebugRunner.RunGrace + ClientSlack;
@@ -69,6 +71,8 @@ public sealed class FlowDebugService : IFlowDebugService
/// Client-side Ask wait for a WRITE-kind script run (<see cref="RunScriptAsync"/>): the
/// trigger-wait window plus <see cref="ClientSlack"/>.
/// </summary>
/// <param name="waitSeconds">The trigger-wait window in seconds.</param>
/// <returns>The Ask timeout to use for the run.</returns>
public static TimeSpan ValueChangeAskTimeoutFor(int waitSeconds)
=> TimeSpan.FromSeconds(Math.Max(1, waitSeconds)) + ClientSlack;
@@ -110,6 +114,14 @@ public sealed class FlowDebugService : IFlowDebugService
IReadOnlyList<FlowScriptOption> Options);
/// <summary>Initializes the service.</summary>
/// <param name="holder">Holder for the management actor reference.</param>
/// <param name="auth">Provides the caller's authentication state.</param>
/// <param name="inboundApi">Repository for inbound API methods.</param>
/// <param name="templates">Repository for templates and instances.</param>
/// <param name="pipeline">Flattening pipeline used to resolve instance scripts.</param>
/// <param name="comms">Communication service for central-to-site requests.</param>
/// <param name="logger">Logger for diagnostics.</param>
/// <param name="sites">Repository for sites.</param>
public FlowDebugService(
ManagementActorHolder holder,
AuthenticationStateProvider auth,
@@ -44,7 +44,7 @@ public interface IAlarmSummaryService
/// <summary>
/// Builds an <see cref="AlarmSummaryResult"/> directly from an in-memory live
/// snapshot (plan #10, Task 5) — the current alarms served by
/// snapshot — the current alarms served by
/// <see cref="ZB.MOM.WW.ScadaBridge.Communication.ISiteAlarmLiveCache.GetCurrentAlarms"/>
/// — instead of fanning out per-instance snapshot Asks. Each
/// <see cref="AlarmStateChanged"/> is flattened to one
@@ -28,6 +28,9 @@ public sealed record FlowDebugOutcome(
public bool Success => Graph != null && Error == null;
/// <summary>Creates a failed outcome.</summary>
/// <param name="error">The error message.</param>
/// <param name="errorCode">The machine-readable error code.</param>
/// <returns>A failed outcome carrying the error.</returns>
public static FlowDebugOutcome Fail(string error, string errorCode) => new(null, null, error, errorCode);
}
@@ -83,18 +86,21 @@ public interface IFlowDebugService
/// <summary>The instance's scripts from its flattened configuration, ordered by canonical name; empty when the instance does not exist.</summary>
/// <param name="instanceId">The instance id.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>The instance's scripts ordered by canonical name.</returns>
Task<IReadOnlyList<FlowScriptOption>> GetInstanceScriptsAsync(int instanceId, CancellationToken cancellationToken = default);
/// <summary>Predicts the flow of running <paramref name="scriptName"/>: the write + spawned script for a write kind, a RouteCall root + the script for a call kind.</summary>
/// <param name="instanceId">The instance id.</param>
/// <param name="scriptName">Canonical script name (case-insensitive).</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>The predicted flow for the script.</returns>
Task<PredictedFlow> PredictScriptAsync(int instanceId, string scriptName, CancellationToken cancellationToken = default);
/// <summary>Runs an inbound API method with tracing on the active central node and returns the flow.</summary>
/// <param name="apiMethodId">The method id.</param>
/// <param name="parameters">Flat JSON object of parameters, or null.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>The outcome of the run, including the built flow graph.</returns>
Task<FlowDebugOutcome> RunMethodAsync(int apiMethodId, JsonElement? parameters, CancellationToken cancellationToken = default);
/// <summary>Runs a script by name (write kinds take <paramref name="value"/>, call kinds <paramref name="parameters"/>) and returns the flow.</summary>
@@ -103,11 +109,13 @@ public interface IFlowDebugService
/// <param name="parameters">Call kinds: flat JSON object of parameters, or null.</param>
/// <param name="value">Write kinds: the new value for the trigger's attribute, in canonical string form.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>The outcome of the run, including the built flow graph.</returns>
Task<FlowDebugOutcome> RunScriptAsync(int instanceId, string scriptName, JsonElement? parameters, string? value, CancellationToken cancellationToken = default);
/// <summary>Rebuilds a past flow from the central audit store.</summary>
/// <param name="executionId">Any execution id in the chain.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>The outcome of the replay, including the rebuilt flow graph.</returns>
Task<FlowDebugOutcome> ReplayAsync(Guid executionId, CancellationToken cancellationToken = default);
/// <summary>
@@ -120,6 +128,7 @@ public interface IFlowDebugService
/// </summary>
/// <param name="step">The outbound step to retry.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>The result of the retry request.</returns>
Task<FlowOutboundActionResult> RetryOutboundAsync(FlowStep step, CancellationToken cancellationToken = default);
/// <summary>
@@ -128,5 +137,6 @@ public interface IFlowDebugService
/// </summary>
/// <param name="step">The outbound step to discard.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>The result of the discard request.</returns>
Task<FlowOutboundActionResult> DiscardOutboundAsync(FlowStep step, CancellationToken cancellationToken = default);
}
@@ -6,7 +6,7 @@ namespace ZB.MOM.WW.ScadaBridge.CentralUI.Services;
/// <summary>
/// Process-level memoized point-in-time KPI snapshots shared by every Blazor circuit on
/// this node (arch-review WP2.4, finding #8 — the per-circuit polling half).
/// this node (the per-circuit polling half).
/// <para>
/// Every KPI here is a <em>global, point-in-time aggregate</em>: it does not vary by user,
/// site scope, or role, so N circuits asking for it inside one refresh window are asking
@@ -13,6 +13,9 @@ public sealed class PollGate
public bool TryEnter() => Interlocked.CompareExchange(ref _inFlight, 1, 0) == 0;
/// <summary>Runs and observes a posted refresh, releasing the gate even if dispatch is rejected.</summary>
/// <param name="dispatch">Delegate that posts the refresh.</param>
/// <param name="onError">Callback invoked with any exception thrown by <paramref name="dispatch"/>.</param>
/// <returns>A task that represents the asynchronous operation.</returns>
public async Task RunAsync(Func<Task> dispatch, Action<Exception> onError)
{
if (!TryEnter()) return;
@@ -6,7 +6,7 @@ using ZB.MOM.WW.ScadaBridge.Communication;
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Services;
/// <summary>
/// Process-level memoizing façade over <see cref="AlarmSummaryService"/> (arch-review WP2.4).
/// Process-level memoizing façade over <see cref="AlarmSummaryService"/>.
/// <para>
/// The Alarm Summary page polls every 15s <em>per circuit</em>, and each poll fans one debug
/// snapshot Ask out to every Enabled instance on the site. Ten operators watching one site
@@ -17,6 +17,9 @@ public static class TemplatePathFormatter
/// Builds the display path for <paramref name="template"/> by walking its
/// folder chain upward through <paramref name="foldersById"/>.
/// </summary>
/// <param name="template">The template whose path is built.</param>
/// <param name="foldersById">Folders keyed by id, used to walk the parent chain.</param>
/// <returns>The folder-qualified display path.</returns>
public static string Format(Template template, IReadOnlyDictionary<int, TemplateFolder> foldersById)
{
var parts = new List<string> { template.Name };