fix(m9/T24a): scope move-guard native-alarm scan to source-site templates (Ordinal); purpose-built include; add guard-4 + repo tests

This commit is contained in:
Joseph Doherty
2026-06-18 11:38:31 -04:00
parent fbe4ddaf58
commit dbe51e5f25
5 changed files with 290 additions and 15 deletions
@@ -1426,17 +1426,40 @@ public class ManagementActor : ReceiveActor
// instances may override that name
// (InstanceNativeAlarmSourceOverride.ConnectionNameOverride). Moving a
// connection changes which physical connection that name resolves to on
// the source site, so any such reference to the moving name is a blocker.
// Detect and report only — never auto-rewrite.
// the SOURCE site, so any such reference *on the source site* to the
// moving name is a blocker. Detect and report only — never auto-rewrite.
//
// Scoping: templates are site-agnostic and ConnectionName resolves
// against the DEPLOYING site's connection pool at flatten time, so the
// template scan is restricted to templates actually instantiated on the
// SOURCE site. A template referencing the same connection NAME but only
// instantiated on another site is NOT affected by this move (that site
// keeps its own connection of the same name), so a global template scan
// would be a false positive that over-blocks legitimate moves whenever
// connection names are reused across sites (the common case).
//
// Comparison is StringComparison.Ordinal to match the
// flattening/deployment pipeline (FlatteningService / FlatteningPipeline
// resolve connection names case-sensitively) — block exactly the
// references the runtime would actually fail to resolve.
var referenceBlockers = new List<string>();
// Source-site instances with their native-alarm-source overrides eagerly
// loaded (purpose-built load so the hot-path GetInstancesBySiteIdAsync
// stays lean). Reused below for both the override scan and to derive the
// set of templates to scan.
var sourceInstances = await repo.GetInstancesWithNativeAlarmOverridesBySiteIdAsync(sourceSiteId);
// Only templates instantiated on the SOURCE site can be broken by the move.
var templateRepo = sp.GetRequiredService<ITemplateEngineRepository>();
var templates = await templateRepo.GetAllTemplatesAsync();
foreach (var template in templates)
var sourceTemplateIds = sourceInstances.Select(i => i.TemplateId).Distinct();
foreach (var templateId in sourceTemplateIds)
{
var template = await templateRepo.GetTemplateByIdAsync(templateId);
if (template is null) continue;
foreach (var source in template.NativeAlarmSources)
{
if (string.Equals(source.ConnectionName, conn.Name, StringComparison.OrdinalIgnoreCase))
if (string.Equals(source.ConnectionName, conn.Name, StringComparison.Ordinal))
{
referenceBlockers.Add(
$"template '{template.Name}' native-alarm-source '{source.Name}'");
@@ -1446,12 +1469,11 @@ public class ManagementActor : ReceiveActor
// Instance-level overrides on the SOURCE site that name this connection
// would orphan once the connection leaves the site.
var sourceInstances = await repo.GetInstancesBySiteIdAsync(sourceSiteId);
foreach (var instance in sourceInstances)
{
foreach (var ovr in instance.NativeAlarmSourceOverrides)
{
if (string.Equals(ovr.ConnectionNameOverride, conn.Name, StringComparison.OrdinalIgnoreCase))
if (string.Equals(ovr.ConnectionNameOverride, conn.Name, StringComparison.Ordinal))
{
referenceBlockers.Add(
$"instance '{instance.UniqueName}' (ID {instance.Id}) native-alarm-source override '{ovr.SourceCanonicalName}'");