Split AppInner's single /api/events subscriber (which drove mailbox counts,
the approval banner, and the first-done GitHub-star prompt) across two hooks:
- useMailboxUnread owns the unread/pending counts, the fetchUnreadCount
refresh, and the message:*/approval:* count SSE handlers; exposes refresh
(for the approval hook) and setMailboxUnreadCount (MailboxView reports its
own count via onUnreadCountChange).
- useApprovalBanner owns the approval-banner dedupe/dismiss state machine,
the task:updated + approval:requested SSE handlers, and the star + mailbox
side effects via onStarPrompt / onMailboxRefresh callbacks (KTD4: the single
task:updated subscriber is preserved; the awaiting-approval mailbox refresh
is preserved via the callback).
The showGitHubStarPrompt boolean stays inline in App (minimal surface) wired
through a stable onStarPrompt callback. App.tsx no longer imports
fetchUnreadCount, parseDateMs, the dismissal helpers, or
didEnterAwaitingApproval/didEnterDone (moved into the hooks; still
re-exported from App for the unit-test contract).
Behavior-preserving; App.test.tsx identical to before (5 pre-existing
experimental-flag failures, none introduced). Verified by typecheck, eslint,
and 8 new renderHook tests. Completes U2 + U3.