diff --git a/docs/CHATWOOT_PARITY_DEVELOPMENT_PLAN.md b/docs/CHATWOOT_PARITY_DEVELOPMENT_PLAN.md index 1d69499c..bc8d1605 100644 --- a/docs/CHATWOOT_PARITY_DEVELOPMENT_PLAN.md +++ b/docs/CHATWOOT_PARITY_DEVELOPMENT_PLAN.md @@ -16,10 +16,10 @@ Build GoChat as a Go backend that can directly reuse the frontend from `referenc ## Current Baseline -- Current tracking checkpoint: 2026-06-06 after `ca3c045 feat(campaigns): align chatwoot payloads`, with this implementation checkpoint prepared as `feat(campaigns): derive chatwoot scheduling`. -- Latest implementation checkpoint: this checkpoint, prepared as `feat(campaigns): derive chatwoot scheduling`. -- Latest documentation-only checkpoint: `8b378c7 docs: refresh parity tracker baseline`; this document is now the active follow-up plan and supersedes `.hermes/plans/*`. -- Worktree status at this implementation checkpoint: Campaign create/update now also mirrors Chatwoot `Campaign#ensure_correct_campaign_attributes` for reused SMS, WhatsApp, and live-chat forms: frontend bodies no longer need `campaign_type`, one-off SMS/Twilio/WhatsApp campaigns derive `campaign_type=one_off`, persist ISO `scheduled_at`, default missing one-off schedules to current UTC, live-chat/web-widget campaigns derive `campaign_type=ongoing` and clear `scheduled_at`, update accepts frontend `inbox_id`, `sender_id`, `scheduled_at`, and omitted descriptions without clearing them, and sender/inbox account scope is validated. Account campaigns already match the reused dashboard `campaigns` store and Chatwoot `CampaignsController`/Jbuilder contract for the core CRUD path: index returns a raw unpaginated array, show/create/update return raw campaign objects with `id` mapped from `display_id`, nested inbox/sender data, parsed JSON `audience/trigger_rules/template_params`, create returns `200 OK`, delete returns empty `200 OK`, PATCH update is registered for `ApiClient.update`, and no-trailing-slash index/create routes are registered for `ApiClient` URLs. Agent/settings/profile/notification/contact/custom-field/report enterprise drifts listed below remain at their prior checkpoint states. The fresh placeholder audit found remaining `chatwootParityStub` routes only in webhook handler-not-injected fallbacks, not in the reused dashboard account/contact/conversation/message/inbox critical path. P3.6 custom filters/custom attribute definitions and label CRUD are already in Review. B11.1a-B11.3e Captain/Copilot slices remain in Review; B12.1/B12.2/B12.3 smoke harnesses remain in Review; P5 durable job work through P5.13b is in Review. Next active implementation slice is the next named Phase 2/3 or Phase 6 drift from fresh reference/smoke evidence. +- Current tracking checkpoint: 2026-06-06 after `a02d11e feat(campaigns): derive chatwoot scheduling`. +- Latest implementation checkpoint: `a02d11e feat(campaigns): derive chatwoot scheduling`. +- Latest documentation-only checkpoint: this checkpoint, prepared as `docs: land help center parity plan`; this document is now the active follow-up plan and supersedes `.hermes/plans/*`. +- Worktree status at this implementation checkpoint: Campaign create/update now also mirrors Chatwoot `Campaign#ensure_correct_campaign_attributes` for reused SMS, WhatsApp, and live-chat forms: frontend bodies no longer need `campaign_type`, one-off SMS/Twilio/WhatsApp campaigns derive `campaign_type=one_off`, persist ISO `scheduled_at`, default missing one-off schedules to current UTC, live-chat/web-widget campaigns derive `campaign_type=ongoing` and clear `scheduled_at`, update accepts frontend `inbox_id`, `sender_id`, `scheduled_at`, and omitted descriptions without clearing them, and sender/inbox account scope is validated. Account campaigns already match the reused dashboard `campaigns` store and Chatwoot `CampaignsController`/Jbuilder contract for the core CRUD path: index returns a raw unpaginated array, show/create/update return raw campaign objects with `id` mapped from `display_id`, nested inbox/sender data, parsed JSON `audience/trigger_rules/template_params`, create returns `200 OK`, delete returns empty `200 OK`, PATCH update is registered for `ApiClient.update`, and no-trailing-slash index/create routes are registered for `ApiClient` URLs. Fresh help-center inspection shows portal/category/article routes still use local numeric IDs, envelopes, wrapped mutation bodies, and missing frontend PATCH/bulk endpoints; those gaps are now tracked as P3.7a-P3.7c below. Agent/settings/profile/notification/contact/custom-field/report enterprise drifts listed below remain at their prior checkpoint states. The fresh placeholder audit found remaining `chatwootParityStub` routes only in webhook handler-not-injected fallbacks, not in the reused dashboard account/contact/conversation/message/inbox critical path. P3.6 custom filters/custom attribute definitions and label CRUD are already in Review. B11.1a-B11.3e Captain/Copilot slices remain in Review; B12.1/B12.2/B12.3 smoke harnesses remain in Review; P5 durable job work through P5.13b is in Review. Next active implementation slice is P3.7a help center portal route/payload parity. - `go test ./...` passes. - Route dump succeeds with `TOTAL: 846` after adding no-trailing-slash campaign index/create aliases and the frontend-used campaign PATCH update route. - Route parity artifacts now exist under `docs/parity/` and are generated by `cmd/route_parity`. @@ -45,9 +45,11 @@ Next ordered checkpoints: | Order | Slice | Required outcome | Primary verification | | --- | --- | --- | --- | -| 1 | Phase 2/3 audit pass | Route/controller/serializer drift found by B12 or new reference inspection is captured as named slices, not free-form TODOs. | Regenerated route parity artifacts and fixture-backed serializer tests. | -| 2 | Phase 6 placeholder burn-down | Remaining account/contact/conversation/message/inbox placeholder handlers are either real Chatwoot-compatible flows or explicitly tracked as unsupported reference gaps. | `rg` placeholder audit, route smoke, and endpoint-family tests. | -| 3 | B12 optional live smoke | Run the checked smoke harness in a full PostgreSQL/Redis/Meilisearch/Vite/Chrome environment and turn failures into named slices. | `docs/parity/frontend_smoke_report.md` pass/fail entries linked to owners. | +| 1 | P3.7a help center portals | Make reused dashboard `helpCenter/portals.js` work against slug-based Chatwoot portal routes and raw Jbuilder payloads. | Focused portal handler/service tests, route dump/parity when routes change, and `go test ./...`. | +| 2 | P3.7b/P3.7c help center categories/articles | Align category/article raw bodies, slug portal lookup, PATCH routes, list/show serializers, and article bulk actions. | Focused category/article/bulk handler tests plus route dump/parity and `go test ./...`. | +| 3 | Phase 2/3 audit pass | Route/controller/serializer drift found by B12 or new reference inspection is captured as named slices, not free-form TODOs. | Regenerated route parity artifacts and fixture-backed serializer tests. | +| 4 | Phase 6 placeholder burn-down | Remaining account/contact/conversation/message/inbox placeholder handlers are either real Chatwoot-compatible flows or explicitly tracked as unsupported reference gaps. | `rg` placeholder audit, route smoke, and endpoint-family tests. | +| 5 | B12 optional live smoke | Run the checked smoke harness in a full PostgreSQL/Redis/Meilisearch/Vite/Chrome environment and turn failures into named slices. | `docs/parity/frontend_smoke_report.md` pass/fail entries linked to owners. | ## Handoff Contract @@ -65,6 +67,7 @@ Open work after the current checkpoint: | --- | --- | --- | --- | | Phase 5 jobs | Keep B9.3 in Review and open only evidence-backed drift slices for new async gaps. | `Phase 5: Background Jobs And Integrations` | Automation tests prove remaining action side effects and reference inspection proves no delayed action params remain. | | B12 smoke | Run optional live API/browser/enterprise smoke in a full PostgreSQL/Redis/Meilisearch/Vite environment and convert failures into named slices. | `B12 reused frontend verification breakdown` | `docs/parity/frontend_smoke_report.md` records checked pass/fail results and maps failures to slices. | +| P3.7 help center | Start with portal route/payload parity, then categories/articles and article bulk actions. | `Open Checkpoint Contracts`, `Phase 3` | Reused Chatwoot help-center dashboard clients can call portal/category/article APIs without adapters. | | Phase 2/3 drift | Expand tracked route/serializer fixtures when B12 exposes frontend-critical gaps. | `Phase 2`, `Phase 3`, `docs/parity/` | Route parity remains 0 missing for tracked frontend routes; serializers have reference fixtures. | | Phase 6 placeholders | Re-run placeholder audit and burn down any frontend-reachable stub. | `Phase 6: Core Product Placeholder Burn-down` | Stub list has no reused-frontend critical path without a named owner. | @@ -87,7 +90,7 @@ This table is the shortest authoritative handoff view. If an older lower section | Priority | Workstream | Current state | Next checkpoint | Commit close rule | | --- | --- | --- | --- | --- | -| 1 | Phase 2/3 drift | Tracked route parity is 0 missing for the current critical set; notification list/action serializers, user notification-settings raw payloads, campaigns raw payload/display-id routes, Devise password reset/confirmation payloads, CRM shared attachment payloads plus fixed 100-row attachment pagination, account/settings payloads, assignable-agent payloads, agent index full-list behavior, agent create/update/delete defaults/errors/scope, label CRUD payloads, custom filters, and custom attribute definitions now match the inspected Chatwoot frontend contract. | Continue Phase 6 placeholder audit, then open the next named serializer/controller drift slice from reference/smoke evidence. | Regenerate parity artifacts when routes change and add endpoint-family fixture tests. | +| 1 | Phase 2/3 drift | Tracked route parity is 0 missing for the current critical set; notification list/action serializers, user notification-settings raw payloads, campaigns raw payload/display-id routes, Devise password reset/confirmation payloads, CRM shared attachment payloads plus fixed 100-row attachment pagination, account/settings payloads, assignable-agent payloads, agent index full-list behavior, agent create/update/delete defaults/errors/scope, label CRUD payloads, custom filters, and custom attribute definitions now match the inspected Chatwoot frontend contract. Fresh help-center inspection found slug/raw-payload/PATCH/bulk route drift. | Implement P3.7a help center portal route/payload parity first, then P3.7b categories and P3.7c articles/bulk actions. | Regenerate parity artifacts when routes change and add endpoint-family fixture tests. | | 2 | Phase 6 placeholder audit | Widget/public/webhook critical placeholders are burned down; inbox WhatsApp health/register-webhook and sync-template drift are closed; fresh `rg` audit shows only webhook nil-handler fallbacks still call `chatwootParityStub`; dashboard conversation transcript response drift is closed. | Continue targeted account/contact/conversation/message/inbox drift from reference/smoke evidence. | `rg` placeholder audit is recorded and no reused-frontend blocker is ownerless. | | 3 | B12 optional live smoke | API/browser/enterprise smoke commands are checked in; live runs need PostgreSQL, Redis, Meilisearch, Vite, and Chrome. | Run full live smoke when environment is available and map failures to the board. | `docs/parity/frontend_smoke_report.md` records pass/fail and linked owners. | | 4 | B9.3 delayed automation actions | Current reference exposes no delayed automation action params; scheduled-item work is already P5.12; `send_email_to_team` is durable and `add_sla` mutates conversation/applied SLA state. | Keep automation drift closed if future reference/smoke exposes delayed params or unsupported action shapes. | Automation worker/action fixtures verify queued team email replay, retry visibility through worker jobs, and SLA action idempotency. | @@ -106,6 +109,9 @@ These rows are the executable development plan from this point forward. A checkp | P5.13b scheduled/cached analytics | `internal/service/analytics_service.go`, `internal/service/analytics_query_helpers.go`, `internal/service/reporting_rollup_worker.go`, `internal/service/reporting_rollup_service.go`, report handlers/services, worker bootstrap | Chatwoot report controllers/services used by dashboard analytics, `Reports::DataSource`, reporting rollup/backfill jobs | Wire scheduled or lazy cached rollup freshness/idempotency and route `GET /reports` to metric timeseries instead of the summary handler. Define freshness rules for expensive rollups. | Review by `feat(reports): add analytics timeseries rollups`; report fixtures prove timeseries values are derived from persisted conversations/messages/reporting events, rollups refresh idempotently, durable `reporting:rollup_day` jobs replay, and hidden placeholder report JSON does not reappear. | | P3.6 label CRUD payload parity | `internal/handler/api/v1/label_handler.go`, `internal/service/tag_service.go`, `internal/model/tag.go`, `internal/repository/tag_repo.go`, migrations, label handler/service tests | `reference/chatwoot/app/controllers/api/v1/accounts/labels_controller.rb`, `app/views/api/v1/accounts/labels/*.json.jbuilder`, `app/models/label.rb`, `db/schema.rb`, dashboard `api/labels.js`, `store/modules/labels.js` | Account `/labels` CRUD now returns Chatwoot `{ payload: [...] }` lists, raw mutation/show payloads, empty `200 OK` delete, `title/description/color/show_on_sidebar` persistence, title lowercasing/uniqueness, account scope, nested `{ label: ... }` request bodies, legacy `name` compatibility, default color, and local association cleanup/rename side effects. | Review by `feat(labels): align chatwoot label payloads`; focused handler/service/repository tests, combined handler/service/router tests, `go test ./cmd/migrate -count=1`, full `go test ./...`, and `git diff --check` passed. | | P3.6 custom filters/custom attribute definitions payload parity | `internal/handler/api/v1/custom_filter_handler.go`, `internal/service/custom_filter_service.go`, `internal/repository/custom_filter_repo.go`, `internal/handler/api/v1/custom_attribute_definition_handler.go`, `internal/service/custom_attribute_definition_service.go`, `internal/service/custom_attribute_value_service.go`, handler/service/repository tests | `reference/chatwoot/app/controllers/api/v1/accounts/custom_filters_controller.rb`, `app/views/api/v1/accounts/custom_filters/*.json.jbuilder`, `app/views/api/v1/models/_custom_filter.json.jbuilder`, dashboard `api/customViews.js`, `store/modules/customViews.js`, `custom_attribute_definitions_controller.rb`, `_custom_attribute_definition.json.jbuilder`, dashboard `api/attributes.js`, `store/modules/attributes.js`, settings attributes screens/constants | `/custom_filters` and `/custom_attribute_definitions` now return raw Chatwoot arrays/objects instead of local envelopes, accept raw reused-frontend bodies plus Rails-style wrappers, use `200 OK` mutation responses, preserve `204` deletes, scope saved views by current user and default `filter_type=conversation`, normalize custom attribute enum integers to Chatwoot string values, and keep custom attribute value validation compatible with normalized `*_attribute` definitions plus legacy rows. | Review by `feat(custom-fields): align chatwoot saved view payloads`; focused handler/service/repository tests, combined handler/service/router tests, full `go test ./...`, and `git diff --check` must pass. | +| P3.7a help center portal route/payload parity | `internal/router/router.go`, `internal/handler/api/v1/portal_handler.go`, `internal/service/portal_service.go`, `internal/repository/portal_repo.go`, portal handler/service/repository tests | `reference/chatwoot/app/controllers/api/v1/accounts/portals_controller.rb`, `app/views/api/v1/accounts/portals/*.json.jbuilder`, dashboard `api/helpCenter/portals.js`, help-center portal store/actions | Register frontend no-trailing-slash `GET/POST /portals` and `PATCH /portals/:portal_id`; resolve `:portal_id` as Chatwoot portal slug with legacy numeric fallback; accept raw portal bodies plus `{ portal: ... }`; return raw portal/list Jbuilder payloads instead of local envelopes; make delete empty `200 OK`; return Chatwoot message payloads for `send_instructions`; normalize `ssl_status` and logo-delete responses. | Review by `feat(help-center): align portal payloads`; focused portal handler/service/repository tests, route dump/parity regeneration when routes change, full `go test ./...`, and `git diff --check` must pass. | +| P3.7b help center category route/payload parity | `internal/router/router.go`, `internal/handler/api/v1/category_handler.go`, `internal/service/category_service.go`, `internal/repository/category_repo.go`, category tests | `reference/chatwoot/app/controllers/api/v1/accounts/categories_controller.rb`, `app/views/api/v1/accounts/categories/*.json.jbuilder`, dashboard `api/helpCenter/categories.js` | Resolve portal by slug for `/portals/:portal_id/categories`; accept raw category bodies plus wrappers; add frontend `PATCH` route; return Chatwoot raw category/list serializers with locale filtering; make delete empty `200 OK`; keep account/portal scope strict. | Review by `feat(help-center): align category payloads`; focused category handler/service/repository tests, route dump/parity regeneration when routes change, full `go test ./...`, and `git diff --check` must pass. | +| P3.7c help center article and bulk-action parity | `internal/router/router.go`, `internal/handler/api/v1/article_handler.go`, `internal/service/article_service.go`, `internal/repository/article_repo.go`, article tests | `reference/chatwoot/app/controllers/api/v1/accounts/articles_controller.rb`, `app/controllers/api/v1/accounts/articles/bulk_actions_controller.rb`, `app/views/api/v1/accounts/articles/*.json.jbuilder`, dashboard `api/helpCenter/articles.js` | Resolve portal by slug; accept raw article bodies plus wrappers; add frontend `PATCH /articles/:id`; align list/search/show/create/update/delete serializers and status/category/locale params; register and implement `PATCH /articles/bulk_actions/update_status`, `PATCH /articles/bulk_actions/update_category`, and `DELETE /articles/bulk_actions/delete_articles` with Chatwoot request/response semantics. | Review by `feat(help-center): align article payloads`; focused article and bulk-action tests, route dump/parity regeneration when routes change, full `go test ./...`, and `git diff --check` must pass. | | P6 conversation transcript response parity | `internal/handler/api/v1/conversation_handler.go`, conversation handler tests | `reference/chatwoot/app/controllers/api/v1/accounts/conversations_controller.rb`, dashboard `api/inbox/conversation.js` | Account conversation transcript now follows Chatwoot's controller contract: missing email returns `422 { error: "email param missing" }`, nonblank email schedules through the existing service boundary and returns empty `200 OK`, and invalid-looking but nonblank email values are not rejected by local email format validation. | Review by `feat(conversations): align transcript responses`; focused transcript handler/service tests, combined handler/service/router tests, full `go test ./...`, and `git diff --check` must pass. | | P6 conversation custom attributes response parity | `internal/handler/api/v1/conversation_handler.go`, conversation handler tests | `reference/chatwoot/app/controllers/api/v1/accounts/conversations_controller.rb`, `app/views/api/v1/accounts/conversations/custom_attributes.json.jbuilder`, dashboard `api/inbox/conversation.js`, conversation store action | Account conversation custom attribute updates now return Chatwoot `{ custom_attributes: ... }` only, matching the store action that reads `response.data.custom_attributes`, instead of returning the full conversation serializer with unrelated fields. | Review by `feat(conversations): align custom attribute response`; focused handler tests, combined handler/service/router tests, full `go test ./...`, and `git diff --check` must pass. | | Phase 2/3 drift audit | `cmd/route_parity`, `docs/parity/*`, serializer tests | `reference/chatwoot/config/routes.rb`, controller Jbuilder views, reused frontend API clients | Convert any smoke/reference mismatch into a named route, controller, or serializer slice. Static route extraction remains acceptable until Ruby/Bundler is available. | Regenerated route parity shows 0 missing tracked frontend routes; new serializer fixtures cover the drift. | @@ -143,6 +149,7 @@ This ledger records the committed parity checkpoints that future slices should b | Commit | Scope | Verification summary | Follow-up state | | --- | --- | --- | --- | +| `docs: land help center parity plan` | Documentation-only handoff checkpoint after `a02d11e feat(campaigns): derive chatwoot scheduling`. Records the latest clean campaign baseline and turns fresh help-center reference/frontend inspection into executable P3.7a-P3.7c contracts for portals, categories, articles, and article bulk actions. | `git diff --check`. No Go code changed. | Start `feat(help-center): align portal payloads`, then continue categories/articles and route parity regeneration. | | `feat(campaigns): derive chatwoot scheduling` | Advances P3.7 campaign behavior parity beyond response envelopes by matching Chatwoot `Campaign#ensure_correct_campaign_attributes`, `campaign_params`, and the reused SMS/WhatsApp/live-chat campaign forms. Create no longer requires frontend-absent `campaign_type`; SMS/Twilio/WhatsApp inboxes derive one-off campaigns, persist ISO `scheduled_at`, and default missing schedules to current UTC; web-widget/live-chat inboxes derive ongoing campaigns and clear schedules; update accepts frontend `inbox_id`, `sender_id`, and `scheduled_at`, preserves omitted descriptions, reloads nested inbox/sender data, and validates inbox/sender account scope. | `go test ./internal/handler/api/v1 -run CampaignHandler -count=1`; `go test ./internal/service ./internal/repository -run Campaign -count=1`; full `go test ./...`; `git diff --check`. No route changes; route dump remains `TOTAL: 846`. | Continue Phase 6 placeholder audit or the next named Phase 2/3 drift from reference/smoke evidence. | | `feat(campaigns): align chatwoot payloads` | Advances P3.7 campaign/settings parity by matching Chatwoot `CampaignsController#index/show/create/update/destroy`, `_campaign.json.jbuilder`, and the reused dashboard `campaigns` API/store. Account campaigns now return raw arrays/objects instead of local success/data/meta envelopes, ignore frontend `page/per_page`, use `display_id` as the external `id`, resolve show/update/delete/start/stop through display ID with legacy primary-key fallback, serialize nested inbox/sender data plus parsed JSON `audience`, `trigger_rules`, and `template_params`, create returns `200 OK`, delete returns empty `200 OK`, and the frontend-used no-trailing-slash index/create plus `PATCH` update routes are registered. | `go test ./internal/handler/api/v1 -run CampaignHandler -count=1`; `go test ./internal/service ./internal/repository -run Campaign -count=1`; `go test ./internal/handler/api/v1 ./internal/service ./internal/repository ./internal/router -run 'Campaign\|Router' -count=1`; `go run ./cmd/dump_routes > docs/parity/gochat_routes.txt`; `go run ./cmd/route_parity`; `go test ./...`; `git diff --check`. Route dump is `TOTAL: 846`; tracked route parity remains `270 exact, 7 parameter-compatible, 0 missing`. | Continue Phase 6 placeholder audit or the next named Phase 2/3 drift from reference/smoke evidence. | | `feat(notification-settings): align chatwoot payloads` | Advances P3.1 auth/profile settings parity by matching Chatwoot `NotificationSettingsController#show/#update`, `show.json.jbuilder`, and reused dashboard `userNotificationSettings` store/API. Account notification-settings show/update now return the raw object with `id`, `user_id`, `account_id`, `all_email_flags`, `selected_email_flags`, `all_push_flags`, and `selected_push_flags`; the local `{ success, data: { notification_setting } }` wrapper is removed, nested update params are retained, and no-trailing-slash route aliases are registered for the frontend `ApiClient` URL. | `go test ./internal/handler/api/v1 -run NotificationSetting -count=1`; `go test ./internal/service -run NotificationSetting -count=1`; `go test ./internal/handler/api/v1 ./internal/service ./internal/router -count=1`; `go run ./cmd/dump_routes > docs/parity/gochat_routes.txt`; `go run ./cmd/route_parity`; `go test ./...`; `git diff --check`. Route dump is `TOTAL: 843`; tracked route parity remains `270 exact, 7 parameter-compatible, 0 missing`. | Continue Phase 6 placeholder audit or the next named Phase 2/3 drift from reference/smoke evidence. | @@ -1577,7 +1584,7 @@ Frontend-critical API groups to audit first: | P3.4 | Conversations/messages | List filters, status changes, assignment, labels, private notes, attachments, drafts, typing/read events. | Review | | P3.5 | Contacts/companies | CRUD, merge, labels, notes, custom attributes, import/export, conversations relation, and shared attachments. | Doing | | P3.6 | Labels/custom attributes/custom filters | Create/update/list behavior and exact response shapes. | Doing; label CRUD is in Review with Chatwoot `title/description/color/show_on_sidebar` payloads and raw/list response contracts, while custom attributes/custom filters remain evidence-driven follow-ups. | -| P3.7 | Notifications/reports/help center/campaigns | Frontend-visible payloads and pagination/error envelopes. | Doing; notifications, reports, and account campaign CRUD/scheduling now have focused Chatwoot payload fixtures, while help center and deeper campaign execution/provider side effects remain broader audit scope. | +| P3.7 | Notifications/reports/help center/campaigns | Frontend-visible payloads and pagination/error envelopes. | Doing; notifications, reports, and account campaign CRUD/scheduling now have focused Chatwoot payload fixtures. Help center is split into P3.7a portals, P3.7b categories, and P3.7c articles/bulk actions after direct inspection of Chatwoot controllers/Jbuilders and reused dashboard clients. | | P3.8 | Widget/public APIs | Widget init, campaigns, config, contact, conversations, messages, direct uploads, public inbox flow, public CSAT. | Doing | | P3.9 | Search payloads | Global search and entity search documents backed by Meilisearch. | Review | @@ -2086,6 +2093,7 @@ Verification milestone gates: ## Progress Log +- 2026-06-06: Documentation checkpoint prepared as `docs: land help center parity plan`; current baseline now points at `a02d11e feat(campaigns): derive chatwoot scheduling`, and direct help-center inspection is converted into P3.7a-P3.7c executable contracts. Audited Chatwoot `PortalsController`, `CategoriesController`, `ArticlesController`, `Articles::BulkActionsController`, portal/category/article Jbuilder views, and reused dashboard `helpCenter/*.js` clients. Current Go gaps are slug-vs-numeric portal resolution, local response envelopes, wrapped mutation bodies, missing no-trailing-slash/PATCH/frontend bulk routes, and non-Chatwoot delete/send-instruction status bodies. Verification for this docs-only checkpoint: `git diff --check`; next implementation commit is `feat(help-center): align portal payloads`. - 2026-06-06: P3.7 campaigns scheduling checkpoint prepared as `feat(campaigns): derive chatwoot scheduling`; audited Chatwoot `CampaignsController#campaign_params`, `Campaign#ensure_correct_campaign_attributes`, and reused SMS/WhatsApp/live-chat campaign forms. Campaign create now accepts frontend bodies without `campaign_type`, derives one-off campaigns for SMS/Twilio/WhatsApp inboxes, persists ISO `scheduled_at`, defaults missing one-off schedules to current UTC, derives ongoing campaigns for web-widget/live-chat inboxes and clears schedules, while update accepts `inbox_id`, `sender_id`, and `scheduled_at` with account-scope checks and preserves omitted descriptions. Focused campaign handler tests, service/repository campaign tests, full `go test ./...`, and `git diff --check` passed; continue Phase 6 placeholder audit or the next named Phase 2/3 drift. - 2026-06-06: P3.7 campaigns checkpoint prepared as `feat(campaigns): align chatwoot payloads`; audited Chatwoot `CampaignsController`, campaign Jbuilder views, `_campaign.json.jbuilder`, and the reused dashboard campaigns API/store. Account campaigns now return raw Chatwoot arrays/objects, ignore `page/per_page`, use `display_id` as API `id`, resolve route IDs by display ID with legacy primary-key fallback, serialize nested inbox/sender data and parsed campaign JSON fields, accept object/array campaign params, return `200 OK` for create/delete, and register frontend-used no-trailing-slash index/create plus `PATCH` update routes. Focused campaign handler tests, service/repository campaign tests, combined handler/service/repository/router tests, route dump/parity, full `go test ./...`, and `git diff --check` passed; continue Phase 6 placeholder audit or the next named Phase 2/3 drift. - 2026-06-06: P3.1 notification-settings checkpoint prepared as `feat(notification-settings): align chatwoot payloads`; audited Chatwoot `NotificationSettingsController#show/#update`, `notification_settings/show.json.jbuilder`, and the reused dashboard `userNotificationSettings` API/store. Account-scoped notification settings now return the raw Jbuilder object with flag arrays instead of the local success/data wrapper, keep nested `notification_settings` update params, and register no-trailing-slash GET/PATCH/PUT route aliases for the frontend client URL. Focused handler/service tests, combined handler/service/router tests, route dump/parity, full `go test ./...`, and `git diff --check` passed; continue Phase 6 placeholder audit or the next named Phase 2/3 drift.