docs(client/windows): the harness passes where the real backend fails, and the note says so
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 <noreply@anthropic.com>
This commit is contained in:
@@ -20,9 +20,16 @@
|
|||||||
//!
|
//!
|
||||||
//! * A child's *sync* `use_state` re-renders it — including under element-equal
|
//! * A child's *sync* `use_state` re-renders it — including under element-equal
|
||||||
//! non-component wrappers (`border`, `scroll_viewer`), and including writes from
|
//! non-component wrappers (`border`, `scroll_viewer`), and including writes from
|
||||||
//! backend-wired event handlers (`sync_state_child_under_element_equal_border_rerenders`,
|
//! handlers fired through the harness backend
|
||||||
//! `sync_state_from_backend_fired_event_rerenders`). Per-screen UI state driven by user
|
//! (`sync_state_child_under_element_equal_border_rerenders`,
|
||||||
//! events may therefore live in the screen's own component.
|
//! `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
|
//! * 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
|
//! 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 —
|
//! 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
|
//! (`async_state_child_under_element_equal_border_rerenders` asserts the drop). So
|
||||||
//! everything THREAD-driven (discovery, HUD stats, speed-test results, spawn events)
|
//! everything THREAD-driven (discovery, HUD stats, speed-test results, spawn events)
|
||||||
//! is held as *root* state and passed down as props.
|
//! 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 connect;
|
||||||
mod help;
|
mod help;
|
||||||
|
|||||||
@@ -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 -----------------
|
// --- Case 3: sync `use_state` written from a backend-fired event handler -----------------
|
||||||
// The client hoists `forget`/`rename`/`hover` to root because WinUI-wired handlers
|
// 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
|
// (MenuFlyout `add_Click`, ComboBox selection, pointer enter/exit) mark dirty without ever
|
||||||
// pumping the reconciler. The harness fires through the same backend attach path.
|
// 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 {
|
struct ToggleInner {
|
||||||
renders: Rc<Cell<u32>>,
|
renders: Rc<Cell<u32>>,
|
||||||
|
|||||||
Reference in New Issue
Block a user