fix(transport): transport template NativeAlarmSources — imports no longer amputate native alarm mirrors nor dangle instance overrides

New TemplateNativeAlarmSourceDto + init-only TemplateDto.NativeAlarmSources (empty
default, additive — IsInherited placeholders carried for the collision detector).
Exported in ToBundleContent, consumed in FromBundleContent + BuildTemplate, and a
new SyncTemplateNativeAlarmSourcesAsync runs the Overwrite add/update/delete child
sync with per-change audit rows. ArtifactDiff.CompareTemplate now DiffChildren over
native sources so Preview reports a NativeAlarmSources field change. Proven by an
end-to-end flatten test: an instance native-alarm-source override that dangled
pre-fix now resolves against the re-imported template source.

Claude-Session: https://claude.ai/code/session_01MtdgwpEeCUn6cUA5f1LMPj
This commit is contained in:
Joseph Doherty
2026-07-09 16:59:37 -04:00
parent 4df5109742
commit e4136cb920
7 changed files with 589 additions and 3 deletions
@@ -91,7 +91,20 @@ public sealed class EntitySerializer
LockedInDerived: s.LockedInDerived)).ToList(),
Compositions: t.Compositions.Select(c => new TemplateCompositionDto(
InstanceName: c.InstanceName,
ComposedTemplateName: templateNameById.TryGetValue(c.ComposedTemplateId, out var cn) ? cn : string.Empty)).ToList());
ComposedTemplateName: templateNameById.TryGetValue(c.ComposedTemplateId, out var cn) ? cn : string.Empty)).ToList())
{
// Native alarm sources carry ALL rows including IsInherited
// placeholders — the collision detector depends on them.
NativeAlarmSources = t.NativeAlarmSources.Select(n => new TemplateNativeAlarmSourceDto(
Name: n.Name,
Description: n.Description,
ConnectionName: n.ConnectionName,
SourceReference: n.SourceReference,
ConditionFilter: n.ConditionFilter,
IsLocked: n.IsLocked,
IsInherited: n.IsInherited,
LockedInDerived: n.LockedInDerived)).ToList(),
};
}).ToList(),
SharedScripts: aggregate.SharedScripts.Select(s => new SharedScriptDto(
Name: s.Name,
@@ -352,6 +365,20 @@ public sealed class EntitySerializer
LockedInDerived = s.LockedInDerived,
});
}
foreach (var n in dto.NativeAlarmSources)
{
t.NativeAlarmSources.Add(new TemplateNativeAlarmSource(n.Name)
{
TemplateId = t.Id,
Description = n.Description,
ConnectionName = n.ConnectionName,
SourceReference = n.SourceReference,
ConditionFilter = n.ConditionFilter,
IsLocked = n.IsLocked,
IsInherited = n.IsInherited,
LockedInDerived = n.LockedInDerived,
});
}
return t;
})
.ToList();