docs(comments): strip internal task/milestone/bundle bookkeeping from code comments

Remove project bookkeeping citations from shipped code comments across the
solution: hyphenated task IDs (WP-14, StoreAndForward-025), milestone/task/
issue refs (M3, Task 4, Audit Log #23, #21), Bundle X task-bundle labels,
and C/D/K/S/T phase labels.

Comment text only — no code logic, string/log literals, or XML-doc structure
changed. Genuine descriptions are preserved (only the citation is stripped),
and technical lookalikes are retained (UTF-8, SHA-256, T00:00:00, M365,
UTC-5, pre-C4/pre-C5 schema versions). Flagged by the new CommentChecker
TaskReferenceInComment / TrackingReferenceInComment checks plus targeted
grep passes; full solution builds clean, append-only guard tests pass.
This commit is contained in:
Joseph Doherty
2026-07-07 11:03:26 -04:00
parent 67005ca4c0
commit 9cff87fe85
435 changed files with 2338 additions and 2547 deletions
@@ -12,7 +12,7 @@ using ZB.MOM.WW.ScadaBridge.StoreAndForward;
namespace ZB.MOM.WW.ScadaBridge.ExternalSystemGateway;
/// <summary>
/// WP-9: Database access from scripts.
/// Database access from scripts.
/// Database.Connection("name") — returns ADO.NET SqlConnection (connection pooling).
/// Database.CachedWrite("name", "sql", params) — submits to S&amp;F engine.
/// </summary>
@@ -58,7 +58,7 @@ public class DatabaseGateway : IDatabaseGateway
{
// OpenAsync failed (unreachable server, bad credentials, cancellation) —
// dispose the just-created connection before the exception propagates so
// it is not leaked (ExternalSystemGateway-010).
// it is not leaked.
await connection.DisposeAsync();
throw;
}
@@ -97,12 +97,12 @@ public class DatabaseGateway : IDatabaseGateway
throw new InvalidOperationException("Store-and-forward service not available for cached writes");
}
// M2.3 (#7): attempt the write IMMEDIATELY and classify the outcome,
// mirroring ExternalSystemClient.CachedCallAsync. The pre-M2.3 behaviour
// enqueued every write unconditionally and the S&F retry sweep then
// retried ALL failures forever — a permanent SQL error (constraint,
// syntax, permission) was never returned to the script and spun in the
// buffer indefinitely. Now:
// Attempt the write IMMEDIATELY and classify the outcome, mirroring
// ExternalSystemClient.CachedCallAsync. The previous behaviour enqueued
// every write unconditionally and the S&F retry sweep then retried ALL
// failures forever — a permanent SQL error (constraint, syntax,
// permission) was never returned to the script and spun in the buffer
// indefinitely. Now:
// * success -> Delivered, NOT buffered;
// * PermanentDatabaseException -> Failed synchronously, NOT buffered;
// * TransientDatabaseException -> buffered to S&F for retry.
@@ -142,7 +142,7 @@ public class DatabaseGateway : IDatabaseGateway
Parameters = parameters
});
// ExternalSystemGateway-015: the entity's MaxRetries is a non-nullable int
// The entity's MaxRetries is a non-nullable int
// whose default is 0, and the Store-and-Forward engine interprets a stored
// MaxRetries of 0 as "no limit" (retry forever) — see
// StoreAndForwardMessage.MaxRetries ("0 = no limit") and the retry-sweep
@@ -157,28 +157,26 @@ public class DatabaseGateway : IDatabaseGateway
originInstanceName,
definition.MaxRetries > 0 ? definition.MaxRetries : null,
definition.RetryDelay > TimeSpan.Zero ? definition.RetryDelay : null,
// M2.3 (#7): attemptImmediateDelivery: false — this method already
// attemptImmediateDelivery: false — this method already
// made the write attempt above (the transient-classified failure is
// exactly why we are buffering). Letting EnqueueAsync re-invoke the
// delivery handler would execute the same write a second time —
// mirrors ExternalSystemClient.CachedCallAsync.
attemptImmediateDelivery: false,
// Audit Log #23 (M3): pin the S&F message id to the
// TrackedOperationId so the retry loop (Bundle E Tasks E4/E5) can
// Pin the S&F message id to the
// TrackedOperationId so the retry loop can
// read it back via StoreAndForwardMessage.Id and emit per-attempt +
// terminal cached-write telemetry. Null -> S&F mints its own GUID
// (legacy pre-M3 behaviour).
// (legacy behaviour).
messageId: trackedOperationId?.ToString(),
// Audit Log #23 (ExecutionId Task 4): thread the originating script
// execution's ExecutionId + SourceScript onto the buffered row so
// the retry-loop cached-write audit rows carry the same provenance
// the script-side cached rows do.
// Thread the originating script execution's ExecutionId + SourceScript
// onto the buffered row so the retry-loop cached-write audit rows carry
// the same provenance the script-side cached rows do.
executionId: executionId,
sourceScript: sourceScript,
// Audit Log #23 (ParentExecutionId Task 6): thread the spawning
// inbound-API request's ExecutionId onto the buffered row so the
// retry-loop cached-write audit rows correlate back to the
// cross-execution chain. Null for a non-routed run.
// Thread the spawning inbound-API request's ExecutionId onto the
// buffered row so the retry-loop cached-write audit rows correlate
// back to the cross-execution chain. Null for a non-routed run.
parentExecutionId: parentExecutionId);
// Buffered for retry — mirrors the API path's WasBuffered=true result.
@@ -186,17 +184,17 @@ public class DatabaseGateway : IDatabaseGateway
}
/// <summary>
/// WP-9/10: Delivers a buffered CachedDbWrite during a store-and-forward retry
/// Delivers a buffered CachedDbWrite during a store-and-forward retry
/// sweep — executes the SQL against the named connection.
/// </summary>
/// <remarks>
/// M2.3 (#7): the outcome is classified, mirroring
/// The outcome is classified, mirroring
/// <see cref="ExternalSystemClient.DeliverBufferedAsync"/>. Returns
/// <c>false</c> — so the S&amp;F engine PARKS the message — when the
/// connection no longer exists, the payload is unreadable, or the SQL fails
/// with a PERMANENT error (constraint / syntax / permission). A TRANSIENT SQL
/// error (<see cref="TransientDatabaseException"/>) propagates so the engine
/// retries. The pre-M2.3 code rethrew on ANY SQL error, so a permanent
/// retries. The previous code rethrew on ANY SQL error, so a permanent
/// failure on the retry path looped forever.
/// </remarks>
/// <param name="message">The buffered store-and-forward message to deliver.</param>
@@ -206,7 +204,7 @@ public class DatabaseGateway : IDatabaseGateway
public async Task<bool> DeliverBufferedAsync(
StoreAndForwardMessage message, CancellationToken cancellationToken = default)
{
// ExternalSystemGateway-018: a malformed (not just empty/null-fielded)
// A malformed (not just empty/null-fielded)
// PayloadJson would otherwise throw `JsonException` here, which the S&F
// engine treats as a transient failure and retries forever (poison
// message). Re-running the same deserialization against the same payload
@@ -280,7 +278,7 @@ public class DatabaseGateway : IDatabaseGateway
new Dictionary<string, object?>();
/// <summary>
/// M2.3 (#7): executes a parameterised SQL write against the given connection
/// Executes a parameterised SQL write against the given connection
/// string and classifies the outcome into
/// <see cref="TransientDatabaseException"/> / <see cref="PermanentDatabaseException"/>,
/// mirroring the ordered catches of
@@ -313,7 +311,7 @@ public class DatabaseGateway : IDatabaseGateway
IReadOnlyDictionary<string, object?> parameters,
CancellationToken cancellationToken)
{
// M2.3 (#7) code-review fix: the catch ordering MIRRORS
// The catch ordering MIRRORS
// ExternalSystemClient.InvokeHttpAsync exactly so the SQL path classifies
// a live outage the same way the HTTP path does:
// 1. caller-requested cancellation propagates UNCHANGED (never a "DB error");
@@ -338,7 +336,7 @@ public class DatabaseGateway : IDatabaseGateway
}
catch (SqlException ex)
{
// [2] ExternalSystemGateway-025: a caller-token cancellation can surface
// [2] A caller-token cancellation can surface
// from the SQL driver as a SqlException (a mid-flight cancel), not an
// OperationCanceledException, so the [1] filter above never sees it.
// Re-check the caller's token at the TOP of this block so such a cancel
@@ -366,7 +364,7 @@ public class DatabaseGateway : IDatabaseGateway
}
/// <summary>
/// M2.3 (#7): the raw ADO.NET write — opens the connection, builds the
/// The raw ADO.NET write — opens the connection, builds the
/// command, and executes it. Marked <c>internal virtual</c> so tests can throw
/// RAW outage-shaped exceptions (e.g. <see cref="InvalidOperationException"/>,
/// <see cref="System.Net.Sockets.SocketException"/>) through the PRODUCTION
@@ -399,7 +397,7 @@ public class DatabaseGateway : IDatabaseGateway
await command.ExecuteNonQueryAsync(cancellationToken).ConfigureAwait(false);
}
// ExternalSystemGateway-020: a JSON number that does not fit in Int64 must
// A JSON number that does not fit in Int64 must
// prefer decimal over double — a script's decimal SQL parameter is
// serialised as JSON without a type tag, and downcasting it to double on
// the cached-write retry path silently loses precision (e.g.
@@ -437,7 +435,7 @@ public class DatabaseGateway : IDatabaseGateway
string connectionName,
CancellationToken cancellationToken)
{
// ExternalSystemGateway-011: name-keyed repository lookup instead of
// Name-keyed repository lookup instead of
// fetch-all-then-filter — connection definitions are resolved on every
// cached write / connection request, so the repository performs an indexed
// query rather than loading every connection into memory.
@@ -3,7 +3,7 @@ using System.Net;
namespace ZB.MOM.WW.ScadaBridge.ExternalSystemGateway;
/// <summary>
/// WP-8: Classifies HTTP errors as transient or permanent.
/// Classifies HTTP errors as transient or permanent.
/// Transient: connection refused, timeout, HTTP 408/429/5xx.
/// Permanent: HTTP 4xx (except 408/429).
/// </summary>
@@ -14,9 +14,9 @@ using ZB.MOM.WW.ScadaBridge.StoreAndForward;
namespace ZB.MOM.WW.ScadaBridge.ExternalSystemGateway;
/// <summary>
/// WP-6: HTTP/REST client that invokes external APIs.
/// WP-7: Dual call modes — Call (synchronous) and CachedCall (S&amp;F on transient failure).
/// WP-8: Error classification applied to HTTP responses and exceptions.
/// HTTP/REST client that invokes external APIs.
/// Dual call modes — Call (synchronous) and CachedCall (S&amp;F on transient failure).
/// Error classification applied to HTTP responses and exceptions.
/// </summary>
public class ExternalSystemClient : IExternalSystemClient
{
@@ -123,7 +123,7 @@ public class ExternalSystemClient : IExternalSystemClient
// attempt above; letting EnqueueAsync re-invoke the handler would
// dispatch the same request a second time.
//
// ExternalSystemGateway-015: the entity's MaxRetries is a non-nullable
// The entity's MaxRetries is a non-nullable
// int whose default is 0, and the Store-and-Forward engine interprets a
// stored MaxRetries of 0 as "no limit" (retry forever) — see
// StoreAndForwardMessage.MaxRetries ("0 = no limit") and the retry-sweep
@@ -139,19 +139,19 @@ public class ExternalSystemClient : IExternalSystemClient
system.MaxRetries > 0 ? system.MaxRetries : null,
system.RetryDelay > TimeSpan.Zero ? system.RetryDelay : null,
attemptImmediateDelivery: false,
// Audit Log #23 (M3): pin the S&F message id to the
// Pin the S&F message id to the
// TrackedOperationId so the retry loop can read it back via
// StoreAndForwardMessage.Id and emit per-attempt + terminal
// cached-call telemetry (Bundle E Tasks E4/E5). Null -> S&F
// mints its own GUID (legacy pre-M3 behaviour).
// cached-call telemetry. Null -> S&F
// mints its own GUID (legacy behaviour).
messageId: trackedOperationId?.ToString(),
// Audit Log #23 (ExecutionId Task 4): thread the originating
// Thread the originating
// script execution's ExecutionId + SourceScript onto the
// buffered row so the retry-loop cached-call audit rows carry
// the same provenance the script-side cached rows do.
executionId: executionId,
sourceScript: sourceScript,
// Audit Log #23 (ParentExecutionId Task 6): thread the spawning
// Thread the spawning
// inbound-API request's ExecutionId onto the buffered row so
// the retry-loop cached-call audit rows correlate back to the
// cross-execution chain. Null for a non-routed run.
@@ -162,7 +162,7 @@ public class ExternalSystemClient : IExternalSystemClient
}
/// <summary>
/// WP-7/10: Delivers a buffered ExternalSystem call during a store-and-forward
/// Delivers a buffered ExternalSystem call during a store-and-forward
/// retry sweep. Returns true on success, false on permanent failure (the message
/// is parked); throws <see cref="TransientExternalSystemException"/> on a
/// transient failure so the engine retries.
@@ -173,7 +173,7 @@ public class ExternalSystemClient : IExternalSystemClient
public async Task<bool> DeliverBufferedAsync(
StoreAndForwardMessage message, CancellationToken cancellationToken = default)
{
// ExternalSystemGateway-018: a malformed (not just empty/null-fielded)
// A malformed (not just empty/null-fielded)
// PayloadJson would otherwise throw `JsonException` here, which the S&F
// engine treats as a transient failure and retries forever (poison
// message). Re-running the same deserialization against the same payload
@@ -229,7 +229,7 @@ public class ExternalSystemClient : IExternalSystemClient
Dictionary<string, JsonElement>? Parameters);
/// <summary>
/// WP-6: Executes the HTTP request against the external system.
/// Executes the HTTP request against the external system.
/// </summary>
/// <param name="system">The external system definition.</param>
/// <param name="method">The external system method to invoke.</param>
@@ -242,8 +242,8 @@ public class ExternalSystemClient : IExternalSystemClient
IReadOnlyDictionary<string, object?>? parameters,
CancellationToken cancellationToken)
{
// ExternalSystemGateway-022: validate the verb against the documented set
// (GET/POST/PUT/PATCH/DELETE — per ESG-023's design-doc reconciliation)
// Validate the verb against the documented set
// (GET/POST/PUT/PATCH/DELETE)
// BEFORE constructing the request. `new HttpMethod(string)` accepts any
// token-character string (e.g. "FOO", "DLETE"), and the body-vs-query
// branch below only knows POST/PUT/PATCH and GET/DELETE — so an
@@ -256,7 +256,7 @@ public class ExternalSystemClient : IExternalSystemClient
var client = _httpClientFactory.CreateClient($"ExternalSystem_{system.Name}");
// ExternalSystemGateway-019: HttpClient.Timeout defaults to 100 seconds
// HttpClient.Timeout defaults to 100 seconds
// and is enforced internally by SendAsync via its own private CTS — a
// TaskCanceledException raised by that internal CTS does not trip
// either the caller's token or the gateway's timeout CTS, so it falls
@@ -277,8 +277,7 @@ public class ExternalSystemClient : IExternalSystemClient
var url = BuildUrl(system.EndpointUrl, method.Path, parameters, method.HttpMethod);
// The request and response own IDisposable resources (StringContent, the
// response content stream). Dispose both, including on the exception paths
// (ExternalSystemGateway-005).
// response content stream). Dispose both, including on the exception paths.
using var request = new HttpRequestMessage(new HttpMethod(method.HttpMethod), url);
// Apply authentication
@@ -354,8 +353,7 @@ public class ExternalSystemClient : IExternalSystemClient
// Bound the external error body before embedding it into a
// script-visible message / event-log entry — a misbehaving or hostile
// endpoint must not be able to inflate every error string
// (ExternalSystemGateway-007).
// endpoint must not be able to inflate every error string.
var errorBody = Truncate(body, MaxErrorBodyChars);
if (ErrorClassifier.IsTransient(response.StatusCode))
@@ -371,7 +369,7 @@ public class ExternalSystemClient : IExternalSystemClient
// The design requires permanent failures to be visible in Site Event
// Logging — emit a warning so the gateway is not silent on a permanent
// failure (ExternalSystemGateway-012).
// failure.
_logger.LogWarning(
"Permanent HTTP {StatusCode} from external system {System} calling {Method}: {Error}",
(int)response.StatusCode, system.Name, method.Name, errorBody);
@@ -383,13 +381,13 @@ public class ExternalSystemClient : IExternalSystemClient
/// <summary>
/// Upper bound (characters) on an external error response body echoed into a
/// script-visible error message — see ExternalSystemGateway-007.
/// script-visible error message.
/// </summary>
private const int MaxErrorBodyChars = 2048;
/// <summary>
/// ExternalSystemGateway-022: documented HTTP-verb allowlist. Matches the
/// design doc's enumerated set (GET/POST/PUT/PATCH/DELETE per ESG-023) and
/// Documented HTTP-verb allowlist. Matches the
/// design doc's enumerated set (GET/POST/PUT/PATCH/DELETE) and
/// the body-vs-query branching above; any addition here must update both.
/// </summary>
private static readonly HashSet<string> SupportedHttpMethods = new(StringComparer.OrdinalIgnoreCase)
@@ -435,7 +433,7 @@ public class ExternalSystemClient : IExternalSystemClient
// A method that targets the base URL itself has an empty (or "/") path.
// Appending a trailing "/" in that case yields ".../api/" which some
// servers treat as a distinct resource — only append a segment when the
// method actually defines a non-empty relative path (ExternalSystemGateway-006).
// method actually defines a non-empty relative path.
var trimmedBase = baseUrl.TrimEnd('/');
var trimmedPath = path.Trim().TrimStart('/');
var url = string.IsNullOrEmpty(trimmedPath)
@@ -454,7 +452,7 @@ public class ExternalSystemClient : IExternalSystemClient
// Only append "?" when the effective query string is non-empty — a method
// whose parameter values are all null produces no query string, and the
// URL must then be identical to the no-parameters case rather than ending
// in a bare "?" (ExternalSystemGateway-017).
// in a bare "?".
if (queryString.Length > 0)
{
url += "?" + queryString;
@@ -466,7 +464,7 @@ public class ExternalSystemClient : IExternalSystemClient
private void ApplyAuth(HttpRequestMessage request, ExternalSystemDefinition system)
{
// ESG-021: distinguish "intentionally unauthenticated" (AuthType = none)
// Distinguish "intentionally unauthenticated" (AuthType = none)
// from "AuthConfiguration is missing or empty for a type that requires it"
// (deployment glitch, decryption failure, operator typo). The unauthenticated
// case is silent; the requires-creds-but-empty case logs a Warning so an
@@ -512,7 +510,7 @@ public class ExternalSystemClient : IExternalSystemClient
}
else
{
// ESG-021: malformed Basic config (no ':' separator) means the
// Malformed Basic config (no ':' separator) means the
// request goes out with no Authorization header. Warn so the
// failure mode is visible inside ZB.MOM.WW.ScadaBridge.
_logger.LogWarning(
@@ -526,7 +524,7 @@ public class ExternalSystemClient : IExternalSystemClient
break;
default:
// ESG-021: unknown AuthType silently fell through here before. Warn.
// Unknown AuthType silently fell through here before. Warn.
_logger.LogWarning(
"ApplyAuth: External system '{System}' has unknown AuthType '{AuthType}'; request will be sent without an auth header. Allowed values: apikey, basic, none.",
system.Name, system.AuthType);
@@ -539,7 +537,7 @@ public class ExternalSystemClient : IExternalSystemClient
string methodName,
CancellationToken cancellationToken)
{
// ExternalSystemGateway-011: name-keyed repository lookups instead of
// Name-keyed repository lookups instead of
// fetch-all-then-filter — definitions are resolved on every hot-path call
// (a script's ExternalSystem.Call()), so the repository performs an indexed
// query rather than loading every system / every method into memory.
@@ -25,7 +25,7 @@ public static class ServiceCollectionExtensions
services.AddHttpClient();
// ExternalSystemGateway-013 / -016: wire MaxConcurrentConnectionsPerSystem
// Wire MaxConcurrentConnectionsPerSystem
// into the primary handler of the gateway's per-system named clients
// ("ExternalSystem_{name}") only. The names are created dynamically, so a
// static AddHttpClient("name") registration is not possible; instead a
@@ -53,13 +53,13 @@ public static class ServiceCollectionExtensions
/// <returns>The same <see cref="IServiceCollection"/> for chaining.</returns>
public static IServiceCollection AddExternalSystemGatewayActors(this IServiceCollection services)
{
// WP-10: Actor registration happens in AkkaHostedService.
// Actor registration happens in AkkaHostedService.
// Script Execution Actors run on dedicated blocking I/O dispatcher.
return services;
}
/// <summary>
/// ExternalSystemGateway-016: configures the primary HTTP message handler with the
/// Configures the primary HTTP message handler with the
/// gateway's <see cref="ExternalSystemGatewayOptions.MaxConcurrentConnectionsPerSystem"/>
/// cap, but only for the gateway's own named clients
/// (<see cref="GatewayClientNamePrefix"/>). Clients owned by other host components
@@ -6,7 +6,7 @@ using Microsoft.Data.SqlClient;
namespace ZB.MOM.WW.ScadaBridge.ExternalSystemGateway;
/// <summary>
/// M2.3 (#7): classifies a SQL Server failure as transient (a brief wait /
/// Classifies a SQL Server failure as transient (a brief wait /
/// retry may succeed — buffer to store-and-forward) or permanent (the identical
/// statement cannot succeed — return to the script / park the buffered message).
/// </summary>
@@ -44,7 +44,7 @@ namespace ZB.MOM.WW.ScadaBridge.ExternalSystemGateway;
/// the obvious permanent cases, but the policy is broader: <b>any error number not
/// in the transient set — including unknown / undocumented / ambiguous numbers —
/// is treated as permanent.</b> Fail-fast is the safer default: silently
/// retrying an unrecognised error forever (the pre-M2.3 behaviour) hides
/// retrying an unrecognised error forever (the previous behaviour) hides
/// authoring bugs and can replay duplicate side effects. A genuinely transient
/// number we have not enumerated will, at worst, surface to the script as a
/// permanent failure — a loud, fixable outcome — rather than spin in an