|
|
|
@@ -49,11 +49,11 @@ Hermes task landing checklist:
|
|
|
|
|
|
|
|
|
|
## Current Baseline
|
|
|
|
|
|
|
|
|
|
- Current tracking checkpoint: 2026-06-07 P3.98 conversation transcript delivery parity, prepared as `feat(conversations): align transcript delivery`.
|
|
|
|
|
- Latest implementation checkpoint: this checkpoint, prepared as `feat(conversations): align transcript delivery`.
|
|
|
|
|
- Latest documentation/tooling checkpoint: this tracker update records Chatwoot conversation transcript plan gate, rate limit, mailer, and message-scope parity. No route artifacts change.
|
|
|
|
|
- Current tracking checkpoint: 2026-06-07 P3.99 conversation destroy async parity, prepared as `feat(conversations): align destroy job`.
|
|
|
|
|
- Latest implementation checkpoint: this checkpoint, prepared as `feat(conversations): align destroy job`.
|
|
|
|
|
- Latest documentation/tooling checkpoint: this tracker update records Chatwoot conversation destroy `head :ok` and `DeleteObjectJob` parity. No route artifacts change.
|
|
|
|
|
- Plan landing status: complete for the current known Hermes plans and user-confirmed scope. Future work should update this file directly instead of opening a parallel tracker.
|
|
|
|
|
- Worktree status at this implementation checkpoint: account conversation transcript delivery now follows Chatwoot's `ConversationsController#transcript` gates: missing email remains `422`, disabled transcript plans return raw `402`, configured daily email limits return empty `429`, delivered transcripts use the SMTP transcript boundary, increment the account outbound-email counter, and include only public incoming/outgoing chat messages. Automation `send_email_transcript` shares the same enabled/rate-limit behavior and stops at the configured limit. This retains P3.97 conversation typing event parity, P3.96 conversation last-seen read parity, P3.95 conversation mute contact-block parity, P3.94 conversation toggle-priority empty-body parity, P3.93 conversation toggle-status fallback parity, P3.92 automation-rule attachment parity, P3.91 upload/macro attachment parity, P3.90 SearchAPI filter-surface parity, and prior checkpoints. Live API/browser/enterprise smoke still needs the full PostgreSQL/Redis/Meilisearch/GoChat/Vite/Chrome stack.
|
|
|
|
|
- Worktree status at this implementation checkpoint: account conversation destroy now returns empty `200 OK` like Chatwoot `head :ok` and, when a WorkerPool is configured, enqueues a low-priority durable `conversation:delete_object` job matching the inspected `DeleteObjectJob` path before the worker performs the actual soft delete and event/search cleanup. No-worker tests keep a synchronous fallback. This retains P3.98 conversation transcript delivery parity, P3.97 conversation typing event parity, P3.96 conversation last-seen read parity, P3.95 conversation mute contact-block parity, P3.94 conversation toggle-priority empty-body parity, P3.93 conversation toggle-status fallback parity, and prior checkpoints. Live API/browser/enterprise smoke still needs the full PostgreSQL/Redis/Meilisearch/GoChat/Vite/Chrome stack.
|
|
|
|
|
- Next executable implementation checkpoint: continue Phase 2/3 drift audit for the next reused-frontend mismatch, or run B12 live smoke when the full PostgreSQL/Redis/Meilisearch/GoChat/Vite/Chrome stack is available. Re-run Phase 6 placeholder audit after future route/smoke changes.
|
|
|
|
|
- `go test ./...` passes when run outside the restricted socket sandbox for the latest implementation baseline; the latest docs/tooling checkpoint verified `scripts/parity_frontend_smoke.sh --check` with workspace-local temp/cache dirs after `/tmp` was full.
|
|
|
|
|
- Route dump succeeds with `967` registered routes after profile MFA route tracking.
|
|
|
|
@@ -156,6 +156,7 @@ This table is the shortest authoritative handoff view. If an older lower section
|
|
|
|
|
|
|
|
|
|
| Priority | Workstream | Current state | Next checkpoint | Commit close rule |
|
|
|
|
|
| --- | --- | --- | --- | --- |
|
|
|
|
|
| 0 | P3.99 conversation destroy async parity | Implemented for reused conversation delete actions: `DELETE /conversations/:conversation_id` now returns empty `200 OK` like Chatwoot and queues a low-priority durable `conversation:delete_object` job when workers are configured. The job performs the existing soft delete, dispatches `conversation.deleted`, and removes the search index entry; no-worker construction keeps synchronous focused-test behavior. | Keep in Review; reopen from B12 conversation delete smoke or fresh reference evidence for enterprise `DeleteObjectJob#process_post_deletion_tasks`, exact Pundit destroy permission drift, or heavy-association purge behavior beyond conversation objects. | Focused ConversationService delete/job tests and ConversationHandler delete response tests passed; full `go test ./...` passed outside the restricted socket sandbox; `git diff --check` passed. No route artifacts change. |
|
|
|
|
|
| 0 | P3.98 conversation transcript delivery parity | Implemented for reused conversation transcript and automation transcript actions: `POST /conversations/:conversation_id/transcript` now checks Chatwoot-style email transcript availability, enforces configured daily email limits, delivers through the SMTP transcript boundary, increments account outbound-email counters, and scopes transcript content to public incoming/outgoing chat messages. Automation `send_email_transcript` no-ops when disabled and stops recipient fan-out at the rate limit before sync delivery or durable enqueue. | Keep in Review; reopen from B12 transcript smoke or fresh reference evidence for exact Redis TTL behavior, cloud-only global default limit handling, mailer template HTML body drift, or provider-specific SMTP availability checks beyond the inspected mailer gate. | Focused ConversationService, ConversationHandler, ActionService, and Account model transcript tests passed; full `go test ./...` passed outside the restricted socket sandbox; `git diff --check` passed. `accounts.limits` migration is included; no route artifacts change. |
|
|
|
|
|
| 0 | P3.97 conversation typing event parity | Implemented for reused conversation typing action: `POST /conversations/:conversation_id/toggle_typing_status` now accepts empty bodies and `{}` as Chatwoot no-op `200 OK`, rejects malformed JSON, and emits `conversation.typing_on` / `conversation.typing_off` events with conversation/contact/user context and `is_private` metadata for valid statuses. | Keep in Review; reopen from B12 conversation typing smoke or fresh reference evidence for ActionCable event payload fields beyond the inspected `TypingStatusManager` contract. | Focused ConversationService and ConversationHandler toggle-typing tests passed; full `go test ./...` must pass outside the restricted socket sandbox; `git diff --check` must pass. No route artifacts change. |
|
|
|
|
|
| 0 | P3.96 conversation last-seen read parity | Implemented for reused conversation read/unread actions: `POST /conversations/:conversation_id/update_last_seen` marks current-user unread notifications for the conversation as read, updates `agent_last_seen_at` plus `assignee_last_seen_at` when the viewer is the assignee and there are unread messages, updates only agent last-seen for non-assignees, and applies Chatwoot's one-hour no-unread write throttle; `unread` backdates both last-seen columns to the last incoming message minus one second. | Keep in Review; reopen from B12 conversation read/unread smoke or fresh reference evidence for unread-count notifier/event side effects not covered by the inspected controller/model contract. | Focused ConversationService and ConversationHandler last-seen/unread tests passed; full `go test ./...` must pass outside the restricted socket sandbox; `git diff --check` must pass. No route artifacts change. |
|
|
|
|
@@ -262,6 +263,7 @@ These rows are the executable development plan from this point forward. A checkp
|
|
|
|
|
|
|
|
|
|
| ID | Owner files | Reference files | Work to land | Exit gate |
|
|
|
|
|
| --- | --- | --- | --- | --- |
|
|
|
|
|
| P3.99 conversation destroy async parity | `internal/handler/api/v1/conversation_handler.go`, `internal/service/conversation_service.go`, `internal/service/conversation_delete_worker.go`, `internal/app/bootstrap.go`, conversation handler/service tests | `reference/chatwoot/app/controllers/api/v1/accounts/conversations_controller.rb#destroy`, `reference/chatwoot/app/jobs/delete_object_job.rb`, reused dashboard `ConversationApi.delete` and `conversations/actions.js#deleteConversation` | Account conversation destroy now matches the inspected Chatwoot controller/job path: the handler returns empty `200 OK`, production service wiring enqueues a low-priority durable delete-object job, and the worker performs the existing soft-delete side effects plus event dispatch/search-index deletion. The service keeps synchronous behavior when no WorkerPool is set so focused tests and non-worker local callers still delete immediately. | Review by `feat(conversations): align destroy job`; focused tests prove handler `200` response shape, not-found behavior, synchronous fallback deletion, durable job enqueue, delayed visibility before worker execution, missing conversations do not enqueue phantom jobs, and worker replay deletion; full `go test ./...` passed outside the restricted socket sandbox; `git diff --check` passed. No route artifacts change. |
|
|
|
|
|
| P3.98 conversation transcript delivery parity | `internal/handler/api/v1/conversation_handler.go`, `internal/service/conversation_service.go`, `internal/model/account.go`, `internal/model/account_email.go`, `internal/automation/action_service.go`, migration `000037_add_accounts_limits`, conversation handler/service/action/model tests | `reference/chatwoot/app/controllers/api/v1/accounts/conversations_controller.rb#transcript`, `reference/chatwoot/app/models/concerns/account_email_rate_limitable.rb`, `reference/chatwoot/app/mailers/conversation_reply_mailer.rb#conversation_transcript`, `reference/chatwoot/app/models/message.rb#chat`, `reference/chatwoot/app/models/concerns/message_filter_helpers.rb#conversation_transcriptable?`, `reference/chatwoot/app/services/action_service.rb#send_email_transcript`, reused dashboard transcript modal/API/i18n payment-required behavior | Account conversation transcript delivery now matches the inspected Chatwoot controller and mailer contract: missing email returns raw `422`, disabled accounts return raw `402`, configured email limits return empty `429`, success returns empty `200 OK` after delivering through a fakeable SMTP transcript boundary and incrementing account outbound-email count. Transcript bodies include only public incoming/outgoing chat messages, and automation transcript actions respect the same enabled/rate behavior while splitting comma recipients and stopping at the first rate-limit miss. | Review by `feat(conversations): align transcript delivery`; focused tests prove account gate/rate helpers, handler `402/429` shape, service delivery/count/message-scope behavior, and automation disabled/rate/filter behavior; full `go test ./...` passed outside the restricted socket sandbox; `git diff --check` passed. No route artifacts change. |
|
|
|
|
|
| P3.97 conversation typing event parity | `internal/handler/api/v1/conversation_handler.go`, `internal/service/conversation_service.go`, `internal/channel/event.go`, conversation handler/service tests | `reference/chatwoot/app/controllers/api/v1/accounts/conversations_controller.rb#toggle_typing_status`, `reference/chatwoot/app/services/conversations/typing_status_manager.rb`, `reference/chatwoot/app/javascript/dashboard/api/inbox/conversation.js`, `reference/chatwoot/app/javascript/dashboard/store/modules/conversationTypingStatus.js`, reused dashboard reply-box typing callers | Account conversation `toggle_typing_status` now matches Chatwoot's permissive member action: missing body or missing `typing_status` resolves the conversation and returns empty `200 OK` without dispatching an event, malformed JSON still fails, and `on`/`off` statuses dispatch `conversation.typing_on` / `conversation.typing_off` events with conversation/contact/user context plus `is_private`. | Review by `feat(conversations): align typing events`; focused tests prove service event type/data behavior, blank-status no-op after conversation lookup, handler empty-body and `{}` no-op success, and invalid JSON failure; full `go test ./...` must pass outside the restricted socket sandbox; `git diff --check` must pass. No route artifacts change. |
|
|
|
|
|
| P3.96 conversation last-seen read parity | `internal/service/conversation_service.go`, `internal/handler/api/v1/conversation_handler.go`, conversation handler/service tests | `reference/chatwoot/app/controllers/api/v1/accounts/conversations_controller.rb#update_last_seen`, `#unread`, `reference/chatwoot/app/models/conversation.rb#unread_messages`, `#assignee_unread_messages`, `reference/chatwoot/app/services/notification/mark_conversation_read_service.rb`, reused dashboard `ConversationApi.markMessageRead/markMessagesUnread` and store `messageReadActions.js` | Account conversation `update_last_seen` now mirrors Chatwoot's read path: it marks unread notifications for the conversation/current user as read, updates assignee last-seen only when the current viewer is the assignee, always updates agent last-seen when unread messages exist, and throttles no-unread writes to one hour. The `unread` endpoint now uses the Chatwoot helper shape by updating both `agent_last_seen_at` and `assignee_last_seen_at` to the last incoming message timestamp minus one second, or clearing both when no incoming message exists. | Review by `feat(conversations): align last seen reads`; focused tests prove notification read side effects, assignee/non-assignee column behavior, throttle behavior, and unread dual-column backdating; full `go test ./...` must pass outside the restricted socket sandbox; `git diff --check` must pass. No route artifacts change. |
|
|
|
|
@@ -384,6 +386,7 @@ This ledger records the committed parity checkpoints that future slices should b
|
|
|
|
|
|
|
|
|
|
| Commit | Scope | Verification summary | Follow-up state |
|
|
|
|
|
| --- | --- | --- | --- |
|
|
|
|
|
| `feat(conversations): align destroy job` | Advances P3.99 with Chatwoot conversation destroy parity. GoChat now returns empty `200 OK` for `DELETE /conversations/:conversation_id`, wires a durable low-priority `conversation:delete_object` job into the WorkerPool, and lets that job perform the existing soft-delete/event/search cleanup while retaining a synchronous fallback without workers. | Focused ConversationService delete/job tests and ConversationHandler delete response tests passed; full `go test ./...` passed outside the restricted socket sandbox; `git diff --check` passed. No route artifacts change. | Move P3.99 to Review; continue Phase 2/3 drift audit, Phase 6 placeholder audit, or B12 live smoke. |
|
|
|
|
|
| `feat(conversations): align transcript delivery` | Advances P3.98 with Chatwoot conversation transcript delivery parity. GoChat now adds the Chatwoot `accounts.limits` field, uses account email transcript availability/rate helpers, maps dashboard transcript disabled/rate-limited responses to raw `402`/empty `429`, delivers transcript emails through the fakeable SMTP boundary, increments outbound-email counters, and filters transcript content to public incoming/outgoing chat messages. Automation `send_email_transcript` now no-ops when disabled and stops recipient fan-out at the configured rate limit. | Focused Account model, ConversationService, ConversationHandler, and ActionService transcript tests passed; full `go test ./...` passed outside the restricted socket sandbox; `git diff --check` passed. No route artifacts change. | Move P3.98 to Review; continue Phase 2/3 drift audit, Phase 6 placeholder audit, or B12 live smoke. |
|
|
|
|
|
| `feat(conversations): align typing events` | Advances P3.97 with Chatwoot conversation `toggle_typing_status` parity. GoChat now treats empty or missing `typing_status` as a no-op `200 OK`, keeps malformed JSON as a validation error, and dispatches Chatwoot-style `conversation.typing_on` / `conversation.typing_off` events with user/contact/conversation context and `is_private` metadata. | Focused ConversationService and ConversationHandler toggle-typing tests passed; full `go test ./...` passed outside the restricted socket sandbox; `git diff --check` passed. No route artifacts change. | Move P3.97 to Review; continue Phase 2/3 drift audit, Phase 6 placeholder audit, or B12 live smoke. |
|
|
|
|
|
| `feat(conversations): align last seen reads` | Advances P3.96 with Chatwoot conversation `update_last_seen` and `unread` parity. GoChat now marks current-user conversation notifications read, applies assignee-aware last-seen updates and the no-unread one-hour throttle, and marks conversations unread by backdating both agent and assignee last-seen columns. | Focused ConversationService and ConversationHandler last-seen/unread tests passed; full `go test ./...` passed outside the restricted socket sandbox; `git diff --check` passed. No route artifacts change. | Move P3.96 to Review; continue Phase 2/3 drift audit, Phase 6 placeholder audit, or B12 live smoke. |
|
|
|
|
@@ -2774,3 +2777,4 @@ Verification milestone gates:
|
|
|
|
|
- 2026-06-07: P3.96 conversation last-seen checkpoint prepared as `feat(conversations): align last seen reads`; audited Chatwoot `ConversationsController#update_last_seen/#unread`, `Conversation#unread_messages/#assignee_unread_messages`, `Notification::MarkConversationReadService`, and reused dashboard message read/unread API/store actions. GoChat account conversation `update_last_seen` now marks current-user unread notifications for the conversation as read, updates agent and assignee last-seen columns according to assignee/unread-message state, and throttles no-unread writes to once per hour; `unread` now backdates or clears both agent and assignee last-seen columns. Focused ConversationService and ConversationHandler tests passed; full `go test ./...` passed outside the restricted socket sandbox; `git diff --check` passed. No route artifacts change.
|
|
|
|
|
- 2026-06-07: P3.97 conversation typing checkpoint prepared as `feat(conversations): align typing events`; audited Chatwoot `ConversationsController#toggle_typing_status`, `Conversations::TypingStatusManager`, and reused dashboard conversation typing API/store/reply-box callers. GoChat account conversation `toggle_typing_status` now accepts empty bodies and missing `typing_status` as no-op `200 OK`, still rejects malformed JSON, and dispatches Chatwoot-style `conversation.typing_on` / `conversation.typing_off` events with current user, contact, conversation, and `is_private` metadata for valid `on`/`off` statuses. Focused ConversationService and ConversationHandler toggle-typing tests passed; full `go test ./...` passed outside the restricted socket sandbox; `git diff --check` passed. No route artifacts change.
|
|
|
|
|
- 2026-06-07: P3.98 conversation transcript checkpoint prepared as `feat(conversations): align transcript delivery`; audited Chatwoot `ConversationsController#transcript`, `AccountEmailRateLimitable`, `ConversationReplyMailer#conversation_transcript`, `Message.chat`, `MessageFilterHelpers#conversation_transcriptable?`, and automation `ActionService#send_email_transcript`. GoChat account conversation transcript now honors email transcript availability, configured email limits, raw `402`/empty `429` response shapes, SMTP transcript delivery, outbound-email count increments, and public incoming/outgoing chat-message scoping; automation `send_email_transcript` no-ops when disabled and stops recipient fan-out at the rate limit before sync delivery or durable enqueue. Focused Account model, ConversationService, ConversationHandler, and ActionService transcript tests passed; full `go test ./...` passed outside the restricted socket sandbox; `git diff --check` passed. No route artifacts change.
|
|
|
|
|
- 2026-06-07: P3.99 conversation destroy checkpoint prepared as `feat(conversations): align destroy job`; audited Chatwoot `ConversationsController#destroy`, `DeleteObjectJob`, and reused dashboard `ConversationApi.delete` / `deleteConversation` action. GoChat account conversation delete now returns empty `200 OK` instead of local `204`, queues a low-priority durable `conversation:delete_object` job when WorkerPool is configured, and the worker performs the existing soft delete, `conversation.deleted` dispatch, and search-index deletion; no-worker construction keeps synchronous deletion for focused tests/local callers. Focused ConversationService delete/job tests and ConversationHandler delete tests passed; full `go test ./...` passed outside the restricted socket sandbox; `git diff --check` passed. No route artifacts change.
|
|
|
|
|