Skip to content

Commit 295e80a

Browse files
spec(047): Phase 3-final — §14 progress note + tracker close-out
Documents the eight Phase 3-final batches (A engine shapes; B Frame / RichTextBlock / NumberBox; C CalendarView via CollectionDiffControlled; D Button-family Flyout via OneWayBridged + CreateFlyoutForDescriptor; E Panel per-child attached props + WrapGrid; F Image events / Path.Data / InfoBar.ActionButton; G-prep ItemsHost IList<object> typing + descriptor-side ordering fix; G1 flat ItemsHost ports for ListBox / ComboBox / RadioButtons) and the deliberate carve-outs to Phase 4 (Expander.HeaderTemplate, RelativePanel per-child attached, TeachingTip.Target, Path.PathDataString, NumberBox coercion, and the G2/G3 templated lists which need a new TemplatedItems strategy + spec-042 keyed-reconcile integration). Tracker marks the Batch 3-followup line as addressed via Phase 3-final Batches B and C. ARM64 stable-AC re-capture on LAPTOP-4MEP83VI remains deferred for the §14 ratification gate (unchanged from Phase 3 advisory state).
1 parent 9d9fa67 commit 295e80a

2 files changed

Lines changed: 109 additions & 9 deletions

File tree

docs/specs/047-extensible-control-model.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1376,6 +1376,28 @@ See §13 Q1 for the full capture lineage and matrix application. Raw data under
13761376
- Phase 3 follow-ups (need new builder/entry shapes — separate spec-reviewed PR): `NumberBox` (Immediate-mode keystroke + `NumberFormatter` ref-equality), `RichTextBlock` (incremental Paragraphs/Inlines diff), `FrameElement` (mount-only entry shape), `CalendarViewElement` (collection-diff with per-element echo). Within-control partial-port gaps (Flyout children, items collections, per-child attached props, IconSource, Path.Data) tracked in `docs/specs/tasks/047-extensible-control-model-implementation.md`. Templated lists (`ListView`, `GridView`, `TreeView`, `FlipView`, `TabView`, `Pivot`, `ItemsRepeater`) require spec-042 keyed reconciliation integrated into `ItemsHost`.
13771377
- Phase 3 advisory perf — final x64 capture under `docs/specs/047/phase3-results/CPC-ander-YTZ3O-x64-advisory/2026-05-27-phase3-final-3x5/` (50 controls registered, 3×5 launches). V1 ON (descriptors) vs V1 OFF (today) headline: M1 +14.9%, M7 +7.4%, M8 +25.5%, M10 +8.7%, M11 +8.5%, M12 +20.9% — descriptor-interpreter Mount/Update overhead amortized over the larger registration table. M4 −21.2% / M5 −24.3% — dispatch wins from a fatter handler table (fewer fallthroughs to the legacy switch arm). Cloud-PC advisory only; ARM64 stable-AC re-capture on `LAPTOP-4MEP83VI` is deferred for the §14 ratification gate.
13781378

1379+
**Phase 3-final descriptor scale-out** (delivers the follow-ups listed above + within-control partial-port gaps from PR #435 batches 3–11):
1380+
1381+
- **Batch A — engine shapes** (`.OneWayBridged`, `.Immediate`, `.CollectionDiffControlled`, `Panel.PerChildAttached`, `ItemsHost<TElement,TControl>` flat, `Reconciler.CreateFlyoutForDescriptor`). Carries no controls; enables the rest.
1382+
- **Batch B**`FrameElement` (.HandCodedEvent triple — gates on callback-at-mount; legacy unconditional subscription stays preferable for late-attached callbacks), `RichTextBlockElement` (reference-equality rebuild on Paragraphs; legacy incremental per-paragraph diff stays preferable for authors needing the incremental shape), `NumberBoxElement` (plain `.OneWay` Min/Max; `.CoercingOneWay` not threaded — see follow-up).
1383+
- **Batch C**`CalendarViewElement` via `.CollectionDiffControlled`. Null `SelectedDates` is treated as empty (descriptor clears the vector); legacy treats null as uncontrolled (preserves user picks) — call sites must pass a list whenever selection is controlled.
1384+
- **Batch D**`DropDownButton`/`SplitButton`/`ToggleSplitButton` Flyout child via `.OneWayBridged` + `Reconciler.CreateFlyoutForDescriptor`.
1385+
- **Batch E**`Grid`/`Canvas`/`FlexPanel` per-child attached props via `Panel.PerChildAttached`; `WrapGrid` via a tailored panel shape.
1386+
- **Batch F**`ImageElement` `ImageOpened`/`ImageFailed` via `.HandCodedEvent`; `PathElement` pre-built `Geometry Data` via `.OneWayConditional` (gated on `PathDataString` being null); `InfoBarElement.ActionButton` via `.OneWayBridged` with a Click trampoline.
1387+
- **Batch G-prep — engine ordering fix.** `ItemsHost.GetCollection` retyped from `System.Collections.IList` to `IList<object>` (WinUI `ItemCollection` does not implement the non-generic projection under CsWinRT). `DescriptorHandler` now dispatches `ItemsHost` inline between `RentControl` and the prop loop on Mount, and before the prop loop on Update, so selection-tracking initial writes (`SelectedIndex`/`SelectedItem`) land against a populated collection. Strategy shape unchanged for hand-coded handlers (V1HandlerAdapter dispatch path kept).
1388+
- **Batch G1 — flat `ItemsHost` ports.** `ListBoxElement`, `ComboBoxElement`, `RadioButtonsElement` migrate from `.OneWay<string[]>` items entries to `Children = new ItemsHost<...>(...)`. `ComboBoxElement.ItemElements` (`Element[]?`) supported alongside `Items` (`string[]`); the engine routes `Element` items through `MountChild`.
1389+
1390+
**Phase 3-final carve-outs to Phase 4** (cannot be expressed inside the current engine shape; explicit reasons):
1391+
1392+
- **Expander.HeaderTemplate** — needs `NamedSlots` but conflicts with the existing `SingleContent` strategy; one Children strategy per descriptor today.
1393+
- **RelativePanel per-child attached** — sequential `PerChildAttached` callbacks can't resolve sibling name references that haven't been mounted yet. Needs a two-pass shape on `Panel<>` or a dedicated `NamedRelativePanel` strategy.
1394+
- **TeachingTip.Target** — cross-element reference resolution to another element's mounted native control; descriptor framework cannot reference another element's resolved control.
1395+
- **PathElement.PathDataString** — legacy `XamlReader`/`PathDataParser` strategy needs string-diff against the old element + multi-source error context the engine's per-prop comparer can't express.
1396+
- **NumberBox coercion**`Minimum`/`Maximum` ship as plain `.OneWay`; `.CoercingOneWay` could be wired later.
1397+
- **Templated lists (G2/G3)**`ListView<T>`, `GridView<T>`, `LazyVStack<T>`, `LazyHStack<T>`, `ItemsRepeater<T>`, `TreeView`, `FlipView`, `TabView`, `Pivot` need a new `TemplatedItems<T,TControl>` (or equivalent) strategy with spec-042 `ReactorListState` + `KeyedListDiff` integration plus a `Reconciler.BindKeyedItemsSource` helper lifting the legacy realization-hook setup. Substantial engine design work; deferred to a follow-up batch.
1398+
1399+
ARM64 stable-AC re-capture on `LAPTOP-4MEP83VI` remains deferred for the §14 ratification gate.
1400+
13791401
**Carry-forward known defects from Phase 1:**
13801402
- **KD-3** — dispatch fast-path for the ported built-ins (M4 was +88.9% V1 vs Today at Phase 1; final advisory shows M4 −21.2% / M5 −24.3% at amortized scope — KD-3 has materially closed at the batch-11 registration set).
13811403
- **KD-4** — public typed-event surface for external descriptor authors. Scope narrowed by Phase 2 to external-author-only; in-tree descriptors already use the internal fast path via `DescriptorControlledPayload<T>` or `.HandCodedControlled`/`.HandCodedEvent` per-descriptor payload pattern.

docs/specs/tasks/047-extensible-control-model-implementation.md

Lines changed: 87 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -751,15 +751,93 @@ shrink lands after V1 ships ON by default.
751751
echo per element or require a custom collection-aware entry
752752
shape. Authors who need declarative multi-date selection stay
753753
on V1 OFF.
754-
- [ ] Batch 3-followup — `NumberBox` (needs Immediate-mode keystroke
755-
handling + `NumberFormatter` reference-equality semantics that the
756-
descriptor builders don't yet express — likely needs a new entry
757-
shape or a `HandCoded*` path). `RichTextBlock` (incremental
758-
paragraph/inline diffing — needs a child-strategy or new entry
759-
shape). `FrameElement` (needs a Mount-only entry shape for
760-
imperative `Navigate` calls — see Batch 11). `CalendarViewElement`
761-
(needs a collection-diff entry shape with per-element echo
762-
suppression — see Batch 11).
754+
- [x] Batch 3-followup — addressed by Phase 3-final Batch B (`Frame`,
755+
`RichTextBlock`, `NumberBox`) and Batch C (`CalendarView`). Engine
756+
shapes (`.Immediate`, `.OneWayBridged`, `.CollectionDiffControlled`)
757+
added in Phase 3-final Batch A. Documented residual carve-outs
758+
retained — see Phase 3-final batch entries below.
759+
760+
**Phase 3-final descriptor scale-out** (delivers the Phase 3 follow-ups
761+
plus within-control partial-port gaps from PR #435 batches 3–11):
762+
763+
- [x] **Batch A — engine shapes.** Added `.OneWayBridged<TValue>` (set
764+
lambda gets `(TControl, TValue, Reconciler, Action requestRerender)`
765+
— for dynamically-constructed child controls), `.Immediate<TPayload>`
766+
(pure subscription wiring), `.CollectionDiffControlled` (`IList<T>`
767+
two-way with hash-set diff under `BeginSuppress`),
768+
`Panel<>.PerChildAttached`, `ItemsHost<TElement,TControl>` (flat),
769+
and `Reconciler.CreateFlyoutForDescriptor`. Engine-only commit —
770+
no controls.
771+
- [x] **Batch B**`Frame`, `RichTextBlock`, `NumberBox`.
772+
- **Frame** — three `.HandCodedEvent` subscriptions
773+
(`Navigated`/`Navigating`/`NavigationFailed`) gate on
774+
callback-at-mount. Legacy `MountFrame` subscribes unconditionally
775+
so late-attached callbacks fire through. Common case covered;
776+
authors who attach callbacks after Mount stay on V1 OFF.
777+
- **RichTextBlock**`Paragraphs` rebuild via reference-equality.
778+
Legacy `UpdateRichTextBlock` does incremental per-paragraph diff;
779+
authors needing the incremental shape stay on V1 OFF.
780+
- **NumberBox** — plain `.OneWay` `Minimum`/`Maximum` (no coercion
781+
suppression). `.CoercingOneWay` could be wired later.
782+
- [x] **Batch C**`CalendarView` via `.CollectionDiffControlled`.
783+
Null `SelectedDates` is treated as empty list (descriptor clears
784+
the vector); legacy treats null as uncontrolled (preserves user
785+
picks). Call sites must pass a list whenever selection is
786+
controlled.
787+
- [x] **Batch D**`DropDownButton`/`SplitButton`/`ToggleSplitButton`
788+
Flyout child via `.OneWayBridged` + `Reconciler.CreateFlyoutForDescriptor`.
789+
Closes the Batch 4 Flyout escape-hatch.
790+
- [x] **Batch E**`Grid`/`Canvas`/`FlexPanel` per-child attached props
791+
via `Panel.PerChildAttached`; `WrapGrid` ported with a tailored
792+
panel shape. Closes the Batch 8 per-child attached gap (except
793+
`RelativePanel` — see carve-outs).
794+
- [x] **Batch F**`Image` events (`ImageOpened`/`ImageFailed` via
795+
`.HandCodedEvent` over the existing `ImageEventPayload`),
796+
`Path.Data` (pre-built `Geometry` via `.OneWayConditional` gated
797+
on `PathDataString` being null), `InfoBar.ActionButton` (via
798+
`.OneWayBridged` with a Click trampoline).
799+
- [x] **Batch G-prep — engine ordering fix.**
800+
`ItemsHost.GetCollection` retyped from `System.Collections.IList`
801+
to `IList<object>` (WinUI `ItemCollection` does not implement the
802+
non-generic projection under CsWinRT). `DescriptorHandler` now
803+
dispatches `ItemsHost` inline between `RentControl` and the prop
804+
loop on Mount, and before the prop Update loop on Update, so
805+
selection-tracking initial writes (`SelectedIndex`/`SelectedItem`)
806+
land against a populated collection. Strategy shape unchanged for
807+
hand-coded handlers — V1HandlerAdapter dispatch path is preserved.
808+
- [x] **Batch G1 — flat `ItemsHost` ports.** `ListBox`, `ComboBox`,
809+
`RadioButtons` migrate from `.OneWay<string[]>` items entries to
810+
`Children = new ItemsHost<...>(GetItems: e => (IReadOnlyList<object>)e.Items, GetCollection: c => c.Items)`.
811+
`ComboBox.ItemElements` (`Element[]?`) supported alongside `Items`
812+
(`string[]`); the engine routes `Element` items through
813+
`MountChild`. Fixtures: `Desc_ListBox_Items`, `Desc_ComboBox_Items`,
814+
`Desc_RadioButtons_Items`.
815+
816+
**Phase 3-final carve-outs to Phase 4** (cannot be expressed inside the
817+
current engine shape; explicit reasons):
818+
819+
- [ ] **Expander.HeaderTemplate** — needs `NamedSlots` but conflicts with
820+
the existing `SingleContent` strategy; one Children strategy per
821+
descriptor today.
822+
- [ ] **RelativePanel per-child attached** — sequential `PerChildAttached`
823+
callbacks can't resolve sibling name references that haven't been
824+
mounted yet. Needs a two-pass shape on `Panel<>` or a dedicated
825+
`NamedRelativePanel` strategy.
826+
- [ ] **TeachingTip.Target** — cross-element reference resolution to
827+
another element's mounted native control; descriptor framework
828+
cannot reference another element's resolved control.
829+
- [ ] **Path.PathDataString** — legacy `XamlReader`/`PathDataParser`
830+
strategy needs string-diff against the old element + multi-source
831+
error context the engine's per-prop comparer can't express.
832+
- [ ] **NumberBox coercion**`.CoercingOneWay` thread for `Minimum` /
833+
`Maximum`.
834+
- [ ] **Templated lists (G2/G3)**`ListView<T>`, `GridView<T>`,
835+
`LazyVStack<T>`, `LazyHStack<T>`, `ItemsRepeater<T>`, `TreeView`,
836+
`FlipView`, `TabView`, `Pivot` need a new `TemplatedItems<T,TControl>`
837+
(or equivalent) strategy with spec-042 `ReactorListState` +
838+
`KeyedListDiff` integration plus a `Reconciler.BindKeyedItemsSource`
839+
helper lifting the legacy realization-hook setup. Substantial engine
840+
design work; deferred to a follow-up batch.
763841

764842
**Carry-forward known defects** (from Phase 1):
765843

0 commit comments

Comments
 (0)