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
@@ -470,7 +470,7 @@ VALUES
// a transient fault. That replay is safe: every step is IF-EXISTS / IF-NOT-EXISTS
// guarded and the staging table is GUID-suffixed, so a re-run is idempotent.
//
// ALIGNED UNIQUENESS (WP2.2): there is no longer an index drop/rebuild around
// ALIGNED UNIQUENESS: there is no longer an index drop/rebuild around
// the SWITCH. EventId uniqueness is now enforced solely by the clustered
// PK_AuditLog (EventId, OccurredAtUtc), which is partition-aligned on
// ps_AuditLog_Month(OccurredAtUtc) — so ALTER TABLE … SWITCH PARTITION has no
@@ -590,16 +590,27 @@ public class DeploymentManagerRepository : IDeploymentManagerRepository
/// </summary>
private sealed class DeploymentInstanceJoin
{
/// <summary>Deployment record primary key.</summary>
public int Id { get; init; }
/// <summary>Unique deployment identifier.</summary>
public string DeploymentId { get; init; } = string.Empty;
/// <summary>Primary key of the target instance.</summary>
public int InstanceId { get; init; }
/// <summary>Unique name of the target instance.</summary>
public string InstanceUniqueName { get; init; } = string.Empty;
/// <summary>Primary key of the instance's site.</summary>
public int SiteId { get; init; }
/// <summary>Current deployment status.</summary>
public DeploymentStatus Status { get; init; }
/// <summary>Revision hash of the deployed configuration, if any.</summary>
public string? RevisionHash { get; init; }
/// <summary>User who initiated the deployment.</summary>
public string DeployedBy { get; init; } = string.Empty;
/// <summary>When the deployment was initiated.</summary>
public DateTimeOffset DeployedAt { get; init; }
/// <summary>When the deployment completed, if it has.</summary>
public DateTimeOffset? CompletedAt { get; init; }
/// <summary>Failure message, if the deployment failed.</summary>
public string? ErrorMessage { get; init; }
}
}
@@ -129,7 +129,7 @@ public sealed class KpiHistoryRepository : IKpiHistoryRepository
s.Source, s.Metric, s.Scope, s.ScopeKey, TruncateToHour(s.CapturedAtUtc)));
// Preload every rollup row already covering this window in ONE query and
// index it by series+hour (WP2.2). The predecessor issued a
// index it by series+hour. The predecessor issued a
// FirstOrDefaultAsync existence probe PER (series, hour) group — an N+1
// that scaled with the metric catalogue times the lookback: a 3 h re-fold
// over ~40 series cost ~120 sequential round trips before a single row was
@@ -172,8 +172,8 @@ public sealed class KpiHistoryRepository : IKpiHistoryRepository
// That invariant used to be an unenforced assumption across three
// independently-owned config keys (KpiHistoryOptions.SampleInterval,
// NotificationOutboxOptions.DeliveredKpiWindow, SiteCallAuditOptions.KpiInterval)
// — widening one silently over/under-counted the rollup (arch-review P5-5, Low).
// As of P5-5, NotificationOutboxKpiSampleSource / SiteCallAuditKpiSampleSource
// — widening one silently over/under-counted the rollup.
// NotificationOutboxKpiSampleSource / SiteCallAuditKpiSampleSource
// rescale their Rate-classified samples onto SampleInterval before recording, so
// this Sum is correct here regardless of how the other two keys are tuned; a
// boot-time cross-validator (Host's KpiRateFoldIntervalCrossValidator) still
@@ -140,7 +140,7 @@ VALUES
public async Task<IReadOnlyList<Notification>> GetDueAsync(
DateTimeOffset now, int batchSize, CancellationToken cancellationToken = default)
{
// AsNoTracking (WP2.2): the dispatcher mutates each row's delivery state in
// AsNoTracking: the dispatcher mutates each row's delivery state in
// memory and persists it through UpdateAsync, which is now a targeted
// server-side ExecuteUpdate and needs no change tracker. Tracking a batch
// of notifications — each carrying an nvarchar(max) Body and TypeData —
@@ -162,8 +162,8 @@ VALUES
{
ArgumentNullException.ThrowIfNull(n);
// Targeted server-side UPDATE of the seven mutable delivery-state columns
// (WP2.2). The predecessor called DbSet.Update(n) + SaveChanges, which
// Targeted server-side UPDATE of the seven mutable delivery-state columns.
// The predecessor called DbSet.Update(n) + SaveChanges, which
// marks EVERY property modified and rewrites all 21 columns — including
// the immutable nvarchar(max) Body/TypeData payloads — on every single
// delivery attempt. ExecuteUpdate also bypasses the change tracker
@@ -212,7 +212,7 @@ VALUES
// Same seven mutable delivery-state columns as UpdateAsync, but the WHERE
// clause below ALSO requires the row's current Status to still be Parked
// -- a single-statement compare-and-swap (arch-review 2026-08-19 P5-4).
// -- a single-statement compare-and-swap.
// Two round trips (GetByIdAsync then this write) with a status check in
// between is not a correctness guard by itself: a detached PipeTo task
// can race the dispatcher's own write (or a second concurrent operator
@@ -265,7 +265,7 @@ VALUES
public async Task<(IReadOnlyList<Notification> Rows, int TotalCount)> QueryAsync(
NotificationOutboxFilter filter, int pageNumber, int pageSize, CancellationToken cancellationToken = default)
{
// AsNoTracking (WP2.2): this is the Central UI's read-only list page. The
// AsNoTracking: this is the Central UI's read-only list page. The
// rows are projected to the wire and never saved, so tracking them cost a
// snapshot copy of every nvarchar(max) Body in the page for nothing.
var query = _context.Notifications.AsNoTracking().AsQueryable();
@@ -347,7 +347,7 @@ VALUES
{
// Time-sliced batches: each DELETE covers at most one hour of terminal rows,
// capping the lock/log footprint per statement — mirrors
// KpiHistoryRepository.PurgeOlderThanAsync (arch-review WP1.4; an unbounded
// KpiHistoryRepository.PurgeOlderThanAsync (an unbounded
// single-statement ExecuteDeleteAsync would otherwise turn a long-idle catch-up
// of the default 365-day window into one giant transaction against
// dbo.Notifications). Pure EF LINQ (no raw SQL) so this stays portable across
@@ -381,14 +381,14 @@ VALUES
// each metric is a COUNT(CASE WHEN <predicate> THEN 1 END) over the same scan
// (arch-review 04). GroupBy(_ => 1) yields a single group (no rows → no group).
//
// WP2.2 — the aggregation is now PREDICATE-RESTRICTED instead of scanning the
// The aggregation is PREDICATE-RESTRICTED instead of scanning the
// whole table. Every KPI here is about the live queue (Pending/Retrying), the
// parked backlog, or the last delivery interval; the overwhelming bulk of the
// Notifications table is historical Delivered and Discarded rows that
// contribute to NONE of them. The pre-filter below is the union of the
// metric-contributing predicates, which lets the optimizer seek the status
// index for the live/parked legs and the filtered
// (DeliveredAt) WHERE Status='Delivered' index (WP1.4) for the interval leg,
// (DeliveredAt) WHERE Status='Delivered' index for the interval leg,
// instead of paying a full scan whose cost grows with retained history.
// Mirrors the pre-filter ComputePerSiteKpisAsync/ComputePerNodeKpisAsync
// already use — the global snapshot was the odd one out.
@@ -125,8 +125,7 @@ WHERE Id = {id}
// A blind full-row UpdateAsync here used to be able to land AFTER a concurrent
// approve had won its own CAS and while its multi-second WriteTagAsync relay was
// still in flight — stamping the row Rejected even though the plant write was
// physically executing, i.e. falsifying the two-person-control record
// (arch-review 2026-08-19 P6-1). The conditional WHERE Status='Pending' makes
// physically executing, i.e. falsifying the two-person-control record. The conditional WHERE Status='Pending' makes
// reject win only from Pending, so approve and reject can never both transition
// one row. Only the columns the reject transition owns are written.
var rowsAffected = await _context.Database.ExecuteSqlInterpolatedAsync(
@@ -74,7 +74,7 @@ public class SiteCallAuditRepository : ISiteCallAuditRepository
var incomingRank = GetRankOrThrow(siteCall.Status);
// ONE round trip, INSERT-first. Both statements ship in a single command
// text, so the round-trip saving of the WP2.2 rewrite is preserved — but
// text, so the round-trip saving of the single-command rewrite is preserved — but
// the ORDER is back to insert-then-update, because UPDATE-first LOSES
// DATA under a concurrent first-write.
//
@@ -283,8 +283,7 @@ WHERE TrackedOperationId = @Id
throw new ArgumentNullException(nameof(paging));
}
// Raw SQL with NAMED parameters (not FromSqlInterpolated) — arch-review
// 2026-08-19 P5-2. FromSqlInterpolated binds every interpolated string
// Raw SQL with NAMED parameters (not FromSqlInterpolated). FromSqlInterpolated binds every interpolated string
// value as an nvarchar(4000) parameter by default; every SiteCalls string
// column is IsUnicode(false) (varchar), so those parameters forced an
// implicit CONVERT on the column side of each comparison and blocked
@@ -175,32 +175,27 @@ public class SiteRepository : ISiteRepository
.ToListAsync(cancellationToken);
}
// Rationale for the watermark bump:
// Bumps the ITemplateGraphWatermark after a successful commit
// that touched a DataConnection.
//
// A data connection's Protocol, primary/backup configuration and
// failover retry count are flattening inputs — FlatteningService
// packages them into the flattened config's Connections map and
// RevisionHashService folds them into the revision hash. Without a bump,
// the process-wide StaleInstanceProbe memo and the flatten-session
// caches keep reporting the pre-edit hash for every instance bound to the
// connection, so an instance whose deployed config genuinely drifted reads as
// up to date.
//
// The bump is ITemplateGraphWatermark.BumpAll rather than a
// per-template one because a connection has no owning template: the affected
// set is "every instance at the site bound to it", which is exactly what the
// unattributed fallback is for. Applied only AFTER a successful commit, so a
// failed save does not invalidate caches for a change that never landed.
// Sites, areas and instances saved here are left alone — only the connection
// rows feed the flattener.
/// <inheritdoc />
/// <remarks>
/// Bumps the <see cref="ITemplateGraphWatermark"/> after a successful commit
/// that touched a <see cref="DataConnection"/>.
///
/// <para>
/// A data connection's <c>Protocol</c>, primary/backup configuration and
/// failover retry count are flattening inputs — <c>FlatteningService</c>
/// packages them into the flattened config's <c>Connections</c> map and
/// <c>RevisionHashService</c> folds them into the revision hash. Without a bump,
/// the process-wide <c>StaleInstanceProbe</c> memo and the flatten-session
/// caches keep reporting the pre-edit hash for every instance bound to the
/// connection, so an instance whose deployed config genuinely drifted reads as
/// up to date.
/// </para>
///
/// <para>
/// The bump is <see cref="ITemplateGraphWatermark.BumpAll"/> rather than a
/// per-template one because a connection has no owning template: the affected
/// set is "every instance at the site bound to it", which is exactly what the
/// unattributed fallback is for. Applied only AFTER a successful commit, so a
/// failed save does not invalidate caches for a change that never landed.
/// Sites, areas and instances saved here are left alone — only the connection
/// rows feed the flattener.
/// </para>
/// </remarks>
public async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
{
// Inspect BEFORE committing: afterwards every entry is Unchanged and the
@@ -91,27 +91,25 @@ public class TemplateEngineRepository : ITemplateEngineRepository
}
/// <inheritdoc />
/// <remarks>
/// Two deliberate departures from <see cref="GetAllTemplatesAsync"/>:
/// <list type="number">
/// <item>
/// <c>AsNoTracking</c> — the analysis walks (cycle detection, collision
/// detection, canonical-name resolution) never write through the loaded
/// graph, so paying for a change-tracker snapshot of every attribute,
/// alarm, script and composition in the database is pure waste.
/// </item>
/// <item>
/// <c>TemplateScript.Code</c> is projected away (left empty). Script
/// bodies are by far the largest column in the graph and NONE of the
/// analysis consumers read them — <c>CycleDetector</c> reads only ids and
/// edges, <c>CollisionDetector</c> and <c>TemplateResolver</c> read only
/// member names and lock flags.
/// </item>
/// </list>
/// Callers that DO need bodies or tracked entities — the inheritance
/// reconciler and the flattener — must keep using
/// <see cref="GetAllTemplatesAsync"/> / <see cref="GetTemplateWithChildrenAsync"/>.
/// </remarks>
// Two deliberate departures from <see cref="GetAllTemplatesAsync"/>:
// <list type="number">
// <item>
// <c>AsNoTracking</c> — the analysis walks (cycle detection, collision
// detection, canonical-name resolution) never write through the loaded
// graph, so paying for a change-tracker snapshot of every attribute,
// alarm, script and composition in the database is pure waste.
// </item>
// <item>
// <c>TemplateScript.Code</c> is projected away (left empty). Script
// bodies are by far the largest column in the graph and NONE of the
// analysis consumers read them — <c>CycleDetector</c> reads only ids and
// edges, <c>CollisionDetector</c> and <c>TemplateResolver</c> read only
// member names and lock flags.
// </item>
// </list>
// Callers that DO need bodies or tracked entities — the inheritance
// reconciler and the flattener — must keep using
// <see cref="GetAllTemplatesAsync"/> / <see cref="GetTemplateWithChildrenAsync"/>.
public async Task<IReadOnlyList<Template>> GetAllTemplatesForAnalysisAsync(CancellationToken cancellationToken = default)
{
return await _context.Templates
@@ -772,13 +770,11 @@ public class TemplateEngineRepository : ITemplateEngineRepository
}
/// <inheritdoc />
/// <remarks>
/// Inspects the change tracker BEFORE committing (afterwards every entry is
/// <see cref="EntityState.Unchanged"/> and the attribution is lost) and bumps
/// the <see cref="ITemplateGraphWatermark"/> for each affected template /
/// instance. The bump is applied only AFTER a successful commit, so a failed
/// save does not invalidate caches for a change that never landed.
/// </remarks>
// Inspects the change tracker BEFORE committing (afterwards every entry is
// <see cref="EntityState.Unchanged"/> and the attribution is lost) and bumps
// the <see cref="ITemplateGraphWatermark"/> for each affected template /
// instance. The bump is applied only AFTER a successful commit, so a failed
// save does not invalidate caches for a change that never landed.
public async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
{
var pending = CollectWatermarkBumps();