Mitra: regression coverage for back-press-during-session-ended
Verified the 2026-05-15 disconnect() fix end-to-end on emulator-5556: mitra logs in → online → accepts blast → backend force-expires → goodbye composer renders → back-press → lands on Bestie Home with online status preserved, zero flutter:E in logcat. - ts-mitra-3-08-back_press_after_session_expired_no_red_screen.yaml codifies the repro for Maestro. Extends ts-mitra-3-04 with the back-tap + home-assertion + red-screen guard. - mitra_app/CLAUDE.md adds a Pitfall section beneath the existing "no ref in dispose" rule: never mutate notifier state synchronously from deactivate() cleanup — wrap in SchedulerBinding.addPostFrameCallback or Riverpod throws "Tried to modify a provider while the widget tree was building" during the back-nav teardown. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -46,3 +46,20 @@ void dispose() {
|
||||
```
|
||||
|
||||
A lint rule (`no_ref_in_dispose` in `halo_lints`) fails `dart run custom_lint` on this pattern. When debugging "screen frozen after navigation", grep the *previous* screen's State for `void dispose()` followed by `ref\.` — that's the first suspect.
|
||||
|
||||
### Never mutate notifier `state` synchronously from `deactivate()` cleanup
|
||||
|
||||
`deactivate()` is the safe place to call `ref.read(...).cleanup()`, but if that cleanup does `state = SomeData()`, Riverpod will notify watchers while the widget tree is mid-teardown → `Tried to modify a provider while the widget tree was building.` → red error screen + engine restart (in debug) or silent state loss (in release). Real case: `mitra_chat_notifier.dart::disconnect()` was firing this on back-press from the session-ended chat screen.
|
||||
|
||||
**Rule:** when a notifier's cleanup-from-`deactivate()` path needs to reset its own state, defer the assignment to the next frame:
|
||||
|
||||
```dart
|
||||
void disconnect() {
|
||||
_cleanup(); // closing channels / cancelling subs is fine synchronously
|
||||
SchedulerBinding.instance.addPostFrameCallback((_) {
|
||||
state = const SomeInitialData();
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
The synchronous side-effects (closing the WS, cancelling timers) still happen immediately. Only the `state =` assignment is deferred, which is a no-op for users — they're navigating away anyway. Regression coverage: `.maestro/flows/ts-mitra-3-08-back_press_after_session_expired_no_red_screen.yaml`.
|
||||
|
||||
Reference in New Issue
Block a user