Files
gochat/docs/parity/contract-fixture-coverage.md
rogee 0dabb8cfa5 docs: 整理文档目录结构 — 清理过时文档、归集功能子目录、统一命名规范
清理:
- 删除 34 份过时文档(gap reports/QA临时报告/验收报告/阶段性文档)
- 删除 docs/.hermes/skills 第三方 skills 副本(16 文件)
- 删除 skills-lock.json

目录归集:
- 根目录仅保留 README.md 索引
- product/ — 产品与架构设计(PRD + ARCHITECTURE + P2设计文档 + AI/企业路线图)
- tracking/ — Chatwoot parity 开发跟踪
- requirements/ — M01-M12 模块需求
- plans/ — 历史实现计划
- parity/ — 路由 parity 与前端契约
- qa/ — QA 报告与测试计划
- ops/ — 运维部署

命名规范:
- 全小写 kebab-case,禁止全大写文件名
- product/tracking/ops 用 NN- 序号前缀
- requirements 用 MNN- 两位零填充模块号
- plans/qa 用 YYYY-MM-DD- 日期前缀
- requirements M1-M9 零填充为 M01-M09(修复字典序)

同步更新:
- backend/cmd/route_parity/main.go 路径默认值
- backend/scripts/parity_frontend_smoke.sh 报告路径
- 所有 docs 内部交叉引用
- .gitignore 排除编译产物 (backend/gochat, backend/route_parity)
- 新增迁移 000052/000053
- 前端 WS 相关修改
2026-07-09 14:53:27 +08:00

70 lines
44 KiB
Markdown

# Contract Fixture Coverage
> Updated: 2026-06-13
> Purpose: track P0/P1 Chatwoot/Woochat frontend contract evidence while aligning GoChat business-service behavior.
## Summary
This file records current focused tests that act as contract fixtures for frontend-direct-connect behavior. A row is `Covered` only when the test asserts response shape, route behavior, or side effects used by the reused Chatwoot/Woochat frontend. A row remains `Needs evidence` when only broad route existence or implementation presence is known. Current inventory backlog is 0 rows after the latest dashboard, CSAT, and widget literal closure.
## P0 / P1 Coverage Matrix
| Owner Area | Frontend Contract Source | GoChat Evidence | Current Status | Next Evidence Needed |
| --- | --- | --- | --- | --- |
| Dashboard literal APIs | `dashboard/api/specs/labels.spec.js`, `macros.spec.js`, `notifications.spec.js`, `reports.spec.js`, `search.spec.js`, `slaReports.spec.js` | label/macro/notification/analytics/search/SLA focused handler fixtures listed in `docs/parity/frontend-contract-inventory.md` | Fixture covered for labels, macros, notifications, APIV2 reports, search, and applied-SLA literal frontend paths | Keep as drift guard; add dedicated fixtures only if Woochat introduces new dashboard API literals. |
| Widget and survey literals | `widget/api/*`, `survey/api/*`, `dashboard/api/teams.js`, `summaryReports.js`, `userNotificationSettings.js`, `webhooks.js`, `yearInReview.js` | widget handler, public CSAT, team, summary-report, notification-setting, webhook, and year-in-review focused fixtures listed in `docs/parity/frontend-contract-inventory.md` | Fixture covered for all remaining frontend inventory rows; inventory has 0 `Mapped; needs fixture/smoke evidence` rows | Keep full regression, route parity, and live browser smoke as release gates before declaring product-level completion. |
| Auth/Profile/Account | `dashboard/api/endPoints.js`, `dashboard/api/account.js`, `dashboard/api/mfa.js`, `CacheEnabledApiClient.js` | API/enterprise smoke sign-in, validate-token, profile; browser login + dashboard token validation; `internal/handler/api/v1/auth_handler_test.go::TestChatwootAuthSignInReturnsDeviseHeadersAndUserPayload`; `internal/handler/api/v1/auth_handler_test.go::TestChatwootAuthValidateTokenReturnsPayloadData`; `internal/handler/api/v1/profile_handler_test.go::TestGet_Success`; `internal/handler/api/v1/profile_handler_test.go::TestSetAvailability_ReturnsChatwootUserSerializer`; `internal/handler/api/v1/profile_handler_test.go::TestSetAutoOffline_ReturnsChatwootUserSerializer`; `internal/handler/api/v1/profile_handler_test.go::TestSetActiveAccount_UpdatesMembershipActiveAt`; `internal/handler/api/v1/mfa_handler_test.go::TestProfileMFA_StatusUsesChatwootRawPayload`; `internal/handler/api/v1/mfa_handler_test.go::TestProfileMFA_EnableVerifyBackupAndDisableUseFrontendPayloads`; `internal/handler/api/v1/account_handler_test.go::TestCacheKeys_Success`; `internal/repository/account_repo_test.go::TestAccountUserRepo_UpdateActiveAt_SetsCurrentTimestamp`; auth/CORS token-header tests | Fixture covered for auth/profile bootstrap, availability, auto-offline, active account, MFA payloads, and account cache keys | Keep as drift guard; broaden only if Woochat adds new auth/account API calls. |
| SSO/Identity | `dashboard/api/samlSettings.js` | `internal/handler/api/v1/account_saml_settings_handler_test.go::TestAccountSamlSettingsHandlerTestSuite/TestChatwootFrontendCollectionCRUDPayloads`; `RegisterAccountSamlSettingsRoutes` | Fixture covered for Woochat SAML settings collection GET/POST/PUT/DELETE, `{ saml_settings: ... }` create/update wrapper, raw `response.data` payload, `sso_url`/`certificate` aliases, fingerprint/id fields, delete/not-found behavior, and legacy id-route compatibility | Keep as drift guard; end-to-end SSO login, LDAP, and OIDC remain deployment/feature-gated outside this settings API contract. |
| Inbox/Agents/Assignment | `dashboard/api/inboxes.js`, `agents.js`, `teams.js`, `assignmentPolicies.js`, `agentCapacityPolicies.js`, `assignableAgents.js` | API/enterprise smoke inbox list and agent-capacity list/users; `internal/handler/api/v1/inbox_handler_parity_test.go::TestInboxHandler_ChatwootSerializerParity`; `internal/handler/api/v1/inbox_handler_parity_test.go::TestInboxHandler_HealthReturnsWhatsAppCloudRawPayload`; `internal/handler/api/v1/inbox_handler_parity_test.go::TestInboxHandler_HealthReturnsProviderFailureState`; `internal/handler/api/v1/inbox_member_handler_test.go::TestAccountScopedInboxMembers_ChatwootPayloadAndDiffUpdate`; `internal/handler/api/v1/assignment_policy_handler_test.go::TestInboxAssignmentPolicy_ChatwootRoutes`; `internal/handler/api/v1/agent_capacity_handler_test.go::TestChatwootPolicyInboxLimitAndUserFlow`; `internal/handler/api/v1/agent_capacity_handler_test.go::TestChatwootPolicyInboxLimitAndUserValidationErrors`; `internal/handler/api/v1/facebook_callbacks_handler_test.go::TestFacebookCallbacks_RegisterFacebookPage`; `internal/handler/api/v1/tiktok_channel_handler_test.go::TestTikTokChannel_Create_ChatwootSetupPayloadAndConfig`; LINE/Twilio setup config assertions in `internal/handler/api/v1/line_channel_handler_test.go` and `internal/handler/api/v1/twilio_channel_handler_test.go`; root OAuth callback inbox config assertions in `internal/router/router_test.go::TestChannelCallbacksCreateInboxesAndRedirect`; assignable-agent route tests | Fixture covered for inbox list/detail/channel settings, members, health, provider failure state, Facebook callback-created inbox/channel config, root OAuth-created Instagram/TikTok/Twitter inbox config, TikTok/LINE/Twilio setup-created inbox/channel config, assignment-policy inbox override, agent-capacity user create/list/delete, inbox-limit create/update/delete, inbox avatar/campaigns/sync-template literal runtime paths, account create literal path, account-scoped agent bulk-create runtime path, and validation/not-found envelopes on Chatwoot literal nested paths | Keep as drift guard; add new provider callback fixtures only when Woochat adds channel setup UI calls or provider payloads change. |
| Conversation/Message | `dashboard/api/conversations.js`, `inbox/message.js`, `inbox/conversation.js`, store modules | API/enterprise smoke conversation list, message list, and message create; `internal/handler/api/v1/conversation_handler_crud_test.go::TestList_Success`; `internal/handler/api/v1/message_handler_test.go::TestCreate_ChatwootFrontendPayloadDefaultsOutgoing`; `internal/handler/api/v1/message_handler_test.go::TestCreate_MultipartAttachmentPersistsAndSerializes`; `internal/handler/api/v1/message_handler_test.go::TestDelete_Success`; `internal/handler/api/v1/message_handler_test.go::TestRetry_Success`; `internal/handler/api/v1/conversation_handler_test.go::TestUnread_DisplayIDRouteSetsLastSeenBeforeIncoming`; `internal/handler/api/v1/conversation_handler_test.go::TestUpdateLastSeen_DisplayIDRouteMarksNotificationRead`; `internal/service/message_service_test.go::TestMessageService_Update`; `internal/service/message_service_test.go::TestMessageService_Delete`; `internal/service/message_service_test.go::TestMessageService_Retry`; `internal/service/conversation_service_test.go::TestConversationService_MutationEventsCarryChatwootChangeData`; `internal/service/conversation_service_test.go::TestConversationService_ToggleTypingDispatchesChatwootPayload`; `internal/handler/api/v1/draft_message_handler_test.go::Test_CollectionDrafts_UseDisplayIDRoute`; `internal/handler/api/v1/conversation_participant_handler_test.go::Test_DisplayIDRouteEndToEnd`; `internal/handler/api/v1/conversation_serializer_test.go` serializer fixtures; `internal/ws/event_publisher_test.go::TestEventPublisher_MessageCreatedChatwootPayloadShape`; `internal/ws/event_publisher_test.go::TestEventPublisher_MessageUpdatedChatwootPayloadShape`; `internal/ws/event_publisher_test.go::TestEventPublisher_ConversationMutationChatwootPayloadShape`; `internal/ws/event_publisher_test.go::TestEventPublisher_TypingChatwootPayloadShape`; `internal/ws/event_publisher_test.go::TestEventPublisher_PresenceUpdateChatwootPayloadShape`; `internal/ws/presence_test.go::TestPresenceTracker_BroadcastsChatwootPresenceUpdatePayload`; `internal/ws/presence_test.go::TestCleanupExpired_BroadcastsChatwootOfflinePayloadAndClearsStatus` | Fixture covered for list envelope, meta sender, labels, attributes, last messages, message create, sender, content attributes, attachments, display conversation IDs, message update external-error dispatch, delete tombstones/attachment cleanup, retry status reset/external-error cleanup, unread transition last-seen rollback, update-last-seen notification read side effect, draft show/create/list display-ID routes, participants fetch/update/remove display-ID routes, conversation status/priority/labels/assignment event change data, typing on/off event data, Chatwoot `presence.update` users/contacts maps, heartbeat refresh, and expiry offline broadcasts, and `message.created`/`message.updated`/`conversation.updated`/`conversation.typing_on` realtime wire shapes | Keep as drift guard; broaden only if Woochat adds new conversation/message realtime calls. |
| CRM | `dashboard/api/contacts.js`, `companies.js`, `contactNotes.js`, labels modules | API/enterprise smoke contact and company show; `internal/handler/api/v1/crm_frontend_smoke_test.go::TestChatwootFrontendCRMSmoke`; `internal/handler/api/v1/contact_handler_crud_test.go::TestChatwootFrontendContactMergeImportExportContracts`; `internal/handler/api/v1/contact_handler_crud_test.go::TestChatwootFrontendContactsSpecRuntimeRoutes`; `internal/handler/api/v1/contact_handler_crud_test.go::TestListAttachmentsTimelineDepthMatchesChatwootFrontend`; `internal/handler/api/v1/contact_handler_crud_test.go::TestUpdatePublishesChatwootContactUpdatedEvent`; `internal/handler/api/v1/contact_handler_crud_test.go::TestDeletePublishesChatwootContactDeletedEvent`; `internal/handler/api/v1/contact_handler_crud_test.go::TestMergePublishesBaseUpdateAndMergeeDeleteEvents`; `internal/handler/api/v1/company_handler_test.go::TestUpdatePublishesChatwootCompanyUpdatedEvent`; contact/company/custom-attribute tests | Fixture covered for dashboard CRM list/show/search/update/labels/notes/company-contact paths, contacts literal-spec runtime paths, contact merge/import/export contracts, attachment timeline ordering/scoping/sender/media fields, and contact/company realtime update event payloads plus contact delete and merge source-side events | Keep as drift guard; broaden only if Woochat adds new CRM API calls. |
| Widget/Public | `widget/api/endPoints.js`, widget direct-upload components, public inbox APIs | API/enterprise smoke widget config and message create; `internal/handler/widget/widget_handler_test.go::TestWidgetHandler_ChatwootConfig_Success`; `TestWidgetHandler_ChatwootConfigPreChatFormOptionsMatchFrontendMixin`; `TestWidgetHandler_ChatwootConversationQueryTokenReusesSession`; `TestWidgetHandler_ChatwootConfigCwConversationReusesContactInbox`; `TestWidgetHandler_ChatwootConversationCookieReusesSession`; `TestWidgetHandler_ChatwootEventsAndLabels`; `TestWidgetHandler_ChatwootMessages_AuthTokenAndNestedPayload`; `TestWidgetHandler_ChatwootMessagePublishesWidgetRealtimePayload`; `TestWidgetHandler_ChatwootMessageDirectUploadAttachment`; `internal/handler/api/v1/upload_handler_test.go::TestUploadHandler_WidgetActiveStorageDirectUploadFlow`; `TestWidgetHandler_PublicAPIMessageDirectUploadAttachment`; `TestWidgetHandler_ChatwootCampaigns_Success`; `TestWidgetHandler_PublicAPIInboxContactConversationMessageFlow`; public API contact/conversation/message tests | Fixture covered for widget config/contact token bootstrap, Chatwoot `preChatFormEnabled`/`preChatFormOptions` frontend mixin shape, `cw_conversation` popout query and cookie session reuse, config boot contact-inbox reuse without creating a new visitor, message create/list envelopes, widget `message.created` pubsub-token realtime delivery, labels/events with header/query/cookie auth, ActiveStorage widget direct-upload create/complete fields, direct-upload attachment payloads, campaigns, public contact update shape, public conversation status/read flows, core public API message windows, and browser-smoke harness coverage for reused widget boot plus messages/inbox-members/campaign requests | Keep as drift guard; rerun live browser smoke when widget SDK requests change. |
| Automation/Macros/Campaigns | `dashboard/api/automation.js`, `dashboard/api/macros.js`, `dashboard/api/campaigns.js`, `dashboard/api/cannedResponse.js`, `dashboard/api/bulkActions.js` | API/enterprise smoke automation and macros; `internal/handler/api/v1/automation_rule_handler_test.go::TestChatwootFrontendCRUDCloneTogglePayloadsAndValidation`; `internal/handler/api/v1/campaign_handler_test.go::TestChatwootFrontendPayloadsAndLifecycleUseDisplayID`; `internal/handler/api/v1/campaign_handler_test.go::TestStartCreatesCampaignConversationsAndMessages`; `internal/handler/api/v1/macro_handler_test.go::TestExecute_UsesConversationDisplayIDsAndMutatesConversation`; `internal/handler/api/v1/macro_handler_test.go::TestExecute_ChatwootFrontendAwaitsEmptyOKAndSupportsSingleConversationID`; canned-response CRUD/search fixtures in `internal/handler/api/v1/canned_response_handler_test.go`; automation-rule, bot-rule, campaign, macro, and bulk-action suites; `internal/handler/api/v1/bulk_action_handler_test.go::TestBulkActionHandler_ConversationEnqueuesChatwootPayload`; `internal/service/conversation_maintenance_worker_test.go::TestConversationMaintenanceJobsConversationBulkActionQueuesSearchIndex`; `internal/service/conversation_maintenance_worker_test.go::TestConversationMaintenanceJobsContactBulkActionQueuesSearchIndexAndCompletes`; `internal/service/conversation_maintenance_worker_test.go::TestConversationMaintenanceJobsTriggerScheduledItemsFanOut`; `internal/service/conversation_maintenance_worker_test.go::TestConversationMaintenanceJobsProcessCampaignSnoozeAndResolution`; `internal/service/conversation_maintenance_worker_test.go::TestConversationMaintenanceJobsCampaignTriggerDispatchesEvents` | Fixture covered for automation-rule create/list/update/clone/toggle/delete payloads, validation error envelope, Chatwoot condition/action param normalization, campaign nested `{ campaign: ... }` create/update payloads, Chatwoot display-id route semantics, campaign start/stop response envelopes, campaign trigger conversation/message side effects, scheduled campaign trigger job dispatch payload/idempotency/completion, campaign trigger conversation/message/opened/outgoing event dispatch, dashboard audience/trigger/template JSON shapes, frontend audience-array trigger tolerance, macro execute side effects against display/single conversation IDs, empty-200 execute response awaited by the frontend, validation envelope for missing body, canned-response raw CRUD/search payloads, and bulk-action enqueue/progress completion plus conversation/contact search-index side effects | Keep as drift guard; re-open only if Woochat changes macro execution to consume a non-empty response body. |
| Help Center | `dashboard/api/helpCenter/*`, `widget/api/endPoints.js` help-center article paths | `internal/handler/api/v1/portal_handler_test.go::TestPublicRedirectDefaultLocale`; `TestPublicRedirectDefaultLocaleFallsBackToPortalLocale`; `TestPublicSitemap_ReturnsPublishedArticleURLs`; `internal/handler/api/v1/category_handler_test.go::TestPublicList_ReturnsChatwootCategoryPayloads`; `TestPublicGet_FiltersBySlugAndLocale`; `internal/handler/api/v1/article_handler_test.go::TestDashboardArticleCRUD_ChatwootFrontendPayloadsAndRoutes`; `internal/handler/api/v1/article_handler_test.go::TestPublicList_WidgetPopularArticles`; `TestPublicList_CategoryRouteFiltersArticlesBySlugAndLocale`; `TestPublicMarkdown_ReturnsOnlyPublishedMarkdown`; `TestPublicTrackingPixel_IncrementsPublishedArticleViews` | Fixture covered for dashboard article create/list/show/edit/update/delete payload shape and slug-portal routes, locale redirect/default fallback, sitemap URL/content type excluding drafts, category JSON/list by slug+locale, article JSON list sorting/meta/category filters, Chatwoot suffix-compatible `.md` raw markdown, and `.png` 1x1 tracking pixel side effects | Keep as drift guard; Article spec bare `/api/v1/portals/...` URLs are frontend unit-test artifacts; runtime account-scoped article paths are fixture-covered. add browser smoke only if Woochat portal UI changes. |
| CSAT/Survey | `dashboard/api/csatReports.js`, `survey/api/*`, public CSAT routes | API/enterprise smoke public CSAT show, CSAT list/metrics/download; `internal/handler/api/v1/csat_survey_handler_test.go::TestCSATFrontendContractShapes`; `internal/handler/api/v1/csat_survey_handler_test.go::TestMetrics_DashboardValueDriftFiltersMatchChatwootFrontend`; `internal/handler/api/v1/csat_survey_handler_test.go::TestUpdate_ChatwootReviewNotesAuditAndSerializerParity`; `internal/handler/api/v1/csat_survey_handler_test.go::TestPublicCsatUpdate_ChatwootPutPersistsShowPayload`; `internal/handler/api/v1/csat_survey_handler_test.go::TestPublicCsatUpdate_LockedAfter14Days`; CSAT survey handler/service tests | Fixture covered for dashboard list/metrics/download, dashboard metric value-drift filters, Chatwoot account/date-only `total_sent_messages_count`, review-note audit/serializer behavior, public PUT show/update persistence, and locked-after-14-days non-mutation behavior | Keep status-transition/job-send fixtures as follow-up drift guards. |
| Search/Indexing | `dashboard/api/search.js`, conversation search store, `dashboard/api/companies.js`, help-center article search calls | Live enterprise-browser smoke on 2026-06-13 reran Meilisearch reindex/search and captured seeded `/search/conversations`, `/search/messages`, `/search/contacts`, `/companies/search`, and `/search/articles` Chatwoot payloads as `.tmp/frontend-smoke-live/search_conversations.json`, `search_messages.json`, `search_contacts.json`, `search_companies.json`, `search_articles.json`, plus `search_reindex.log`; smoke seed creates a help-center portal/category/article; search handler/indexer tests; company/article hooks assert updated objects reach indexing; Meilisearch request/filter contract tests; release-mode fallback rejection config test | Fixture and live Meilisearch gate covered for the current reused frontend search paths | Keep as release drift guard; rerun live Meilisearch smoke/reindex after search schema or searchable mutation changes. |
| Reports/Audit | `dashboard/api/reports.js`, `summaryReports.js`, `liveReports.js`, `auditLogs.js` | Enterprise smoke audit logs; `internal/handler/api/v1/analytics_handler_test.go::TestAPIV2Reports_ChatwootPayloadShapes`; `internal/handler/api/v1/analytics_handler_test.go::TestAPIV2Reports_AllFrontendEndpointShapes`; `internal/handler/api/v1/analytics_handler_test.go::TestAPIV2Reports_TimeseriesTimezoneValueParity`; `internal/handler/api/v1/analytics_handler_test.go::TestAPIV2Reports_CSVDownloadEntrypointsMatchChatwootFrontend`; `internal/handler/api/v1/analytics_handler_test.go::TestAPIV2Reports_OutgoingMessagesCountValueParity`; `internal/handler/api/v1/live_report_handler_test.go::TestAPIV2LiveReports_ChatwootFrontendPayloadShapes`; `internal/handler/api/v1/live_report_handler_test.go::TestAPIV2LiveReports_StoreRefreshSequenceMatchesChatwootFrontend`; `internal/router/router_test.go::TestAPIV2LiveReportsRouterAuthAndAccountScope`; `internal/middleware/account_scope_test.go::TestAccountScope_RouteAccountMustMatchContext`; `internal/handler/api/v1/audit_handler_test.go::TestList_ChatwootPayloadFiltersAndSerializer`; analytics/report/audit/live-report service and handler tests | Fixture covered for audit list payloads, fixed Chatwoot page size, pagination meta, descending page order, API v2 reports.js index/summary/conversations/agents/inboxes/labels/teams/conversations_summary/conversation_traffic/bot_metrics/bot_summary endpoint shapes, frontend CSV download entrypoints, timezone-offset day grouping value parity, outgoing message count value parity, live-report frontend payloads, live-report store-refresh sequence, and live-report auth/account-scope enforcement | Keep as drift guard; broaden only if Woochat adds new audit filters or report pagination calls. |
| Notifications/Realtime/Cache | `dashboard/store/modules/notifications`, `notificationSubscription.js`, user notification settings, `helper/actionCable.js` | `internal/handler/api/v1/notification_handler_test.go` list/read/unread/delete/destroy-all fixtures; `internal/handler/api/v1/notification_handler_test.go::TestNotificationHandler_MutationsPublishChatwootRealtimePayload`; `internal/handler/api/v1/notification_subscription_handler_test.go::TestNotificationSubscriptionCreateAcceptsPushHelperPayload`; `internal/handler/api/v1/notification_subscription_handler_test.go::TestNotificationSubscriptionCreateAcceptsAccountScopedPushHelperPayload`; `internal/handler/api/v1/notification_setting_handler_test.go::TestNotificationSettingHandlerSuite/TestUpdate_ReturnsRawChatwootPayload`; `internal/ws/event_publisher_test.go::TestEventPublisher_NotificationChatwootPayloadShape`; `internal/ws/event_publisher_test.go::TestEventPublisher_AccountCacheInvalidatedChatwootPayloadShape`; `internal/ws/event_publisher_test.go::TestEventPublisher_WidgetEvent_PubsubTokenRoomDelivery`; `internal/handler/ws/subscriber.go` topic bridge for notification updated/deleted and account cache invalidation | Fixture covered for notification list/read-state/delete REST behavior, source-side read/unread/snooze/delete realtime publication, PushHelper browser subscription create/destroy including account-scoped routes, user notification settings show/update selected flag persistence, `notification.created`/`notification.updated`/`notification.deleted` realtime payloads, `account.cache_invalidated.cache_keys` label/inbox/team revalidation, and widget `message.created` delivery to the contact `pubsub_token` room | Keep as drift guard; add browser-store smoke evidence if Woochat changes subscription/preference UI behavior. |
| Enterprise/Captain | `dashboard/api/captain/*`, `customRole.js`, `sla.js`, enterprise account APIs | Enterprise API and browser route-request smoke passed for SLA, CSAT, automation, macros, audit, custom roles, capacity, Captain, and Copilot; `internal/handler/api/v1/enterprise_account_handler_test.go::TestEnterpriseAccountLiteralFrontendPathsUseCurrentAccountContext`; `internal/handler/api/v1/enterprise_account_handler_test.go::TestEnterpriseAccountBillingErrorBoundariesDoNotMutateAccount`; `internal/handler/api/v1/enterprise_account_handler_test.go::TestEnterpriseAccountLimits_CaptainUsageDoesNotExposeNegativeAvailability`; `internal/handler/api/v1/enterprise_account_handler_test.go::TestEnterpriseAccountSubscriptionPreservesExistingCustomerState`; `internal/handler/api/v1/custom_role_handler_test.go::TestChatwootPermissionSetCreateUpdateListShowParity`; `internal/handler/api/v1/captain_assistant_handler_test.go::TestCaptainAssistantHandler_PlaygroundLegacyNoLLMFallback`; `internal/handler/api/v1/captain_assistant_handler_test.go::TestCaptainAssistantHandler_PlaygroundV2AppendsCurrentMessageOnce`; `internal/handler/api/v1/captain_assistant_handler_test.go::TestCaptainAssistantHandler_PlaygroundV2ProviderErrorReturnsChatwootFallback`; `internal/handler/api/v1/captain_resource_parity_handler_test.go::TestCaptainDocumentHandler_ChatwootDocumentPayloadsAndSync`; `internal/handler/api/v1/captain_resource_parity_handler_test.go::TestCaptainAssistantResponseHandler_ChatwootResponsePayloadsAndFilters`; `internal/service/captain_document_service_test.go::TestCaptainDocumentService_RequestSyncQueuesDurableJob`; `internal/service/captain_document_service_test.go::TestCaptainDocumentService_RequestCrawlQueuesParserJobs`; `internal/service/captain_document_service_test.go::TestCaptainDocumentService_ResponseBuilderCreatesResponsesAndEmbeddingJobs`; `internal/handler/api/v1/copilot_thread_handler_test.go::TestCopilotThreadCreateProviderDisabledReturnsStableUnavailableAssistantMessage`; `internal/handler/api/v1/copilot_thread_handler_test.go::TestCopilotThreadCreateProviderEnabledPersistsGeneratedAssistantMessage`; `internal/service/copilot_response_worker_test.go::TestCopilotResponseJobForFollowupMessageUsesStoredThreadHistoryAndIdempotency`; `internal/handler/api/v1/sla_policy_handler_test.go::TestSlaPolicyHandler_InboxAssociationChatwootPayloadAndSideEffects`; captain/custom-role/SLA/agent-capacity tests | Fixture covered for enterprise browser route smoke, Chatwoot enterprise account literal-path limits/billing actions, Captain limit availability clamping, subscription existing-customer no-op side effect, and billing error no-mutation boundaries, Chatwoot custom-role permission key create/update/list/show parity, Captain document list/show/sync payload keys plus `assistant_id`/`filter`/`source`/`search_key` filters, Captain assistant-response payload keys plus `assistant_id`/`document_id`/`status`/`search` filters and `Captain::Document` documentable shape, Captain document sync/crawl response-builder job queue/idempotency payloads, legacy `CaptainDocument` cleanup, generated approved FAQ responses, and embedding job fan-out, Captain playground legacy provider-disabled fallback, V2 provider-enabled response shape/history handling, V2 provider-error Chatwoot `conversation_handoff` fallback, Copilot thread provider disabled/enabled message persistence, background Copilot follow-up job payload/history/idempotency, and SLA inbox association response/DB side effects | Keep as drift guard; broaden only when Woochat adds new Captain/Copilot provider modes. |
| Integrations/Channels | `dashboard/api/channel/*`, `integrations/*` | provider/channel service and webhook tests; Dyte meeting/add-participant handler fixtures; Linear teams/team-entities/create/link/unlink/search/linked-issues handler fixtures using fake GraphQL; Slack create/delete/list-all-channels handler fixtures using fake HTTP; generic app/hook no-trailing-slash fixtures; WhatsApp health raw-payload and provider-failure fixtures in `internal/handler/api/v1/inbox_handler_parity_test.go`; Facebook page setup/provider-failure/reauthorize fixtures in `internal/handler/api/v1/facebook_callbacks_handler_test.go`; root OAuth callback redirect/config fixture, existing-channel upsert fixture, email OAuth provider-error fallback fixture, and Linear/Shopify/Notion provider-error fallback fixture in `internal/router/router_test.go`; Telegram webhook success/failure reauthorization fixture in `internal/channel/provider/telegram_test.go`; TikTok setup payload/config fixture in `internal/handler/api/v1/tiktok_channel_handler_test.go`; LINE/Twilio setup payload/config fixtures in `internal/handler/api/v1/line_channel_handler_test.go` and `internal/handler/api/v1/twilio_channel_handler_test.go`; WhatsApp delivered/read/failed-delivery, Twilio delivered/read/failed-delivery, TikTok read-receipt, LINE valid/missing/invalid signature, and `TestFacebookWebhookDeliveryReceiptPersistsDeliveredStatus`/`TestFacebookWebhookReadReceiptPersistsReadStatusForContactConversation` and `TestInstagramWebhookDeliveryReceiptPersistsDeliveredStatus`/`TestInstagramWebhookReadReceiptPersistsReadStatusForContactConversation` fixtures in `internal/handler/webhook/webhook_lookup_test.go` | Fixture covered for current reused frontend provider/channel paths; WhatsApp signed status callback now persists delivered/read/failed message statuses, delivery status timestamps, status events, frontend-visible `external_error` content attributes for failures, and duplicate same-status callbacks as idempotent no-ops; Twilio delivery-status callback now normalizes callback phone numbers, persists delivered/read/failed message statuses, delivery status rows, status events, and frontend-visible `external_error` for failures, and treats duplicate status callbacks as idempotent no-ops without duplicate realtime events; TikTok read receipts now persist read message status, delivery status, status event, and duplicate read-receipt retries as idempotent no-ops; contact-level read receipts now also backfill per-contact delivery status rows for messages already at the target read status (`TestIncomingPersisterQueuesContactMessagesStatusUpdateWithWorker`); LINE webhooks now cover valid, missing-signature, and invalid-signature behavior without creating phantom messages; Facebook and Instagram delivery/read receipts now persist delivered/read message statuses, delivery status rows, status events from their Chatwoot-compatible webhook endpoints, and duplicate receipt retries as idempotent no-ops; Facebook `facebook_pages.json` returns Chatwoot `data.page_details`/`user_access_token` and provider failures as 422, `register_facebook_page` returns the raw inbox payload, and `reauthorize_page` updates persisted page config; root Instagram/TikTok/Twitter OAuth callbacks now persist serializer-readable inbox channel config before redirecting Woochat to inbox setup, and repeated Google/Instagram/TikTok/Twitter callbacks refresh existing channel/inbox config and redirect to settings, while Google/Microsoft token payload errors redirect to the frontend fallback without creating phantom inboxes; Linear/Shopify/Notion OAuth token payload errors redirect to stable Chatwoot frontend destinations and do not create phantom integration hooks; Telegram webhook setup success stores `webhook_url`, and setup failure marks `reauthorization_required` instead of silently exposing a working channel; TikTok setup now returns a stable raw `{channel,inbox}` payload and persists `tiktok_business_id`/`access_token` channel config even when provider setup is disabled; LINE and Twilio setup now assert raw inbox payloads plus persisted channel linkage/config | Keep as drift guard; add signed fixtures when Woochat or provider payloads introduce new delivery states. Generic `integrations.js` apps/hooks, Slack, and Shopify frontend paths are covered by `TestIntegrationHookHandlerSuite/TestChatwootFrontendAppsAndHooksNoTrailingSlashPayloads`, Slack handler/service fixtures, and `TestShopifyIntegration_Auth_ReturnsChatwootRedirectPayload`. |
## Current Focused Commands
```bash
GOCACHE=/tmp/go-build-cache GOMODCACHE=/tmp/go-mod-cache go test ./internal/handler/api/v1 -run 'TestAgentCapacityHandlerSuite|TestArticleHandlerSuite'
GOCACHE=/tmp/gochat-gocache GOMODCACHE=/tmp/gochat-gomodcache go test ./internal/handler/api/v1 -run 'TestInboxHandler_ChatwootSerializerParity|TestInboxHandler_HealthReturnsWhatsAppCloudRawPayload|TestInboxHandler_HealthReturnsProviderFailureState|TestInboxMemberHandlerSuite|TestInboxHandler_SensitiveFieldsRequireAdministratorRole|TestAgentBotHandlerSuite/TestAccountChatwootPayloadsAndRoutes|TestAgentCapacityHandlerSuite/TestChatwootPolicyInboxLimitAndUserFlow|TestAgentCapacityHandlerSuite/TestChatwootPolicyInboxLimitAndUserValidationErrors|TestAssignableAgentHandlerSuite/(TestList_StandaloneResourceUsesFrontendInboxIDsQuery|TestList_ResponseStructure)|TestAssignmentPolicyHandlerTestSuite/(TestPluralAssignmentPolicies_ChatwootPayloads|TestInboxAssignmentPolicy_ChatwootRoutes)|TestAgentHandlerSuite/TestCreateAgentChatwootFrontendPayload|TestCustomAttributeDefinitionHandler_ListChatwootFrontendPayload'
GOCACHE=/tmp/gochat-gocache GOMODCACHE=/tmp/gochat-gomodcache go test ./internal/handler/api/v1 -run 'Test(FacebookCallbacks_RegisterFacebookPage|GoogleAuthorization_ReturnsChatwootPayload|MicrosoftAuthorization_ReturnsChatwootPayload|InstagramAuthorization_ReturnsChatwootPayload|TikTokChannel_Create_ChatwootSetupPayloadAndConfig|TwilioChannel_Create_Success|TwitterAuthorization_ReturnsChatwootPayload|InboxHandler_ChatwootSerializerParity|WhatsAppCallHandler_AccountRoutesMatchFrontendAPI|WhatsAppCallHandler_ActionsAndRecordingPayloads)$' -count=1 && GOCACHE=/tmp/gochat-gocache GOMODCACHE=/tmp/gochat-gomodcache go test ./internal/router -run 'TestChannelCallbacks(CreateInboxesAndRedirect|UpdateExistingInboxesAndRedirectToSettings|RedirectErrorsToNewInbox)$' -count=1 && GOCACHE=/tmp/gochat-gocache GOMODCACHE=/tmp/gochat-gomodcache go test ./internal/handler/webhook -run 'Test(TwilioDeliveryStatusPersistsDeliveredAndReadStatuses|WhatsAppWebhookPersistsDeliveredAndReadStatuses|TikTokWebhookReadReceiptUpdatesMessageStatus|FacebookWebhookDeliveryReceiptPersistsDeliveredStatus|InstagramWebhookDeliveryReceiptPersistsDeliveredStatus)$' -count=1
GOCACHE=/tmp/gochat-gocache GOMODCACHE=/tmp/gochat-gomodcache go test ./internal/router -run 'TestTwilioVoiceRoutesServeConferenceAndPersistCallbacks$' -count=1 && GOCACHE=/tmp/gochat-gocache GOMODCACHE=/tmp/gochat-gomodcache go test ./internal/service -run 'TestWhatsAppAuthorization_(CreateEmbeddedSignupInbox|ReauthorizesExistingInbox)$' -count=1
GOCACHE=/tmp/gochat-gocache GOMODCACHE=/tmp/gochat-gomodcache go test ./internal/ws -run 'TestEventPublisher_MessageCreatedChatwootPayloadShape|TestEventPublisher_WSMessageFormat|TestEventPublisher_ConversationEvent_RoomDelivery|TestEventPublisher_WidgetEvent_PubsubTokenRoomDelivery'
GOCACHE=/tmp/gochat-gocache GOMODCACHE=/tmp/gochat-gomodcache go test ./internal/handler/api/v1 -run 'TestConversationCrudTestSuite/TestList_Success|TestMessageHandlerTestSuite/TestCreate_ChatwootFrontendPayloadDefaultsOutgoing|TestMessageHandlerTestSuite/TestList_Success|TestMessageHandlerTestSuite/TestCreate_MultipartAttachmentPersistsAndSerializes|TestConversationSerializer'
GOCACHE=/tmp/gochat-gocache GOMODCACHE=/tmp/gochat-gomodcache go test ./internal/handler/api/v1 -run 'TestConversationCrudTestSuite/(TestList_Success|TestUpdateLabels_Success|TestToggleStatus.*|TestToggleTyping.*|TestFilter_ChatwootPayload.*)|TestConversationHandlerTestSuite/(TestMeta_Success|TestUnreadCounts_Success|TestTranscript_Success)|TestMessageHandlerTestSuite/(TestList_Success|TestCreate_ChatwootFrontendPayloadDefaultsOutgoing|TestCreate_MultipartAttachmentPersistsAndSerializes|TestDelete_Success|TestRetry_Success|TestTranslate_Success)' -count=1
GOCACHE=/tmp/gochat-gocache GOMODCACHE=/tmp/gochat-gomodcache go test ./internal/handler/api/v1 -run 'TestChatwootFrontendCRMSmoke|TestContactHandlerCRUDTestSuite/(TestChatwootFrontendContactMergeImportExportContracts|TestMergePublishesBaseUpdateAndMergeeDeleteEvents|TestInitiateCall_CreatesConversationCallAndVoiceMessage)|TestCsatSurveyHandlerTestSuite/TestCSATFrontendContractShapes|TestCustomRoleHandlerSuite|TestCustomFilterHandlerSuite|TestDashboardAppHandlerSuite'
GOCACHE=/tmp/gochat-gocache GOMODCACHE=/tmp/gochat-gomodcache go test ./internal/handler/widget -run 'TestWidgetHandler_ChatwootConfig_Success|TestWidgetHandler_ChatwootMessages_AuthTokenAndNestedPayload|TestWidgetHandler_ChatwootMessagePublishesWidgetRealtimePayload|TestWidgetHandler_ChatwootMessageDirectUploadAttachment|TestWidgetHandler_ChatwootCampaigns_Success'
GOCACHE=/tmp/gochat-gocache GOMODCACHE=/tmp/gochat-gomodcache go test ./internal/handler/api/v1 -run 'TestArticleHandlerSuite/(TestDashboardArticleCRUD_ChatwootFrontendPayloadsAndRoutes|TestReorder_PositionsHashScoped|TestBulkActions_FrontendRoutes|TestBulkTranslate_FrontendRouteQueuesAndReturnsConflict)|TestCategoryHandlerSuite/(TestCreate_RawFrontendPayloadAndSlugPortal|TestList_Success|TestReorder_Success)|TestPortalHandlerSuite/(TestList_ReturnsChatwootPayloadEnvelope|TestUpdate_Success|TestDelete_BySlugReturnsOK|TestSendInstructions_ChatwootPayload)' -count=1
GOCACHE=/tmp/gochat-gocache GOMODCACHE=/tmp/gochat-gomodcache go test ./internal/handler/api/v1 -run 'TestInboxHandler_(ChatwootSerializerParity|HealthReturnsWhatsAppCloudRawPayload|HealthReturnsProviderFailureState|ChatwootCreateUpdateRequestBinding)|TestInboxMemberHandlerSuite/TestAccountScopedInboxMembers_ChatwootPayloadAndDiffUpdate|TestInboxSyncTemplates_ChatwootQueuedResponse' -count=1
GOCACHE=/tmp/gochat-gocache GOMODCACHE=/tmp/gochat-gomodcache go test ./internal/handler/api/v1 -run 'TestAuditHandlerSuite/(TestList_ChatwootPayloadFiltersAndSerializer|TestList_ChatwootPaginationMetaAndDescendingOrder)|TestAutomationRuleHandlerTestSuite/TestChatwootFrontendCRUDCloneTogglePayloadsAndValidation|TestCampaignHandlerTestSuite/TestChatwootFrontendPayloadsAndLifecycleUseDisplayID|TestCampaignHandlerSuite/TestStartCreatesCampaignConversationsAndMessages|TestMacroHandlerSuite/TestExecute_UsesConversationDisplayIDsAndMutatesConversation|TestMacroHandlerSuite/TestExecute_ChatwootFrontendAwaitsEmptyOKAndSupportsSingleConversationID|TestCannedResponseHandlerSuite/(TestList_Success|TestList_SearchParamReturnsRawRankedArray|TestCreate_RawFrontendBodyReturnsRawPayload|TestUpdate_PatchRawBodyIsAccountScoped|TestDelete_ReturnsOKEmptyAndScopesAccount)|TestBulkActionHandler_(ConversationEnqueuesChatwootPayload|ContactEnqueuesChatwootPayload|InvalidTypeMatchesChatwootPayload)' -count=1
GOCACHE=/tmp/gochat-gocache GOMODCACHE=/tmp/gochat-gomodcache go test ./internal/handler/api/v1 -run 'Test(LinearIntegration_ChatwootFrontendRuntimeRoutes|SlackIntegration_ListAllChannels_NoTrailingSlash_ReturnsChannelArray|SlackIntegration_(Create|Delete)_NoTrailingSlash_Returns|DyteIntegrationHandler(CreateMeetingReturnsMessagePayload|AddParticipantReturnsAuthToken))|TestIntegrationHookHandlerSuite/TestChatwootFrontendAppsAndHooksNoTrailingSlashPayloads|TestLabelHandlerSuite/(TestListTags_Success|TestCreateTag_RawNameCompatibility|TestUpdateTag_ChatwootPayloadAndAccountScope|TestDeleteTag_ReturnsOKAndRemovesAssociations)|TestLiveReportHandlerTestSuite/(TestAPIV2LiveReports_ChatwootFrontendPayloadShapes|TestAPIV2LiveReports_StoreRefreshSequenceMatchesChatwootFrontend)|TestMacroHandlerSuite/(TestList_Empty|TestCreate_Success|TestUpdateDeleteAuthorizationAndPayload|TestExecute_ChatwootFrontendAwaitsEmptyOKAndSupportsSingleConversationID)|TestMFAHandlerSuite/TestProfileMFA_(StatusUsesChatwootRawPayload|EnableVerifyBackupAndDisableUseFrontendPayloads)|TestNotificationSubscription(CreateAcceptsPushHelperPayload|CreateAcceptsAccountScopedPushHelperPayload|CreateAcceptsRailsWrapperAndUpdatesExisting|DestroyUsesPushTokenAndReturnsEmptyOK|DestroyAccountScopedUsesPushToken)$|TestNotificationHandler_(List_ChatwootEnvelopeAndIncludes|MutationsPublishChatwootRealtimePayload)|TestNotionIntegration_Authorization_ReturnsChatwootPayload|TestUpdateOnboarding_Success|TestSearchHandler_(GlobalSearch_UsesReferenceResultTypes|SearchConversations_ChatwootPayloadShape|SearchMessages_HydratesChatwootMessagePayload|SearchArticles_HydratesPortalAndCategoryPayload)|TestSlaPolicyHandler_(List_Success|Create_Success|InboxAssociationChatwootPayloadAndSideEffects|ListAppliedSlas_ChatwootPayloadAndFilters|GetAppliedSlaMetrics_ChatwootReportShape|GetAppliedSlaDownload_Success)' -count=1
GOCACHE=/tmp/gochat-gocache GOMODCACHE=/tmp/gochat-gomodcache go test ./internal/service -run 'TestLinearIntegrationService_ChatwootFrontendPayloads$' -count=1 && GOCACHE=/tmp/gochat-gocache GOMODCACHE=/tmp/gochat-gomodcache go test ./internal/handler/api/v1 -run 'TestShopifyIntegration_Auth_ReturnsChatwootRedirectPayload$' -count=1
GOCACHE=/tmp/gochat-gocache GOMODCACHE=/tmp/gochat-gomodcache go test ./internal/handler/api/v1 -run 'TestCaptainAssistantHandler_|TestCaptain(DocumentHandler_ChatwootDocumentPayloadsAndSync|AssistantResponseHandler_ChatwootResponsePayloadsAndFilters|ScenarioHandler_ChatwootScenarioPayloadsAndScope|CustomToolHandler_ChatwootToolPayloadsAndScope|BulkActionHandler_ChatwootResourceActions|CustomToolHandler_ChatwootTestToolPayload)$|TestCopilotThread(CreateReturnsChatwootPayload|MessagesListAndCreateUseNestedPayloads|CreateProviderDisabledReturnsStableUnavailableAssistantMessage|CreateProviderEnabledPersistsGeneratedAssistantMessage|MessagePayloadUsesThreadPushShape|MessagePushPayloadMatchesChatwootEventData)$|TestCaptainPreferences(GetReturnsRawChatwootConfig|UpdateMergesAccountModelsAndFeatures|UpdateRejectsNonAdminAndInvalidModel)$|TestCaptainTaskHandler_(Summarize_ChatwootRawPayload|Rewrite_NoProviderRawDisabled|StreamSummarize_ChatwootDisplayID)$|TestCaptainTaskExtendedHandler_(LabelSuggestion_ChatwootPostRawPayload|FollowUp_ChatwootPostUpdatesContext)$' -count=1
GOCACHE=/tmp/gochat-gocache GOMODCACHE=/tmp/gochat-gomodcache go test ./internal/service -run 'TestConversationMaintenanceJobs(TriggerScheduledItemsFanOut|ProcessCampaignSnoozeAndResolution|CampaignTriggerDispatchesEvents|ConversationBulkActionQueuesSearchIndex|ContactBulkActionQueuesSearchIndexAndCompletes)$' -count=1
GOCACHE=/tmp/gochat-gocache GOMODCACHE=/tmp/gochat-gomodcache go test ./internal/handler/api/v1 -run 'TestChatwootAuth|TestProfileHandlerSuite'
GOCACHE=/tmp/gochat-gocache GOMODCACHE=/tmp/gochat-gomodcache go test ./internal/handler/api/v1 -run 'TestChatwootAuth(SignInReturnsDeviseHeadersAndUserPayload|ValidateTokenReturnsPayloadData|SignOutRevokesRefreshSession|ResetPasswordReturnsReferenceMessage|UpdatePasswordWithResetToken)$|TestProfileHandlerSuite/(TestGet_Success|TestUpdateAvatar_Success|TestDeleteAvatar_ReturnsChatwootUserSerializer|TestSetAvailability_ReturnsChatwootUserSerializer|TestSetAutoOffline_ReturnsChatwootUserSerializer|TestSetActiveAccount_UpdatesMembershipActiveAt|TestResetAccessToken_RegeneratesTokenInChatwootUserSerializer|TestResendConfirmation_.*)$|TestFacebookCallbacks_FacebookPagesPayload' -count=1
GOCACHE=/tmp/gochat-gocache GOMODCACHE=/tmp/gochat-gomodcache go test ./internal/handler/api/v1 -run 'TestMFAHandlerSuite|TestAccountHandlerSuite/(TestCacheKeys_Success|TestUpdateActiveAt_Success)'
GOCACHE=/tmp/gochat-gocache GOMODCACHE=/tmp/gochat-gomodcache go test ./internal/ws -run 'TestEventPublisher_(NotificationChatwootPayloadShape|AccountCacheInvalidatedChatwootPayloadShape|AllEventTypes)$' -count=1
GOCACHE=/tmp/gochat-gocache GOMODCACHE=/tmp/gochat-gomodcache go test ./internal/handler/ws -count=1
GOCACHE=/tmp/gochat-gocache GOMODCACHE=/tmp/gochat-gomodcache go test ./internal/repository -run 'TestAccount(User)?Repo_UpdateActiveAt'
GOCACHE=/tmp/go-build-cache GOMODCACHE=/tmp/go-mod-cache go test ./internal/router -run TestRegisterRoutesBootsWithChatwootParityConflictGroups
GOCACHE=/tmp/go-build-cache GOMODCACHE=/tmp/go-mod-cache go test ./...
```
## Open Gates
- Live `scripts/parity_frontend_smoke.sh --enterprise-browser-smoke` passed on 2026-06-13 on a fresh Docker PostgreSQL/Redis/Meilisearch stack after migrations and seed were made repeatable; the harness captures seeded conversation/message/contact/company/article search payloads as `search_conversations.json`, `search_messages.json`, `search_contacts.json`, `search_companies.json`, and `search_articles.json`.
- Live browser smoke now covers reused Chatwoot login/dashboard boot, token validation, dashboard conversations request, widget boot, widget messages/inbox-members/campaigns endpoint checks, enterprise route requests, and no frontend-visible proxied API 4xx/5xx failures; latest browser report: `.tmp/frontend-smoke-live/browser-smoke-report.json`.
- Live enterprise API and enterprise browser smoke passed after migration/seed against GoChat `127.0.0.1:13000`, reused Chatwoot Vite `localhost:3036`, PostgreSQL `15432`, Redis `16379`, and Meilisearch `17700`; browser evidence now includes 37 checks and explicit reused-frontend requests for `/api/v1/accounts/:account_id/notifications?page=1` and `/api/v1/accounts/:account_id/notification_settings`.
- Auth/profile/account fixture coverage now asserts exact frontend-visible user/account keys for sign-in, validate-token, profile, availability, auto-offline, active-account side effects, MFA raw payloads, and account cache keys.
- Inbox fixture coverage now asserts Chatwoot-style list/detail envelopes, admin-only sensitive channel fields, inbox-member payload/diff updates, and raw WhatsApp Cloud health payloads.
- Conversation/message fixture coverage now asserts Chatwoot-style list envelopes, meta sender, labels, additional/custom attributes, last message payloads, outgoing message create defaults, sender fields, content attributes, attachments, display conversation IDs, and `message.created` WS/SSE payload shape.
- Notification/cache realtime fixture coverage now asserts the Chatwoot notifications store payload shape `{ notification, unread_count, count }` for created/updated/deleted events and the `account.cache_invalidated.cache_keys` label/inbox/team payload used by `CacheEnabledApiClient` revalidation; browser-store smoke now verifies the current notification index and profile notification-preference screens request GoChat without frontend patches.
- Fixture comparisons outside auth/profile/account are still broad and should be split into owner-slice tests as each frontend inventory row is closed; the CRM row now has frozen dashboard payload-shape assertions for the common contact/company frontend flows, including companies and contacts literal-spec runtime paths, and the widget/public row now has focused config/popout/message/direct-upload/create-complete/campaign payload-shape guards.