From 85da0beb9351e43f0146969e2cdd5d9293e59421 Mon Sep 17 00:00:00 2001 From: Rogee Date: Fri, 5 Jun 2026 07:18:32 +0800 Subject: [PATCH] docs: record applied sla persistence checkpoint --- docs/CHATWOOT_PARITY_DEVELOPMENT_PLAN.md | 33 +++++++++++++----------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/docs/CHATWOOT_PARITY_DEVELOPMENT_PLAN.md b/docs/CHATWOOT_PARITY_DEVELOPMENT_PLAN.md index d3d0abb9..7a2d5350 100644 --- a/docs/CHATWOOT_PARITY_DEVELOPMENT_PLAN.md +++ b/docs/CHATWOOT_PARITY_DEVELOPMENT_PLAN.md @@ -16,9 +16,9 @@ Build GoChat as a Go backend that can directly reuse the frontend from `referenc ## Current Baseline -- Latest implementation checkpoint: `95224fa feat(sla): align sla policy payloads`. -- Latest documentation checkpoint before this update: `be9c8f1 docs: record capacity enforcement checkpoint`. -- Worktree status at this planning checkpoint: clean after the matching implementation checkpoint; next active slice is B7 applied-SLA persistence. +- Latest implementation checkpoint: `a11bb96 feat(sla): persist applied sla on conversations`. +- Latest documentation checkpoint before this update: `4cf68d1 docs: record sla policy payload checkpoint`. +- Worktree status at this planning checkpoint: clean after the matching implementation checkpoint; next active slice is B7 SLA timer/breach lifecycle. - `go test ./...` passes. - Route dump succeeds with `TOTAL: 829` after adding Chatwoot-compatible nested AgentCapacityPolicy users and inbox-limit routes. - Route parity artifacts now exist under `docs/parity/` and are generated by `cmd/route_parity`. @@ -35,7 +35,7 @@ Build GoChat as a Go backend that can directly reuse the frontend from `referenc | Phase 1 | Meilisearch search engine | Review | B6 payload parity, optional live gate, and DB-fallback hardening are implemented; an actual live Meilisearch run is optional and environment-dependent | | Phase 2 | Route and controller parity audit | Doing | Ruby/Bundler unavailable, so Chatwoot route extraction currently uses static `routes.rb` fallback | | Phase 3 | Data and serializer parity | Doing | JSON fixture coverage is partial and still endpoint-family based | -| Phase 4 | Enterprise feature completion | Doing | B7 assignment capacity and SLA policy CRUD payload parity are implemented; applied-SLA lifecycle remains the active gap | +| Phase 4 | Enterprise feature completion | Doing | B7 assignment capacity, SLA policy CRUD payload parity, and applied-SLA conversation persistence are implemented; timer/breach lifecycle remains the active gap | | Phase 5 | Background jobs and integrations | Planned | durable worker choice and job parity are open | | Phase 6 | Core placeholder burn-down | Doing | account/contact/conversation/message/inbox placeholder groups remain broad | | Phase 7 | Verification harness | Planned | search live gate and reused-frontend smoke harness are not complete | @@ -97,12 +97,13 @@ This ledger records the committed parity checkpoints that future slices should b | `f08c743 test(search): add meilisearch live gate` | Completed B6 gate/hardening work: added an env-gated live Meilisearch test that bootstraps isolated indexes, indexes message/contact documents, verifies account-scoped searches and sender filters, rejects `search.engine=db` in release mode, and prevents `cmd/reindex_search` from silently running against the DB fallback. | `go test ./internal/config -count=1`; `go test ./cmd/reindex_search -count=1`; `go test ./internal/search -count=1`; `go test ./...`; `git diff --check`. Live gate is skipped unless `GOCHAT_LIVE_MEILI_HOST` is set. No route changes; route dump remains `TOTAL: 829`. | B6 moves to Review. Optional next verification is running `GOCHAT_LIVE_MEILI_HOST=http://localhost:7700 GOCHAT_LIVE_MEILI_API_KEY=... go test ./internal/search -run TestLiveMeiliSearchEngineIndexesAndSearchesChatwootShapes -count=1`; otherwise continue B7 SLA/assignment capacity. | | `a98dc2c feat(capacity): enforce inbox assignment limits` | Started B7 assignment/capacity enforcement: manual assignment, team assignment with explicit agent, team overflow fallback, and auto-assignment now respect `account_users.agent_capacity_policy_id` plus matching `inbox_capacity_limits.conversation_limit`; only open conversations in the same inbox count toward capacity and resolved conversations do not block new assignments. | `go test ./internal/service -run 'AssignAgent\|AssignTeam' -count=1`; `go test ./internal/autoassignment -run Capacity -count=1` escalated after sandbox socket denial; `go test ./internal/service -count=1`; `go test ./internal/autoassignment -count=1` escalated after sandbox socket denial; `go test ./internal/handler/api/v1 -run 'Conversation\|AgentCapacity' -count=1`; escalated `go test ./...`; `git diff --check`. No route changes; route dump remains `TOTAL: 829`. | Continue B7 with SLA policy CRUD payload review, applied-SLA persistence for new conversations, and timer/breach lifecycle tests. | | `95224fa feat(sla): align sla policy payloads` | Completed B7.2 SLA policy CRUD payload parity for the reused Chatwoot frontend: create/show/update now return `{ payload: sla_policy }`, index returns `{ payload: [...] }`, destroy returns empty `200 OK`, and the SLA policy serializer emits only the fields from Chatwoot's enterprise SLA policy partial. | `go test ./internal/handler/api/v1 -run SlaPolicy -count=1`; `go test ./internal/service -run Sla -count=1`; `go test ./internal/handler/api/v1 -count=1`; `go test ./...`; `git diff --check`. No route changes; route dump remains `TOTAL: 829`. | Continue B7.3 with applied-SLA persistence for new/open conversations, then B7.4 timer/breach lifecycle. | +| `a11bb96 feat(sla): persist applied sla on conversations` | Completed the first B7.3 applied-SLA persistence slice: conversation create/update now accept Chatwoot enterprise `sla_policy_id`, validate account ownership and replacement/removal rules, create one `applied_slas` row when a policy is attached, keep status-only updates idempotent for existing SLA conversations, and serialize `applied_sla` in conversation payloads using Chatwoot's applied-SLA partial fields. | `go test ./internal/service -run 'ConversationService_.*Sla\|ConversationService_Update\|ConversationService_Create' -count=1`; `go test ./internal/handler/api/v1 -run 'ConversationCrudTestSuite/Test(Update_WithSlaPolicy\|Update_Success\|Create_Success)' -count=1`; `go test ./internal/service -run 'Conversation\|AppliedSla\|Sla' -count=1`; `go test ./internal/handler/api/v1 -run 'ConversationCrudTestSuite\|SlaPolicy' -count=1`; `go test ./internal/service -count=1`; `go test ./internal/handler/api/v1 -count=1`; `go test ./...`; `git diff --check`. No route changes; route dump remains `TOTAL: 829`. | Continue B7.4 with idempotent SLA event/timer lifecycle and then B7.5 applied-SLA report/list payload parity. | ## Next Slice Contract -Completed implementation slice: B5.1-B5.5 now cover inbox serializer shape, Chatwoot frontend create/update binding, working-hours persistence, out-of-office behavior, inbox member assignment payload/mutation semantics, channel-specific config depth, AgentCapacityPolicy/InboxCapacityLimit API data contracts, and dedicated Email/Twilio/LINE channel route response shapes. B6 is now in Review after `a16c23c` and `f08c743`: Chatwoot search response envelopes, frontend query params, Meilisearch sender filters, mocked hit serialization, env-gated live Meilisearch validation, release-mode DB fallback rejection, and reindex Meilisearch-only guard are covered. B7 has started with `a98dc2c` and `95224fa`, enforcing per-inbox capacity limits in manual/automatic assignment paths and aligning SLA policy CRUD payloads. B3 and B4 remain in review for deeper side effects and browser validation. +Completed implementation slice: B5.1-B5.5 now cover inbox serializer shape, Chatwoot frontend create/update binding, working-hours persistence, out-of-office behavior, inbox member assignment payload/mutation semantics, channel-specific config depth, AgentCapacityPolicy/InboxCapacityLimit API data contracts, and dedicated Email/Twilio/LINE channel route response shapes. B6 is now in Review after `a16c23c` and `f08c743`: Chatwoot search response envelopes, frontend query params, Meilisearch sender filters, mocked hit serialization, env-gated live Meilisearch validation, release-mode DB fallback rejection, and reindex Meilisearch-only guard are covered. B7 has started with `a98dc2c`, `95224fa`, and `a11bb96`, enforcing per-inbox capacity limits in manual/automatic assignment paths, aligning SLA policy CRUD payloads, and persisting applied SLA records when SLA policies are attached to conversations. B3 and B4 remain in review for deeper side effects and browser validation. -Next implementation slice: continue B7 with applied-SLA persistence for new/open conversations. +Next implementation slice: continue B7 with timer/breach lifecycle and idempotent SLA events. | Step | Required result | Reference source | Verification | | --- | --- | --- | --- | @@ -110,8 +111,9 @@ Next implementation slice: continue B7 with applied-SLA persistence for new/open | N2 | Optionally run the live Meilisearch gate when a local Meilisearch instance is available. | `internal/search/engine_meili_live_test.go`. | `GOCHAT_LIVE_MEILI_HOST=http://localhost:7700 GOCHAT_LIVE_MEILI_API_KEY=... go test ./internal/search -run TestLiveMeiliSearchEngineIndexesAndSearchesChatwootShapes -count=1`. | | N3 | Keep B7 assignment capacity enforcement as current capacity baseline. | `AssignmentHandler`, `AutoAssignmentHandler`, `AgentCapacityPolicy`, `InboxCapacityLimit`. | Done by `a98dc2c`; manual/team/auto assignment now skip or reject agents at per-inbox capacity. | | N4 | Keep SLA policy CRUD payload review as current SLA API baseline. | `reference/chatwoot/enterprise/app/controllers/api/v1/accounts/sla_policies_controller.rb`, SLA policy Jbuilder views, dashboard SLA store/API. | Done by `95224fa`; create/show/update/index/delete payloads match the frontend-consumed Chatwoot shape. | -| N5 | Add applied-SLA persistence for new/open conversations. | `reference/chatwoot/enterprise/app/models/applied_sla.rb`, `Sla::EvaluateAppliedSlaService`, applied-SLA controllers/views. | New conversations receive the right policy and store an applied SLA record; status changes keep applied SLA state consistent. | -| N6 | Update this tracker after every implementation checkpoint. | This document. | `git diff --check`; `go test ./...` for Go changes. | +| N5 | Keep applied-SLA persistence as current conversation SLA baseline. | `reference/chatwoot/enterprise/app/models/enterprise/concerns/conversation.rb`, `AppliedSla#push_event_data`, applied-SLA model partial. | Done by `a11bb96`; `sla_policy_id` attach creates exactly one applied SLA and conversation payloads include Chatwoot-style `applied_sla`. | +| N6 | Add SLA timer and breach lifecycle. | `reference/chatwoot/enterprise/app/services/sla/evaluate_applied_sla_service.rb`, `SlaEvent`, SLA processing jobs. | Active applied SLAs create idempotent FRT/NRT/RT miss events, transition to `active_with_misses`, `hit`, or `missed`, and preserve status on repeated evaluation. | +| N7 | Update this tracker after every implementation checkpoint. | This document. | `git diff --check`; `go test ./...` for Go changes. | Current B2 profile checkpoint: @@ -295,7 +297,7 @@ Active B7 task board: | --- | --- | --- | --- | --- | | B7.1 | Enforce AgentCapacityPolicy/InboxCapacityLimit during manual and automatic assignment. | `AssignmentHandler`, `AutoAssignmentHandler`, `AgentAssignmentService`, `AgentCapacityPolicy`, `InboxCapacityLimit`. | Done | `a98dc2c`; manual assignment, team assignment, overflow fallback, and auto-assignment respect per-inbox open-conversation limits. | | B7.2 | Align SLA policy CRUD payloads and validation. | `sla_policies_controller.rb`, SLA frontend store/API, SLA policy views/entities. | Done | `95224fa`; handler tests prove Chatwoot request wrapper, raw `{ payload }`/list shapes, validation, and empty `200 OK` delete status. | -| B7.3 | Persist applied SLA on new/open conversations. | Chatwoot applied SLA model/services and conversation create/status callbacks. | Todo | Conversation creation and status changes select the right SLA policy and create/update applied SLA state. | +| B7.3 | Persist applied SLA on new/open conversations. | Chatwoot applied SLA model/services and conversation create/status callbacks. | Done | `a11bb96`; `sla_policy_id` create/update creates one applied SLA, validates policy/account/replacement rules, and serializes conversation `applied_sla`. | | B7.4 | Implement SLA timer and breach lifecycle. | First response, next response, resolution targets, business-hours behavior, SLA events/notifications. | Todo | Lifecycle tests cover active, hit, missed, and active-with-misses transitions with idempotent breach events. | | B7.5 | Add SLA report/list payload parity. | `applied_slas` routes, SLA report frontend. | Todo | Account-side applied-SLA list/report filters return frontend-compatible payloads. | @@ -305,8 +307,8 @@ B7 execution breakdown: | --- | --- | --- | --- | --- | | B7.2a | Return SLA policy create/show/update as `{ payload: sla_policy }` and list as `{ payload: [sla_policy] }`; omit local model metadata from the serializer. | `reference/chatwoot/enterprise/app/views/api/v1/accounts/sla_policies/*.json.jbuilder`, `_sla_policy.json.jbuilder`. | Handler tests assert payload envelope, field set, and absence of `success`, `created_at`, and `account_id`. | Done by `95224fa` | | B7.2b | Match Chatwoot destroy behavior with empty `200 OK` instead of a local success envelope. | `sla_policies_controller.rb#destroy`, controller spec. | Delete handler test asserts `200 OK` and empty body. | Done by `95224fa` | -| B7.3a | Determine applied-SLA policy selection for conversations from Chatwoot callbacks/services and map it to Go conversation create/open flows. | `AppliedSLA`, `Sla::EvaluateAppliedSlaService`, conversation lifecycle callbacks. | Service tests prove policy selection by account/inbox relation and no duplicate applied SLA rows. | Todo | -| B7.3b | Persist `applied_slas` and link `conversations.sla_policy_id` when a conversation enters an SLA-covered state. | Applied SLA model and serializer views. | Conversation create/status tests assert `applied_slas`, `conversations.sla_policy_id`, and initial status fields. | Todo | +| B7.3a | Determine applied-SLA policy attachment behavior from Chatwoot callbacks/services and map it to Go conversation create/update/open flows. | `Enterprise::Concerns::Conversation`, `Enterprise::Api::V1::Accounts::ConversationsController#permitted_update_params`, `AppliedSLA`. | Service tests prove explicit `sla_policy_id` attachment, account validation, replacement rejection, and no duplicate applied SLA rows. | Done by `a11bb96` | +| B7.3b | Persist `applied_slas` and link `conversations.sla_policy_id` when a conversation enters an SLA-covered state. | Applied SLA model and serializer views. | Conversation create/update tests assert `applied_slas`, `conversations.sla_policy_id`, initial active status, and frontend `applied_sla` payload fields. | Done by `a11bb96` | | B7.4a | Implement first-response, next-response, and resolution target state transitions with idempotent SLA events. | SLA timer jobs/services and event model. | Lifecycle tests cover active, hit, missed, active-with-misses, and duplicate-event prevention. | Todo | | B7.5a | Align applied-SLA metrics/download/list payloads used by the frontend reports. | `applied_slas_controller.rb`, applied-SLA Jbuilder views, dashboard reports. | Handler tests assert filters, envelopes, CSV/download shape, and frontend field names. | Todo | @@ -394,7 +396,7 @@ Remaining slice landing plan: | Slice | First implementation checkpoint | Follow-up checkpoints | Done classification rule | | --- | --- | --- | --- | | B6 | Compare current Meilisearch document fields and global/entity search payloads against Chatwoot frontend consumers. | Add optional live Meilisearch integration gate, tighten account-scoped filters, and document DB fallback as development-only. | Done only after mocked tests and live-shape review prove search payloads work without DB-only assumptions. | -| B7 | Done: assignment capacity enforcement and SLA policy CRUD payload parity. Next: applied-SLA persistence for new/open conversations. | Business-hours timer math, first/next/resolution breach events, capacity-aware assignment policy binding, report/list payloads. | Done only after lifecycle state tests cover active, hit, missed, and active-with-misses transitions. | +| B7 | Done: assignment capacity enforcement, SLA policy CRUD payload parity, and applied-SLA conversation persistence. Next: SLA timer/breach lifecycle. | Business-hours timer math, first/next/resolution breach events, capacity-aware assignment policy binding, report/list payloads. | Done only after lifecycle state tests cover active, hit, missed, and active-with-misses transitions. | | B8 | Account-side CSAT list/metrics/review-note payload parity. | Resolve-triggered survey send idempotency, report/download filters, public/update lock regression, channel-specific send hooks. | Done only after account APIs, public APIs, and resolve listener agree on one message-linked response model. | | B9 | Macro execution side effects for labels, status, assignee/team, priority, private notes, and attachments. | Automation rule condition/action parity, execution logs, webhook delivery retry, email transcript delivery, stop-on-match semantics. | Done only after actions mutate real conversations and async external actions are retryable or explicitly feature-gated. | | B10 | Audit list payload and audit writer boundary for representative mutating core resources. | CustomRole permission-key parity, AccountUser permission resolution, InboxLimit enforcement in inbox/channel creation paths. | Done only after authorization tests prove admin-only surfaces and non-admin denial shapes match Chatwoot. | @@ -670,7 +672,7 @@ Enterprise tracking table: | ID | Feature | Existing Go surface | Required next work | Status | | --- | --- | --- | --- | --- | -| P4.1 | SLA policies/events | `internal/model/sla_policy.go`, `internal/model/sla_event.go`, `internal/service/sla_policy_service.go`, `internal/service/sla_event_service.go`, `internal/handler/api/v1/sla_policy_handler.go` | SLA policy CRUD payload tests are done; next wire applied-SLA lifecycle events, breach tracking, business-hours timers, and report payloads. | Doing | +| P4.1 | SLA policies/events | `internal/model/sla_policy.go`, `internal/model/sla_event.go`, `internal/service/sla_policy_service.go`, `internal/service/applied_sla_service.go`, `internal/service/sla_event_service.go`, `internal/handler/api/v1/sla_policy_handler.go` | SLA policy CRUD and applied-SLA conversation attach are done; next wire idempotent lifecycle events, breach tracking, business-hours timers, and report payloads. | Doing | | P4.2 | Audit logs | `internal/model/audit.go`, `internal/service/audit_service.go`, `internal/repository/audit_repo.go`, `internal/handler/api/v1/audit_handler.go` | Ensure every mutating enterprise/core action emits audit events and filters match Chatwoot. | Todo | | P4.3 | Custom roles/permissions | `internal/model/custom_role.go`, `internal/service/custom_role_service.go`, `internal/middleware/role_check.go`, `internal/handler/api/v1/custom_role_handler.go` | Align permission keys, inherited roles, authorization failures, and admin UX payloads. | Todo | | P4.4 | Agent capacity | `internal/model/agent_capacity_policy.go`, `internal/service/agent_capacity_policy_service.go`, `internal/handler/api/v1/agent_capacity_handler.go`, `internal/autoassignment/*` | First align AgentCapacityPolicy API, serializers, nested users, and `InboxCapacityLimit` data contracts; then enforce capacity in manual/auto assignment. | Review | @@ -684,7 +686,7 @@ Enterprise work package breakdown: | Package | Subtasks | Must verify | Status | | --- | --- | --- | --- | -| SLA | Policy CRUD parity, conversation SLA assignment, first-response/next-response/resolution timers, business-hours handling, breach events, notifications. | Field/unit parity, timer state transitions, breach idempotency, report payloads. | Doing | +| SLA | Policy CRUD parity, conversation SLA assignment, first-response/next-response/resolution timers, business-hours handling, breach events, notifications. | Policy payloads and applied-SLA attach are covered; timer state transitions, breach idempotency, and report payloads remain. | Doing | | Assignment and capacity | Assignment policy CRUD, inbox policy binding, round-robin/availability/capacity selection, manual assignment limits, fallback behavior. | Manual and automatic assignment respect policy, availability, team/inbox membership, and limits. | Review | | CSAT account side | Survey send on resolve, response list, metrics, filters, downloads, review notes, resend/idempotency, public lock already implemented. | Account API payload fixtures, metrics math, 14-day lock, one response per CSAT message. | Todo | | Automation rules | Condition/action parity, event listener coverage, delayed actions, execution logs, stop-on-match behavior, webhook and transcript actions. | Rule trigger tests for conversation/contact/message events and durable retry for external actions. | Todo | @@ -698,7 +700,7 @@ Enterprise acceptance gates: | Feature | Required gates before `Done` | Reference notes | | --- | --- | --- | -| SLA | Field names and units match Chatwoot; `only_during_business_hours` exists; policy CRUD payloads match the enterprise Jbuilder views; applied SLA state machine is wired; breach events create notifications; delete/update statuses match Rails behavior. | `docs/verification/SLA_ASSIGNMENT_POLICY_V2_COMPATIBILITY_REPORT.md`, `docs/requirements/M11-enterprise-features.md` | +| SLA | Field names and units match Chatwoot; `only_during_business_hours` exists; policy CRUD payloads match the enterprise Jbuilder views; applied SLA is created when `sla_policy_id` is attached; remaining state machine/breach events create notifications; delete/update statuses match Rails behavior. | `docs/verification/SLA_ASSIGNMENT_POLICY_V2_COMPATIBILITY_REPORT.md`, `docs/requirements/M11-enterprise-features.md` | | Audit | Mutating account resources emit audit records with actor, IP, request UUID, auditable type/id, associated account, and changes; admin list pagination matches Chatwoot. | `docs/requirements/M11-enterprise-features.md` | | CustomRole | Permission keys match Chatwoot; `AccountUser` permission resolution honors custom roles; deleting a role nullifies users; admin-only policy is enforced. | `docs/requirements/M1-accounts-and-users.md`, `docs/requirements/M11-enterprise-features.md` | | AgentCapacity and InboxLimit | Assignment and auto-assignment respect per-inbox conversation limits; account/inbox limits are enforced in create paths and surfaced to frontend. | `docs/requirements/M1-accounts-and-users.md`, `docs/requirements/M5-team-and-assignment.md` | @@ -904,3 +906,4 @@ Verification milestone gates: - 2026-06-05: B6 live-gate/fallback checkpoint committed as `f08c743 test(search): add meilisearch live gate`; added an env-gated live Meilisearch test for isolated bootstrap/index/search of contacts and messages, kept it skipped unless `GOCHAT_LIVE_MEILI_HOST` is set, rejected `search.engine=db` in release validation, and made `cmd/reindex_search` fail fast if configured with DB fallback. Focused config/reindex/search tests, full `go test ./...`, and `git diff --check` passed. B6 moves to Review; next implementation slice is B7 SLA/assignment capacity unless the optional live gate is explicitly run first. - 2026-06-05: B7 capacity enforcement checkpoint committed as `a98dc2c feat(capacity): enforce inbox assignment limits`; manual agent assignment, team assignment with explicit agent, team overflow fallback, and auto-assignment now apply AgentCapacityPolicy/InboxCapacityLimit per-inbox open-conversation limits. Resolved conversations do not count against capacity. Focused assignment/capacity tests, handler smoke for conversation/capacity routes, escalated full `go test ./...`, and `git diff --check` passed. Next B7 checkpoint is SLA policy CRUD payload review and applied-SLA persistence. - 2026-06-05: B7 SLA policy payload checkpoint committed as `95224fa feat(sla): align sla policy payloads`; SLA policy create/show/update now return `{ payload: sla_policy }`, index returns `{ payload: [...] }`, destroy returns empty `200 OK`, and the serializer is limited to Chatwoot's enterprise SLA policy fields. Focused SLA policy handler tests, SLA service tests, handler package tests, full `go test ./...`, and `git diff --check` passed. Next B7 checkpoint is applied-SLA persistence for new/open conversations. +- 2026-06-05: B7 applied-SLA persistence checkpoint committed as `a11bb96 feat(sla): persist applied sla on conversations`; conversation create/update now accepts `sla_policy_id`, validates SLA policy account ownership plus Chatwoot replacement/removal rules, creates exactly one active `applied_slas` record, keeps status-only updates idempotent for existing SLA conversations, and includes Chatwoot-style `applied_sla` payload fields in conversation responses. Focused conversation/SLA service and handler tests, full `go test ./...`, and `git diff --check` passed. Next B7 checkpoint is SLA timer/breach lifecycle with idempotent FRT/NRT/RT events.