Files
halobestie-clone/requirement/resume-2026-05-15.md
ramadhan sjamsani a09f37135c Phase 4 checkpoint: chat-screen perf refactor + retryable blast-failure + repo-wide dispose-ref guardrail
Chat-screen performance (customer + mitra):
- Parent screens have zero `ref.watch` — only `ref.listen` for side effects
- Body extracted into its own `ConsumerStatefulWidget`; AppBar parts split
  into narrow `.select` consumers (mode, sensitivity, timer)
- Per-second timer ticks routed to dedicated providers
  (`chatRemainingSecondsProvider` + new `mitraChatRemainingSecondsProvider`)
  so WS `session_tick` frames don't invalidate the rest of the chat state

Dispose-in-ref bug fix:
- `home_screen.dart`, `payment_screen.dart`, `mitra_chat_screen.dart` —
  ref-using cleanup moved from `dispose()` to `deactivate()`. Modern
  Riverpod invalidates `ref` the moment `dispose()` runs; the resulting
  silent error corrupts the widget-tree finalize and the next screen
  appears frozen
- `halo_lints` package added at repo root with `no_ref_in_dispose` rule
  to catch this pattern in CI / IDE analysis
- `custom_lint` activated in both apps' `analysis_options.yaml`
  (was installed but never wired in — also brings `riverpod_lint`'s
  `avoid_ref_inside_state_dispose` online)
- CLAUDE.md Pitfalls section added to client_app + mitra_app

Phase 4 §3 retryable blast-failure (Option A):
- Backend `expirePairingRequest` + all-rejected use
  `recordIntermediateFailure` instead of `failPaymentSession` so the
  payment session stays `confirmed` for re-blast
- WS `pairing_failed` payload carries `is_terminal: false` on the
  retryable paths; client parses the flag and exposes `retryBlast()`
- "Coba cari lagi" CTA on S7 Timeout now re-blasts on the same payment
- Pairing service test updated to reflect the new semantics

Customer waiting-payment screen navigation patch:
- `_navigateTerminal` uses `Future.microtask` + `addPostFrameCallback`
  redundancy after a release-mode bug where polling stopped but
  `context.go` never fired, leaving the screen visually stuck on
  "menunggu pembayaran"

See requirement/resume-2026-05-15.md for next-day pickup checklist
(mitra release rebuild + S21 Ultra install + retest is the gating item).

Bundles unrelated in-flight Phase 4 §2.x work that was already on disk
(ESP screen removal, USP one-time gate scaffolding, bestie-availability
public route, OTP service edits, Maestro flow tweaks) — kept together
to avoid a partial-rebase mess.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 19:12:34 +08:00

5.7 KiB

Resume — 2026-05-15

Cross-device pickup note. Mirror of the local Claude memory project_resume_next.md so this is reachable on any machine that clones the repo. Delete this file when fully resumed.

Paused 2026-05-14 evening. Chat-screen perf refactor done in code on both apps; release rebuild + install + retest on mitra is the gating step that didn't complete (S21 Ultra unplugged before the final build could finish).

What needs doing tomorrow — in order

1. Rebuild + install mitra release on S21 Ultra

Code is on disk in mitra_app/lib/features/chat/screens/mitra_chat_screen.dart (full refactor) and mitra_app/lib/core/chat/mitra_chat_notifier.dart (timer-extraction provider). The APK currently on the S21 Ultra only has the timer-extraction fix — NOT the full body/AppBar split.

# Plug the S21 Ultra, authorize USB debugging if needed:
adb devices    # confirm device shows as `device`, not `unauthorized`

# Build + install + run:
cd mitra_app
flutter run -d <S21_DEVICE_ID> --release --dart-define=API_BASE_URL=http://<DEV_MACHINE_IP>:3000

Yesterday's IDs (will differ on a new host):

  • S21 Ultra: RRCR100NN7Z
  • Customer SM-A530F: 52002a5db8e0c46b
  • Dev machine static IP: 192.168.88.247

Backend dev server (cd backend && npm run dev) needs to be running first. The dev API_BASE_URL defaults to production if you forget the dart-define.

2. Test mitra chat under release

After install: open a chat session, send a few messages, watch the partner type. Expected:

  • Timer ticks every 1s rebuild ONLY the timer pill in the AppBar.
  • Sending/receiving messages rebuilds ONLY the body widget.
  • Typing pulses don't cause whole-screen flicker.

Bar: it should feel as snappy as the customer app does now (which is the reference point).

3. Verify customer waiting_payment_screen navigation patch

Yesterday the customer app got stuck on "menunggu pembayaran" after a payment was confirmed (polling stopped but addPostFrameCallback(context.go(...)) never fired). Patched with belt-and-suspenders in waiting_payment_screen.dart::_navigateTerminalFuture.microtask + addPostFrameCallback redundancy.

End-to-end test path:

  1. Customer app: tap "aku mau curhat" → pick tier → create payment.
  2. SQL-confirm the payment (or use the dev confirm endpoint).
  3. Watch the waiting screen — should advance off "menunggu pembayaran" into notif-gate → searching within ~3s (one poll cycle).

If still stuck: I added print instrumentation would surface debug-mode only; consider running customer in debug to capture log output.

4. If mitra chat is still laggy after #1

Next suspect: message-list rebuilds on every state change re-iterate visible ListView.builder items. Try:

  • Convert _MessageBubble to const constructor (immutable inputs).
  • Wrap bubbles in RepaintBoundary to isolate paint.

Don't touch until #1 confirms whether the body-extraction refactor was sufficient.

What landed today (already on disk / committed)

  • Dispose-in-ref fix in home_screen.dart, payment_screen.dart (customer), mitra_chat_screen.dart (mitra). Pattern: ref-using cleanup goes in deactivate(), not dispose(). Symptom of regression: next screen looks frozen after navigation, even though app is alive.
  • halo_lints package at repo root with no_ref_in_dispose rule. Wired into both apps' analysis_options.yaml. Also activates the already-installed riverpod_lint package (which ships avoid_ref_inside_state_dispose for the same case).
  • CLAUDE.md Pitfalls section added to client_app/CLAUDE.md and mitra_app/CLAUDE.md documenting the dispose-ref landmine.
  • Customer chat refactorchat_screen.dart split into _ChatHeader + _ChatBodySection + _TimerBanner. Parent has zero ref.watch.
  • Mitra chat refactormitra_chat_screen.dart mirrors customer pattern: _MitraChatBodyContent, _MitraChatTopicToggle, _MitraChatVoicePill, _MitraChatTimerAction. Plus the mitraChatRemainingSecondsProvider for per-second ticks.
  • Customer waiting screen navFuture.microtask + addPostFrameCallback redundancy at terminal status.
  • Phase 4 Option A retryable blast-failure — backend expirePairingRequest + all-rejected use recordIntermediateFailure instead of failPaymentSession; WS payload has is_terminal: false; client carries topicSensitivity through PairingFailedData; "coba cari lagi" CTA re-blasts on the same payment via retryBlast(). Test updated to match new semantics.

Hazards / gotchas to remember

  • Release mode is the bar. Debug-mode JIT on both phones (SM-A530F + S21 Ultra) was unusably laggy. Always rebuild release to test real perf.
  • node --watch doesn't pick up newly-added module files. When you add a brand-new route file or service, kill + restart the backend dev server. Don't trust the auto-reload for new files.
  • AVD on the dev host is unusable for interactive rendering — use the physical devices.
  • .claude/settings.local.json + .claude/agent-memory/ + client_app/devtools_options.yaml stay modified — local-only, never commit.

Decisions explicitly deferred

  • CI integration — user raised the topic but we punted. Scope to gather when resuming: GitHub Actions vs other; per-PR triggers; which projects (backend vitest + control_center playwright + client/mitra flutter analyze + dart run custom_lint); APK build artifacts; Maestro Cloud or self-hosted device runner.
  • Phase 4 §2.1 real-device verification — still pending from before today. See requirement/phase3.4-testing.md §1.5.1 for the runbook.
  • backend/test/services/session-timer.service.test.js — 2 pre-existing failures (uuid-string fixture bug). Unrelated to anything we touched.