fix(centralui): surface inherited compositions in the templates tree (followup #9)
The templates tree rendered a derived/composed member (e.g. LeftReactorSide, derived from ReactorSide) as a flat leaf, omitting compositions it inherits from its base (e.g. LeakTest composed onto ReactorSide). BuildCompositionLeavesFor recursed only over a template's OWN composition rows; an inherited composition row lives on the ancestor, and TemplateComposition has no IsInherited placeholder (unlike attributes/alarms/scripts/native-sources), so the child's own Compositions was empty. Same 'derived templates don't surface inherited members' family as followups #1/#2, but for compositions. Deploy/flatten was always correct (TemplateResolver.ResolveAllMembers walks the chain) — display-only. Fix: - BuildCompositionLeavesFor now renders the EFFECTIVE composition set (own + inherited) via EffectiveCompositionsFor, which walks the inheritance chain (leaf->root, child wins on InstanceName), mirroring the resolver. - Inherited slots are flagged (TemplateTreeNode.IsInherited), badged 'inherited' in the label, and their context menu offers only 'Open composed template' (Rename/Delete edit the ancestor's slot, so suppressed on inherited nodes). - The same inherited row can appear under several derived members (LeakTest under both LeftReactorSide and RightReactorSide), so composition nodes use a path-qualified KeyOverride to keep TreeView selection/expansion keys unique; recursion is cycle-guarded. Tests: +1 bUnit (TemplatesPageTests.Renders_InheritedComposition_UnderDerivedComposedMember); CentralUI suite 867 green; full solution builds 0/0. Docs: Component-CentralUI.md (effective composition set in tree); known-issues tracker #9 recorded + resolved. Note: CentralUI change — shows on wonder-app-vd03 only after that host is redeployed.
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
**Status:** RESOLVED · **Found:** 2026-06-24 · **Context:** live ops session on `wonder-app-vd03` (CvdReactor / Z28061 / Z28061Sim) — renaming the template, adding the LeakTest module, and adding MoveInType to the MESReceiver children.
|
||||
**Components:** Central UI (#9), Template Engine (#1), CLI (#19), Configuration Database (#17), Deployment Manager (#2)
|
||||
|
||||
**Resolved:** #3 (collision detector) and #7 (sandbox compile surface) on branch `fix/followups-3-7`; #1 + #2 (inherited-member propagation & resync) on branch `fix/followups-1-2`; #4 + #5 + #6 + #8 (CLI ergonomics + structured deploy validation error) on branch `fix/followups-4-5-6-8` (all 2026-06-24). All items resolved.
|
||||
**Resolved:** #3 (collision detector) and #7 (sandbox compile surface) on branch `fix/followups-3-7`; #1 + #2 (inherited-member propagation & resync) on branch `fix/followups-1-2`; #4 + #5 + #6 + #8 (CLI ergonomics + structured deploy validation error) and #9 (inherited compositions in the templates tree) on branch `fix/followups-4-5-6-8` (all 2026-06-24). All items resolved.
|
||||
|
||||
Issues are listed worst-first. Severities are author estimates. None caused data loss; the runtime/flattened config and deployed instances are correct.
|
||||
|
||||
@@ -150,3 +150,16 @@ message fallback, mixed categories).
|
||||
**Root cause:** `ValidateConnectionBindingCompleteness` emits one clause per unbound attribute and joins them into a flat string; there is no grouping or count.
|
||||
|
||||
**Suggested fix:** return a structured/summarized error — leading count (`52 attributes are unbound`) + grouped-by-module breakdown (or a capped list with "…and N more") — instead of the flat semicolon-joined dump. Keep the full list available in a detail/expandable view or the deploy log.
|
||||
|
||||
---
|
||||
|
||||
## 9. Templates tree omits inherited compositions under derived composed-members (user-reported)
|
||||
**Severity:** Low-Medium · **Components:** Central UI (#9) · **✅ RESOLVED 2026-06-24 (branch `fix/followups-4-5-6-8`)**
|
||||
|
||||
**Fix:** `Templates.razor` → `BuildCompositionLeavesFor` now renders the **effective** composition set (own + inherited) via a new `EffectiveCompositionsFor` that walks the inheritance chain (leaf→root, child wins on `InstanceName`), mirroring `TemplateResolver.ResolveAllMembers`. Inherited slots are flagged (`TemplateTreeNode.IsInherited`), badged **"inherited"** in the label, and their context menu offers only **Open composed template** (Rename/Delete edit the ancestor's slot, so they're suppressed on inherited nodes). Because the same inherited row can now appear under several derived members (LeakTest under both LeftReactorSide and RightReactorSide), composition nodes use a path-qualified `KeyOverride` (`t:{owner}/c:{id}/…`) so TreeView selection/expansion keys stay unique; the recursion is cycle-guarded. Covered by `TemplatesPageTests.Renders_InheritedComposition_UnderDerivedComposedMember`.
|
||||
|
||||
**Symptom:** On `/design/templates`, the base `ReactorSide` node expands to show its composed `↳ LeakTest`. But under `CvdReactor`, the composed members `LeftReactorSide` / `RightReactorSide` (derived from `ReactorSide`) render as **flat leaves with no LeakTest** — even though they inherit it. Observed live on `wonder-app-vd03`.
|
||||
|
||||
**Root cause:** `BuildCompositionLeavesFor(owner)` recursed only over `owner.Compositions` (the template's **own** rows). A derived template's inherited composition row lives on its ancestor, and `TemplateComposition` has no `IsInherited` placeholder (unlike attributes/alarms/scripts/native-sources, which `BuildDerivedTemplate`/the #1/#2 reconcile materialize) — so the derived child's own `Compositions` is empty and the recursion found nothing. Same "derived templates don't surface inherited members" family as #1/#2, but for compositions, which the #1/#2 fix did not cover. Deploy/flatten was always correct (`TemplateResolver.ResolveAllMembers` walks the chain), so this was display-only.
|
||||
|
||||
**Note:** this is a Central UI change — it shows on `wonder-app-vd03` only after that host is redeployed with the new build.
|
||||
|
||||
@@ -38,7 +38,7 @@ Central cluster only. Sites have no user interface.
|
||||
|
||||
### Template Authoring (Design Role)
|
||||
- The `/design/templates` page uses a **split-pane layout**: a folder/template tree sidebar on the left and the editor on the right.
|
||||
- The tree shows nested `TemplateFolder` entities with their templates underneath; composition children render inline as leaf nodes beneath their owning template (right-click "Open composed template" reveals and selects the target).
|
||||
- The tree shows nested `TemplateFolder` entities with their templates underneath; composition children render inline beneath their owning template (right-click "Open composed template" reveals and selects the target). Compositions are shown by **effective set** (own + inherited): a derived/composed member surfaces the slots it inherits from its base — e.g. `LeakTest` composed onto base `ReactorSide` appears under each derived `…ReactorSide` member — badged **"inherited"** and read-only (Rename/Delete are offered on the base's own slot, not the inherited copy).
|
||||
- **Per-kind context menus** on folder, template, and composition nodes expose the relevant operations (new folder, new template, rename, move, delete, move to folder). Root-level folders also carry a context menu. **Folder sibling reorder** is done via **Move up / Move down** menu items (M9/T23, `ReorderTemplateFolderCommand`); drag-drop is **not implemented** (permanently deferred). Tree expansion state persists in `sessionStorage`, and deep links (`/design/templates/{id}`) reveal and select the target node.
|
||||
- A **search box** above the tree (M9/T22) filters visible nodes by substring match; it is wired to `TemplateFolderTree.Filter`.
|
||||
- The `TemplateEdit` page shows a read-only **"Inherited members" panel** (M9/T26) listing the full multi-level effective inherited member set (origin, locked state, merged HiLo config) resolved by `GetResolvedTemplateMembersCommand` / `TemplateInheritanceResolver`. A **"Base changed" banner** appears when the resolver's staleness summary indicates the parent template has changed since the child was last edited. This is read-only — no "update-derived" mutation is exposed; the child is redeployed through the normal flow to pick up base changes.
|
||||
|
||||
Reference in New Issue
Block a user