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:
2026-07-29 18:31:49 +02:00
co-authored by Claude Fable 5
parent 27039e04ff
commit 2409ec9a4b
2 changed files with 20 additions and 5 deletions
+13 -3
View File
@@ -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;
+7 -2
View File
@@ -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<Cell<u32>>,