Main coves client

refactor(providers): extract viewer-state hydration, comment-tree algebra, and the admin panel master

Three god-objects were doing provider-layer work inline. Each is now a focused unit with a characterization net proving behavior is unchanged. Viewer-state hydration was copy-pasted at EIGHT call sites spanning both the provider and widget layers, each re-implementing the isAuthenticated gate and the null-provider gate. ViewerStateHydrator now owns those gates and the per-shape traversal once. It deliberately does NOT own which items a caller passes, when listeners are notified, or how a hydration throw is handled -- those genuinely differ per site, and unifying them would have been a silent behavior change disguised as cleanup. Changes: - add lib/services/viewer_state_hydrator.dart; all 8 sites delegate. Wired through main.dart DI and CommentsProviderCache. - add lib/models/comment_thread_tree.dart: pure tree algebra out of CommentsProvider (967 -> 858). No mocks needed to test it. - split communities_admin_panel.dart (1156 -> 539) into CreateCommunityForm, CommunityAvatarUploadPage, AdminTextField and a pure CommunityNameValidator. Enum page state kept deliberately: real routes would let system back pop these pages, changing MainShellScreen behavior. - fix: hoist create/upload orchestration to the admin shell. The split had moved them onto a State destroyed on every trip to the menu, so a response landing after navigation silently dropped the receipt and left submit enabled over a populated draft -- creating a duplicate community. - fix: CommentThreadTree.replaceNode returns ({tree, replaced}); nodes is now an UnmodifiableListView. The old identity side-channel conflated "node absent" with "already that instance" and left the provider's live comment list publicly mutable. - fix: rebind the hydrator in UserProfileProvider.updateAuthProvider so the auth gate is call-time again. - restore the null-provider diagnostic under assert(() {...}()); release behavior is byte-identical. Seven behaviors that look like bugs are preserved verbatim and documented at the branch that makes the choice, each pinned by a test. They are filed as issues dated 2026-08-08 rather than fixed here, so this commit stays behavior-preserving. Tests: 1463 -> 1579 passing. Every net mutation-tested before and after its refactor (10/10, 13/13, 10/10). analyze: 0 errors, infos 433 -> 420. Verified on-device signed-out and signed-in against a local dev stack. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>