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
@@ -392,10 +392,17 @@ public sealed class BundleImporter : IBundleImporter
.ConfigureAwait(false);
var hydratedByName = hydratedTemplates
.ToDictionary(t => t.Name, t => t, StringComparer.Ordinal);
// #05-T23: a template's ParentTemplateId (base) and each composition's
// ComposedTemplateId point at ANY template in the DB (not just the ones
// whose names the bundle carries), so resolve names through the full
// template set. Without this map the diff compares "<id:N>" placeholders
// against real bundle names and reports spurious Modified.
var allTemplateStubs = await _templateRepo.GetAllTemplatesAsync(ct).ConfigureAwait(false);
var templateNameById = allTemplateStubs.ToDictionary(t => t.Id, t => t.Name);
foreach (var tDto in content.Templates)
{
hydratedByName.TryGetValue(tDto.Name, out var existing);
items.Add(_diff.CompareTemplate(tDto, existing));
items.Add(_diff.CompareTemplate(tDto, existing, folderNameById, templateNameById));
}
// ---- SharedScripts ----