From 7351b6680667d8e6cc030810cdc6a64fa1481dde Mon Sep 17 00:00:00 2001 From: renanfulas Date: Mon, 29 Jun 2026 15:21:33 -0300 Subject: [PATCH] Document reference flows (sync vs async) in the tiering plan Adds the concrete operational design agreed in the 2026-06-29 architecture discussion: the per-turn flow (one synchronous Postgres transaction that persists the turn and enqueues the outbox; fail-open hot state; async off-box backup via the worker) and the handoff flow (the support_case + handoff.requested in the same sync transaction is the consistency gate; async external delivery). Clarifies that Redis only ever enters as a non-authoritative hot-state backend / read cache (levels 1-2), never as the durability anchor. Makes the plan self-contained. Co-Authored-By: Claude Opus 4.8 --- .../conversation-persistence-tiering-plan.md | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/docs/quality-plans/conversation-persistence-tiering-plan.md b/docs/quality-plans/conversation-persistence-tiering-plan.md index feccf77..9945a71 100644 --- a/docs/quality-plans/conversation-persistence-tiering-plan.md +++ b/docs/quality-plans/conversation-persistence-tiering-plan.md @@ -138,6 +138,40 @@ Escada de escalada (só sobe quando o gatilho aparecer, sem virar a B inteira): Assim a A **cresce para dentro da B** de forma incremental e reversível, em vez de pagar a complexidade adiantado por uma carga que ainda não existe. +### Fluxos de referência (síncrono vs assíncrono) + +O princípio operacional do Nível 0, concreto. Existe um diagrama de referência +deste desenho (gerado na discussão de 2026-06-29); o texto abaixo é a fonte da +verdade. + +**Fluxo de um turno (qualquer domínio):** + +1. Inbound (web/WhatsApp/…) chega ao `ChatFlowService` — roteia, recupera (pgvector), + responde (LLM). +2. **Síncrono (1 transação Postgres):** grava o turno (`conversations`/`messages` + + `chat_audits`) **e**, na mesma transação, **enfileira o(s) evento(s) no + `operational_outbox`**. Commit atômico = o turno está durável e a cópia está + garantida. Custo ~ms (não percebido; o LLM domina). +3. **Estado quente (síncrono, fail-open):** lê/grava `SessionStateStore` (in-memory + no Nível 0) por `(domain, channel, session_hash)`. Se o store cair, o `/chat` + responde mesmo assim — não é autoritativo. +4. **Assíncrono (worker `dispatch_outbox`, fora do hot path):** entrega o evento + `conversation.turn.archived` ao **backup off-box (R2/S3, append-only)** — at-least-once, + idempotente. RPO ≈ lag do worker (segundos). + +**Fluxo de handoff (escalou para humano):** + +1. Mesma transação síncrona do turno, **mais** o `support_cases` (ticket durável) + + o evento `handoff.requested` no outbox. **Este é o gate de consistência:** se a + transação falhar, **não** se promete humano ao usuário (não "barra" em falso). +2. Assíncrono via outbox: entrega de `handoff.requested` ao consumidor externo + (quando configurado) + a cópia off-box. O **support inbox** (leitura) já serve o + ticket a partir do Postgres, independente da entrega externa. + +**Onde o Redis entra (Níveis 1–2, sob gatilho):** como backend do `SessionStateStore` +(estado quente, TTL, não-autoritativo) e/ou cache de leitura 7d por cima do Postgres +— **nunca** como âncora de durabilidade no Nível 0. + --- ## 5. Tradeoffs honestos / limites de MVP