fix(transport): template diffs resolve folder/base/composition names — unchanged structured templates classify Identical again

#05-T23. ArtifactDiff.CompareTemplate gains optional folderNameById /
templateNameById maps; FolderNameOf / BaseTemplateNameOf / CompositionTargetNameOf
resolve the existing template's FolderId / ParentTemplateId / composition
ComposedTemplateId to real names (falling back to the <id:N> placeholder only
when a map is absent or misses, keeping existing unit-test callers valid).
PreviewAsync builds templateNameById from GetAllTemplatesAsync (parent/composition
targets can be any template, not just bundle-named ones) and threads both maps
through. Fixes spurious Modified on every re-import of a foldered/derived/composed
template. End-to-end preview test + two ArtifactDiff unit tests.

Claude-Session: https://claude.ai/code/session_01MtdgwpEeCUn6cUA5f1LMPj
This commit is contained in:
Joseph Doherty
2026-07-10 02:22:44 -04:00
parent 523e670579
commit 92e713f1a1
4 changed files with 128 additions and 15 deletions
@@ -150,6 +150,52 @@ public sealed class ArtifactDiffTests
Assert.Contains(("add", "new2"), hunks);
}
// ============ #05-T23: placeholder-identity resolution via name maps ============
[Fact]
public void CompareTemplate_MatchingFolderBaseAndComposition_IsIdentical()
{
// A structured template (FolderId, ParentTemplateId, one composition) that
// the incoming bundle matches by NAME must classify Identical. Before T23,
// FolderNameOf/BaseTemplateNameOf/CompositionTargetNameOf returned "<id:N>"
// placeholders that never equalled the incoming names, so every re-import
// of an unchanged structured template spuriously read Modified.
var existing = MakeTemplate("Pump");
existing.FolderId = 5;
existing.ParentTemplateId = 7;
existing.Compositions.Add(new TemplateComposition("Mod1") { ComposedTemplateId = 9 });
var folderNameById = new Dictionary<int, string> { [5] = "Machines" };
var templateNameById = new Dictionary<int, string> { [7] = "BasePump", [9] = "FeatureX" };
var incoming = MakeTemplateDto("Pump") with
{
FolderName = "Machines",
BaseTemplateName = "BasePump",
Compositions = new[] { new TemplateCompositionDto("Mod1", "FeatureX") },
};
var item = _diff.CompareTemplate(incoming, existing, folderNameById, templateNameById);
Assert.Equal(ConflictKind.Identical, item.Kind);
}
[Fact]
public void CompareTemplate_DivergentBaseTemplateName_IsModified()
{
// Sanity: the maps must not mask a REAL change. Existing base = "BasePump",
// incoming names a different base → Modified.
var existing = MakeTemplate("Pump");
existing.ParentTemplateId = 7;
var templateNameById = new Dictionary<int, string> { [7] = "BasePump" };
var incoming = MakeTemplateDto("Pump") with { BaseTemplateName = "DifferentBase" };
var item = _diff.CompareTemplate(incoming, existing, folderNameById: null, templateNameById: templateNameById);
Assert.Equal(ConflictKind.Modified, item.Kind);
}
// ============ #05-T6: single-source child equality — diff sees the full field set ============
[Fact]