diff --git a/internal/http/handlers/platform_webhook_handler.go b/internal/http/handlers/platform_webhook_handler.go index 356efdc..0544050 100644 --- a/internal/http/handlers/platform_webhook_handler.go +++ b/internal/http/handlers/platform_webhook_handler.go @@ -88,6 +88,13 @@ func (h *PlatformWebhookHandler) Handle(w http.ResponseWriter, r *http.Request) writeJSON(w, http.StatusInternalServerError, map[string]any{"error": map[string]any{"code": cserrors.CS_SYS_5001, "message": cserrors.ErrorMsg(cserrors.CS_SYS_5001)}}) return } + // NOTE: Final-consistency boundary. + // dialog.Process and outbox.InsertPendingBatch are NOT enclosed in a single + // transaction. If outbox insertion fails after dialog succeeds, the business + // state (Session/Ticket/Audit) is already persisted but no downstream event + // is emitted. In that case we return HTTP 500 so the platform caller can + // retry; DedupRepository.TryRecord inside dialog.Process guards against + // duplicate business-state mutations on retry. if h.eventWriter == nil { writeJSON(w, http.StatusInternalServerError, map[string]any{"error": map[string]any{"code": cserrors.CS_SYS_5001, "message": cserrors.ErrorMsg(cserrors.CS_SYS_5001)}}) return diff --git a/internal/service/dialog/service.go b/internal/service/dialog/service.go index 7b24dd2..cb6f195 100644 --- a/internal/service/dialog/service.go +++ b/internal/service/dialog/service.go @@ -1,3 +1,14 @@ +// Package dialog implements the core message-processing service. +// +// NOTE on transaction boundaries: the current repository layer does not +// provide a unified transactional outer box (UnitOfWork / sql.Tx). Each +// repository call (SessionRepository, DedupRepository, TicketRepository, +// AuditRepository) executes on its own connection. Therefore the operations +// inside Process are eventually-consistent rather than strictly atomic. +// +// A future refactor introducing UnitOfWork or TxProvider could lift this +// limitation and wrap the entire flow plus outbox insertion in a single +// transaction. package dialog import (