docs: record inbox members checkpoint

This commit is contained in:
2026-06-05 05:31:49 +08:00
parent 82167f5dd3
commit 8f69ef490e
+32 -9
View File
@@ -16,9 +16,9 @@ Build GoChat as a Go backend that can directly reuse the frontend from `referenc
## Current Baseline
- Latest implementation checkpoint: `945782e feat(inboxes): persist chatwoot working hours`.
- Latest implementation checkpoint: `82167f5 feat(inboxes): align chatwoot inbox members`.
- Latest documentation checkpoint before this consolidation: `1f6ec3e docs: record inbox serializer checkpoint`.
- Worktree status at this planning checkpoint: clean after docs checkpoint.
- Worktree status at this planning checkpoint: clean after the matching docs checkpoint.
- `go test ./...` passes.
- Route dump succeeds with `TOTAL: 821` after adding the Chatwoot-compatible `PATCH` inbox update route.
- Route parity artifacts now exist under `docs/parity/` and are generated by `cmd/route_parity`.
@@ -88,20 +88,22 @@ This ledger records the committed parity checkpoints that future slices should b
| `0e83e8d feat(inboxes): align chatwoot inbox serializers` | Started B5 inbox/channel parity by rendering Chatwoot `api/v1/models/_inbox.json.jbuilder`-style payloads for inbox list/show/create/update/reset-secret flows, mapping local channel slugs to `Channel::*` class names, exposing channel-specific settings from `channel_config`, returning Chatwoot's async delete message, making avatar delete empty `200 OK`, and adding the frontend-used `PATCH /inboxes/:id` route. | `go test ./internal/handler/api/v1 -run 'TestInboxHandler_ChatwootSerializerParity|TestInbox' -count=1`; `go test ./internal/handler/api/v1 -count=1`; regenerated `docs/parity/gochat_routes.txt` (`TOTAL: 821`); `go run ./cmd/route_parity`; `go test ./...`; `git diff --check`. | Continue B5 with create/update request binding for Chatwoot multipart `channel[...]` payloads, working-hours/settings persistence, inbox members, and channel-specific config depth. |
| `ee93546 feat(inboxes): bind chatwoot channel settings` | Completed B5.2 request binding for reused dashboard inbox create/update flows: JSON and multipart/urlencoded bodies now accept top-level inbox settings, nested `channel[...]`, nested `csat_config[...]`, selected feature flags, API webhook config, generated web widget/API secrets, and Telegram create without explicit `name`. | `go test ./internal/handler/api/v1 -run 'TestInboxHandler_ChatwootSerializerParity\|TestInboxHandler_ChatwootCreateUpdateRequestBinding\|TestInbox' -count=1`; `go test ./internal/service -run Inbox -count=1`; `go test ./internal/handler/api/v1 -count=1`; escalated `go test ./...` after sandbox socket denial; `git diff --check`. | Continue B5.3 with durable working-hours update behavior and out-of-office parity, then B5.4 inbox members and B5.5 channel-specific config depth. |
| `945782e feat(inboxes): persist chatwoot working hours` | Completed B5.3 working-hours parity for the generic inbox update flow: inbox create initializes Chatwoot default weekly schedule where the table exists, inbox update accepts frontend `working_hours` JSON with string/empty time fields from `timeSlotTransform`, persists schedules in `working_hours`, serializes `working_hours` from the relation, and preserves `working_hours_enabled`, `out_of_office_message`, and timezone behavior. | `go test ./internal/handler/api/v1 -run 'TestInboxHandler_ChatwootSerializerParity\|TestInboxHandler_ChatwootCreateUpdateRequestBinding\|TestInbox' -count=1`; `go test ./internal/service -run 'Inbox\|WorkingHour' -count=1`; `go test ./internal/handler/api/v1 -count=1`; escalated `go test ./...`; `git diff --check`. | Continue B5.4 with inbox member payload and round-robin membership side effects, then B5.5 channel-specific config depth. |
| `82167f5 feat(inboxes): align chatwoot inbox members` | Completed B5.4 inbox member parity for reused dashboard agent assignment flows: account-scoped and nested member endpoints now return Chatwoot `{ payload: [agent] }` responses, agent payloads expose frontend-required user fields, duplicate adds are idempotent, create adds only missing users, and update performs a diff so preserved rows are not recreated unnecessarily. | `go test ./internal/handler/api/v1 -run InboxMember -count=1`; `go test ./internal/service -run InboxMember -count=1`; `go test ./internal/repository -run InboxMember -count=1`; `go test ./internal/handler/api/v1 -count=1`; `go test ./...`; `git diff --check`. | Continue B5.5 with deeper channel-specific config parity and InboxLimit enforcement on channel creation. |
## Next Slice Contract
Completed implementation slice: B4 contact merge, contact import, contact export, notes, nested conversation payloads, Meilisearch-first CRM search, and frontend CRM API smoke now cover contact/company CRUD payloads, company-contact relations, contact labels, label filtering, avatar deletion, selected custom-attribute deletion, company destroy-custom-attributes, dashboard contact merge behavior, persisted contact import lifecycle behavior, persisted contact export artifacts, Chatwoot-shaped contact/company notes, nested contact/company conversations, and the reused dashboard contacts/companies API call sequence.
Completed implementation slice: B5.1-B5.4 now cover inbox serializer shape, Chatwoot frontend create/update binding, working-hours persistence, out-of-office behavior, and inbox member assignment payload/mutation semantics. B4 remains in review after contact/company CRUD payloads, relations, labels, merge, import/export, notes, nested conversations, Meilisearch CRM search, and API smoke coverage landed.
Next implementation slice: continue B5 inbox/channel behavior fixtures. Keep B3 and B4 in review for delivery/storage side-effect parity and real browser frontend validation.
Next implementation slice: continue B5.5 channel-specific config depth and InboxLimit enforcement. Keep B3 and B4 in review for delivery/storage side-effect parity and real browser frontend validation.
| Step | Required result | Reference source | Verification |
| --- | --- | --- | --- |
| N1 | Align inbox list/show/create/update/delete/avatar/reset-secret serializer shapes. | `InboxesController`, `api/v1/models/_inbox.json.jbuilder`, dashboard inbox store. | Done by `0e83e8d`; list returns `{ payload: [...] }`, item responses are raw inbox payloads, channel slugs serialize as `Channel::*`, and route dump is `TOTAL: 821`. |
| N2 | Accept Chatwoot frontend inbox create/update request bodies, including multipart FormData and `channel[...]` nested params. | `dashboard/store/modules/inboxes.js#buildInboxData`, `InboxesController#permitted_params`. | Done by `ee93546`; handler tests cover website multipart create, API JSON create, Telegram no-name create, and settings update request bodies. |
| N3 | Persist inbox working hours and out-of-office behavior with Chatwoot defaults. | `OutOfOffisable`, `Inbox#OFFISABLE_ATTRS`, settings pages. | Done by `945782e`; update tests prove weekly schedule persistence and service tests prove out-of-office behavior. |
| N4 | Deepen inbox member/channel-specific flows. | `InboxMembersController`, channel controllers/models. | Member list/update and channel config tests return frontend-compatible payloads. |
| N5 | Update this tracker after every implementation checkpoint. | This document. | `git diff --check`; `go test ./...` for Go changes. |
| N4 | Align inbox member payloads and membership mutation semantics. | `InboxMembersController`, `Inbox#add_members/remove_members`, dashboard `inboxMembers.js`. | Done by `82167f5`; account-scoped and nested member routes return `{ payload: [agent] }`, create is add-only/idempotent, update is diff-based, and delete returns empty `200 OK`. |
| N5 | Deepen channel-specific config flows and enforce inbox limits. | WebWidget/API/Telegram/Email/WhatsApp/SMS/LINE channel models/controllers, enterprise inbox limits. | Channel create/update/show tests cover frontend settings screens and paid inbox limits block over-quota creation with Chatwoot-compatible errors. |
| N6 | Update this tracker after every implementation checkpoint. | This document. | `git diff --check`; `go test ./...` for Go changes. |
Current B2 profile checkpoint:
@@ -162,7 +164,9 @@ Current B5 inbox/channel checkpoint:
- Chatwoot dashboard create/update request bodies now bind through the generic inbox route for JSON, urlencoded forms, and multipart FormData. The binder accepts top-level inbox settings, nested `channel[...]`, `channel[selected_feature_flags][]`, and nested `csat_config[...]` values generated by `buildInboxData`.
- Create/update now persists greeting/email collection/CSAT/auto-assignment/timezone/post-resolve/single-conversation/sender/business settings, stores channel-specific fields in `channel_config`, generates web widget `website_token`/`hmac_token`, generates API `secret`/`hmac_token`/identifier, and derives a Telegram inbox name when the frontend omits `name`.
- Inbox create/update now follows Chatwoot's `OutOfOffisable` schedule lifecycle: default seven-day working hours are created for new inboxes, `working_hours` updates persist the weekly relation, empty string time fields from the frontend are normalized, open-all-day rows are expanded to `00:00`-`23:59`, and the serializer returns `weekly_schedule` shape from the relation.
- Remaining B5 gaps: inbox member payload depth and membership side effects, deeper LINE/Twilio/Email/WhatsApp channel-specific creation/config parity, and inbox limit enforcement.
- Inbox member routes now follow Chatwoot's account-scoped `InboxMembersController` contract: `GET /inbox_members/:inbox_id`, `POST /inbox_members`, and `PATCH /inbox_members` return `{ payload: [agent] }`; `DELETE /inbox_members` returns empty `200 OK`; nested local member routes use the same agent payload boundary for frontend compatibility.
- Inbox member create is add-only and idempotent for duplicate users, while update computes add/remove diffs instead of clearing and recreating rows. Returned agent payloads include `id`, `account_id`, `availability_status`, `auto_offline`, `confirmed`, `email`, `provider`, `available_name`, `name`, `role`, `thumbnail`, and enterprise `custom_role_id`.
- Remaining B5 gaps: deeper LINE/Twilio/Email/WhatsApp channel-specific creation/config parity and InboxLimit enforcement.
Active B5 task board:
@@ -171,8 +175,8 @@ Active B5 task board:
| B5.1 | Inbox CRUD serializer and route shape. | `InboxesController`, `_inbox.json.jbuilder`, dashboard inbox store/API. | Done | `0e83e8d`; serializer parity test, route dump `TOTAL: 821`, full tests. |
| B5.2 | Inbox create/update request binding and settings persistence. | `buildInboxData`, `permitted_params`, settings pages. | Done | `ee93546`; JSON and multipart `channel[...]` requests persist core inbox/channel settings. |
| B5.3 | Working hours and out-of-office parity. | `OutOfOffisable`, working hour controller/model. | Done | `945782e`; weekly schedule create/update/show and `out_of_office` service tests match Chatwoot semantics. |
| B5.4 | Inbox member payload and round-robin side effects. | `InboxMembersController`, `Inbox#add_members/remove_members`. | Todo | Account-scoped and nested member routes return `{ payload }` agents and update membership idempotently. |
| B5.5 | Channel-specific config depth. | WebWidget/API/Telegram/Email/WhatsApp/SMS/LINE channel models/controllers. | Todo | Channel create/update/show tests cover frontend settings screens. |
| B5.4 | Inbox member payload and round-robin side effects. | `InboxMembersController`, `Inbox#add_members/remove_members`. | Done | `82167f5`; account-scoped and nested member routes return `{ payload }` agents, create is add-only/idempotent, update is diff-based, and delete is empty `200 OK`. |
| B5.5 | Channel-specific config depth and inbox limits. | WebWidget/API/Telegram/Email/WhatsApp/SMS/LINE channel models/controllers, enterprise inbox limits. | Todo | Channel create/update/show tests cover frontend settings screens; InboxLimit blocks over-quota creation. |
B5.2 execution breakdown:
@@ -193,6 +197,24 @@ env GOCACHE=/tmp/gochat-gocache GOMODCACHE=/tmp/gochat-gomodcache go test ./...
git diff --check
```
B5.4 execution breakdown:
| Step | Implementation target | Reference source | Required tests | Status |
| --- | --- | --- | --- | --- |
| B5.4a | Return Chatwoot account-scoped inbox member envelopes as `{ payload: [agent] }` instead of local `{ members, meta }` model envelopes. | `reference/chatwoot/app/controllers/api/v1/accounts/inbox_members_controller.rb`, `app/views/api/v1/accounts/inbox_members/*.json.jbuilder` | `TestAccountScopedInboxMembers_ChatwootPayloadAndDiffUpdate` validates payload shape and agent fields. | Done |
| B5.4b | Serialize assigned users through the Chatwoot agent partial field set, including enterprise `custom_role_id`. | `reference/chatwoot/app/views/api/v1/models/_agent.json.jbuilder` | Handler payload assertions cover `available_name`, `availability_status`, and absence of local `inbox_id`. | Done |
| B5.4c | Match create/update/delete mutation semantics: create adds missing users only, update diffs desired membership, duplicate user IDs are idempotent, and delete returns empty `200 OK`. | `Inbox#add_members`, `Inbox#remove_members`, dashboard `inboxMembers.js` store actions. | Account-scoped handler regression creates duplicates, patches to a replacement set, and deletes back to zero. | Done |
| B5.4d | Preserve loaded `User` and `Inbox` relations for member serialization after create/list/update. | Go repository preload boundary; Chatwoot views expect account/user fields. | Repository/handler focused tests and full `go test ./...`. | Done |
B5.5 planned breakdown:
| Step | Implementation target | Reference source | Required tests | Status |
| --- | --- | --- | --- | --- |
| B5.5a | Compare current Go channel config fields against Chatwoot channel models/controllers for WebWidget, API, Telegram, Email, WhatsApp, Twilio SMS, and LINE. | `reference/chatwoot/app/models/channel/*`, channel controllers, dashboard inbox setting screens. | Add a documented matrix of supported/missing request and response fields in this file. | Todo |
| B5.5b | Persist and serialize deeper channel-specific settings used by reused settings screens, preferring first-class columns only where GoChat already has them and `channel_config` otherwise. | Chatwoot channel model validations and `InboxesController#permitted_params`. | Handler tests for each channel's create/update/show payload shape. | Todo |
| B5.5c | Enforce enterprise InboxLimit during inbox/channel creation and return frontend-compatible over-limit errors. | Chatwoot enterprise inbox limit policy/model behavior. | Service/handler tests cover under-limit create, at-limit rejection, and account-scoped counting. | Todo |
| B5.5d | Keep route artifacts unchanged unless new Chatwoot-compatible routes are registered; regenerate route dump only on route changes. | `cmd/dump_routes`, `cmd/route_parity`. | `git diff --check`; `go test ./...`; route dump if needed. | Todo |
Active B4 task board:
| ID | Task | Reference source | Status | Exit gate |
@@ -776,3 +798,4 @@ Verification milestone gates:
- 2026-06-05: Planning tracker expanded for handoff continuity. B5.2 now has executable substeps, B6-B12 have explicit first checkpoints and Done rules, and every future checkpoint has a required documentation update rule.
- 2026-06-05: B5.2 inbox request binding checkpoint committed as `ee93546 feat(inboxes): bind chatwoot channel settings`; generic inbox create/update now accepts Chatwoot frontend JSON and multipart/urlencoded FormData, persists top-level inbox settings and nested channel/CSAT config, generates web widget/API secrets, and supports Telegram creation without an explicit name. Focused inbox handler tests, service inbox tests, handler package tests, escalated full `go test ./...`, and `git diff --check` passed. Route dump unchanged at `TOTAL: 821`.
- 2026-06-05: B5.3 working-hours checkpoint committed as `945782e feat(inboxes): persist chatwoot working hours`; generic inbox create/update now initializes default working hours, accepts the reused frontend's `working_hours` payload with string and empty time fields, persists the weekly schedule relation, serializes working hours from the relation, and covers out-of-office detection. Focused inbox/working-hour tests, handler package tests, escalated full `go test ./...`, and `git diff --check` passed. Route dump unchanged at `TOTAL: 821`.
- 2026-06-05: B5.4 inbox members checkpoint committed as `82167f5 feat(inboxes): align chatwoot inbox members`; account-scoped and nested inbox member routes now return Chatwoot `{ payload: [agent] }` responses, serialize assigned users with agent partial fields including `custom_role_id`, make create add-only/idempotent, make update diff-based, and keep delete as empty `200 OK`. Focused inbox member handler/service/repository tests, handler package tests, full `go test ./...`, and `git diff --check` passed. Route dump unchanged at `TOTAL: 821`.