Transport: derived (composition-slot) templates round-trip as BASE templates — IsDerived/OwnerCompositionId are not in the bundle #118
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Found while fixing #100 item 1 (code reading; not reproduced end to end).
TemplateDto(src/ZB.MOM.WW.ScadaBridge.Transport/Serialization/EntityDtos.cs) carries noIsDerived/OwnerCompositionId.DependencyResolver.ExpandTemplateClosureAsync) pulls everyComposedTemplateIdinto the bundle, and the compose flow points a composition at its slot-owned derived template (TemplateService.BuildDerivedTemplate(source, instanceName)— the derived template is named after the composition slot).new Template(name) { Description = … }(BundleImporter.cs, the Add path), so an imported derived template lands as a base template named after the slot (e.g.Motor1), visible in the template tree, inside the filtered unique name index, and no longer cascade-owned by its composition.Consequences to confirm: two compositions with the same slot name in one bundle produce two
TemplateDtos with the same name (collide on import); a slot name equal to an existing base template name collides with it (or Overwrites it); deleting the composition on the target no longer deletes its slot template.This is a design decision for the owner (carry
IsDerived+ owner(TemplateName, InstanceName)additively in the DTO and recreate the derived row through the compose flow, vs. flatten derived templates out of the bundle). #100 item 1 only fixed by-name reference resolution against the target (base-first, ambiguous derived-only names unresolved); it does not address this.Owner decision 2026-11-10: carry the derived/base template relationship in the bundle and rebuild the derived slot through the normal compose flow on import (recommended option). Implementation in progress.
Fixed on main @
84a05042. Implemented the owner decision: the bundle now carries the derived/base relationship, and the import rebuilds the slot through the normal compose flow.Format.
TemplateDtogets an additive field,Derivation = { OwnerTemplateName, SlotPath, BaseTemplateName, BaseSlotPath }. It names the owner path and the base path by stable names. Examples:Pump.M1, and for a nested slot the cascade created,Station.P.M1with basePump.M1.WhenWritingNull), so a base template's JSON is unchanged.IsInherited == false, meaning overrides and additions). It does not carry the inherited placeholders.schemaVersion1.1 → 1.2.bundleFormatVersionstays 1, so no format bump: older importers ignore the property.content.json/content.enc. The manifest'scontentHashcovers that content, so the #45 signature overmanifest.jsoncovers it too.Import.
DerivedTemplatePlanorders derived templates topologically after their owner and base, so bundle order does not matter.SemanticValidationException, raised before anything is staged.TemplateService.AddCompositionAsync.DeleteCompositionAsyncand recomposed.ResyncInheritedMembersAsyncthen reconciles them.BundleImportDerivedTemplateUnresolvedaudit row and anImportResult.Warningsentry. No base template is created in its place.Differential / identical-skip. Derived rows are previewed and resolved under their qualified name (
Template/Pump.M1). Each is compared against the target's template at the same owner path, never against a base template with the same name. The comparison covers derived-ness, the base (qualified) and the target slot's own rows. An unchanged slot reads Identical, and a changed override reads Modified on that row only.Compatibility. New fixture
Fixtures/pre-118-derived-plaintext.scadabundle, exported bya13aabc7(pre-fix code), contains a Pump whose M1 slot is a derived Motor with Speed overridden to 5. It imports exactly as before:M1arrives as a base template inheriting Motor, and Pump composes it. Thepre-signing-*fixtures andLegacyBundleCompatibilityTestsare unchanged and pass.Tests.
DerivedTemplateTransportTests(10),DerivedTemplatePlanTests(3) and oneDependencyResolverTestscase cover:Mutation-proved, every mutation killed:
Derivation: 7 tests fail;Evidence (after rebasing onto
fb04b54d):dotnet build ZB.MOM.WW.ScadaBridge.slnx0 warnings / 0 errors. All passed: Transport.Tests 236, Transport.IntegrationTests 161, TemplateEngine.Tests 617, ManagementService.Tests 790, CentralUI.Tests 1540, CLI.Tests 592 (TemplateEngine and CLI were run before the rebase). Not deployed. The design is documented in Component-Transport.md, "Derived (composition-slot) templates (ScadaBridge#118)".Seen on the way, not changed (TemplateEngine): once a derived slot holds an override,
TemplateService.AddAttributeAsyncon that slot fails with "Naming collision on 'Speed': Attribute from M1, Attribute from parent 'Motor'". So an addition is only possible before the first override. Worth a separate look.