From 2409ec9a4bc42f6b037c3e21d7aed45517054bc0 Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Wed, 29 Jul 2026 14:05:20 +0200 Subject: [PATCH] docs(client/windows): the harness passes where the real backend fails, and the note says so MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Case 3 of the characterization suite (a sync use_state write from a backend-fired handler re-renders) is true of the ENGINE and was taken as license to de-hoist event-driven page state — and the live app immediately disproved it: with settings componentized and its scope local, a real ComboBox pick changed nothing on screen (UIA-verified, no repaint). The real WinUI backend's handler wiring still never pumps the pass the harness pumps. The de-hoist is reverted one commit back; this one makes the record honest. The module discipline note now separates the three tiers of evidence — engine rules (harness-pinned), real-backend behaviour (live-verified, hoisting stays), and the tween-keyed corollary — and case 3's comment warns it must not be read as de-hoist permission without a live UIA check. The upstream gap worth reporting grows to two: the AsyncSetState HostId registry miss, and the backend event wiring that bypasses the render pump. Co-Authored-By: Claude Fable 5 --- clients/windows/src/app/mod.rs | 16 +++++++++++++--- clients/windows/tests/reactor_semantics.rs | 9 +++++++-- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/clients/windows/src/app/mod.rs b/clients/windows/src/app/mod.rs index 82bcc6f5..09a05bea 100644 --- a/clients/windows/src/app/mod.rs +++ b/clients/windows/src/app/mod.rs @@ -20,9 +20,16 @@ //! //! * A child's *sync* `use_state` re-renders it — including under element-equal //! non-component wrappers (`border`, `scroll_viewer`), and including writes from -//! backend-wired event handlers (`sync_state_child_under_element_equal_border_rerenders`, -//! `sync_state_from_backend_fired_event_rerenders`). Per-screen UI state driven by user -//! events may therefore live in the screen's own component. +//! handlers fired through the harness backend +//! (`sync_state_child_under_element_equal_border_rerenders`, +//! `sync_state_from_backend_fired_event_rerenders`). +//! * **BUT the real WinUI backend does not honour that last rule.** Measured 2026-07-29: +//! settings was componentized with its scope as local sync state, and a live UIA pick in +//! the scope ComboBox changed nothing on screen — `on_selection_changed` wired in the +//! real backend never pumped the pass the harness pumps (the de-hoist is reverted in this +//! branch's history; repro = tests/reactor_semantics.rs case 3 passing while the same +//! flow fails live). So per-screen EVENT-driven UI state ALSO stays in root as +//! `AsyncSetState` props — hoisting here is on real-backend evidence, not engine rules. //! * An `AsyncSetState` written from a background thread does NOT re-render its owning //! component: the value lands and the dirty flag is set, but the rerender request is //! keyed by the component's own HostId, which is never registered — only the root's is — @@ -30,6 +37,9 @@ //! (`async_state_child_under_element_equal_border_rerenders` asserts the drop). So //! everything THREAD-driven (discovery, HUD stats, speed-test results, spawn events) //! is held as *root* state and passed down as props. +//! * Corollary: state a root tween is KEYED on (`screen`, the settings section, `show_add`) +//! must stay in root regardless — the tween workers are root effects writing root async +//! state, and root can only start a tween off a trigger it owns. mod connect; mod help; diff --git a/clients/windows/tests/reactor_semantics.rs b/clients/windows/tests/reactor_semantics.rs index 7dfedcae..37f7d376 100644 --- a/clients/windows/tests/reactor_semantics.rs +++ b/clients/windows/tests/reactor_semantics.rs @@ -247,8 +247,13 @@ fn async_state_child_under_element_equal_border_rerenders() { // --- Case 3: sync `use_state` written from a backend-fired event handler ----------------- // The client hoists `forget`/`rename`/`hover` to root because WinUI-wired handlers -// (MenuFlyout `add_Click`, pointer enter/exit) were believed to mark dirty without ever -// pumping the reconciler. The harness fires through the same backend attach path. +// (MenuFlyout `add_Click`, ComboBox selection, pointer enter/exit) mark dirty without ever +// pumping the reconciler. This test PASSES — the ENGINE honours a handler-driven sync +// write — but the claim it probes is about the REAL WinUI backend, and there it was +// re-confirmed live on 2026-07-29: a componentized settings page with local sync scope +// state did not repaint on a real ComboBox pick (see the discipline note in app/mod.rs; +// the de-hoist was reverted). Keep this green as the ENGINE contract; do NOT read it as +// permission to de-hoist event-driven state until a live UIA check passes too. struct ToggleInner { renders: Rc>,