Fixed
Private state mutations no longer cause false noop (#1281).
_snapshot_assigns()previously skipped all_-prefixed attrs viak.startswith("_"), so a handler that mutated only private state (e.g.self._orders) produced identical pre/post snapshots → the render was skipped → the client received a noop frame. The filter now usesview_instance._framework_attrsmembership (captured at__init__) to distinguish framework-internal_-prefixed attrs from user-defined private attrs set inmount()or event handlers. 9 regression cases intest_skip_render_private_state.py._action_statenow persists across WebSocket reconnects (#1284). The@actiondecorator populates_action_state[action_name]with{pending, error, result}so templates can reference{{ action_name.error }}. Previously_action_statewas initialized before_framework_attrscapture in__init__, putting it in the framework-internal set that_snapshot_user_private_attrsand_restore_private_stateexclude. It now initializes after the capture, so the standard user-private save/restore cycle handles it automatically. 6 regression cases intest_action_state_reconnect.py.Snapshot truncation warning for large lists/dicts (#1285).
_snapshot_assigns()now emits a one-shotlogger.warningper view class when a list has ≥100 items or a dict has ≥50 keys. These containers have truncated content fingerprints (list: only(id, length); dict: onlylen(v)instead of a key tuple), so in-place mutations inside them are not detected by auto-diff. The warning tells developers to useset_changed_keys()or assign a new reference. 10 regression cases intest_snapshot_truncation_warning.py.Change-detection unified: identity snapshots now use
_framework_attrs(#1286). The push_commands-only auto-skip path (#700) had identity snapshots at lines 3001 and 3102 that still usedk.startswith("_")to filter attrs, while_snapshot_assigns()was updated in #1281 to use_framework_attrsmembership. This meant the two detection paths could disagree on whether private state changed. Both identity snapshots now use_framework_attrs, closing the dual-path discrepancy (weakness #8 from the lifecycle audit). 8 regression cases intest_change_detection_unified.py.