O renderer de links do chat decide interno vs externo (B187/B188); a decisão pura de fechamento mora no owner src/lib/ai/markdownLinks.ts (shouldCloseDrawerOnLinkClick: interno && mobile && clique simples) e o markdownComponents estatico virou useMemo no corpo do CampaignAIChat para alcancar o contexto. Desktop inalterado; links externos/modos de nova aba mantem o drawer da aba de origem; sessao e restore intactos (close persiste open:false). - unit: 12 testes da decisao pura - e2e (modo CI, E2E_PROD=1): interno fecha + navega sem load + reload nao reabre; externo abre popup e mantem o drawer - debito B199/#672 (depends #660): race de persistencia ao fechar mid-stream
4.2 KiB
Sollinha: fechar o drawer durante streaming pode reabri-lo num reload (race de persistência)
Status: registrado Atualizado em: 2026-08-11 Issue: # Priority: P3 Model: composer-2.5 Impeccable: B — comportamento do drawer existente, sem nova superfície Appetite: ~0,5 dia eng; um ajuste no persist effect do provider
Intenção
O persist effect do chat (CampaignAISidebarContext.tsx) só grava a sessão quando status === 'ready'. Se o usuário fecha o drawer enquanto a resposta ainda está chegando (X manual, swipe — ou, desde B198, um toque num link da resposta que fecha o drawer no mesmo tab) e um reload acontece antes do settle, o sessionStorage ainda carrega o estado velho open: true — o restore reabre o drawer "sozinho" na próxima página (mesma classe OPS22 que o repo já caça).
Pré-existente (o X tinha a mesma janela), mas o link-navegação do B198 torna o fechamento-durante-streaming um fluxo normal de campo: o usuário toca no link no meio da resposta, o drawer fecha, ele navega; um reload (ou o próprio fluxo de volta) não deve ressuscitar o drawer.
Persona e fluxo
- Persona / contexto: assessor/liderança no celular; toca num link interno enquanto o Sollinha ainda está respondendo.
- Job principal: fechar o drawer em qualquer momento (mesmo mid-stream) e ter um reload sem drawer fantasma.
- Fluxo desejado: resposta em streaming → toque no link → drawer fecha → reload → drawer continua fechado, conversa restaurada.
- Anti-goal: não reabrir a raça de restore (OPS22/B188): fechar mid-stream não pode ser tratado como "estado velho válido".
Objetivo e aceite
- Fechar o drawer com
status !== 'ready'persisteopen: falsemesmo antes do settle — um reload imediato não reabre o drawer. - A conversa em andamento continua persistindo normalmente (nada de descartar mensagens do stream em andamento).
- O mecanismo
openBy/userToggledOpenRefcontinua governando o restore como hoje (não inverter intenção).
Dados (intenção)
Dados: N/A — persistência client-side (sessionStorage), sem servidor.
Direção no codebase (hipótese)
- Área provável:
src/components/campaign/shell/ai/CampaignAISidebarContext.tsx— o persist effect (linhas ~116-121) hoje early-returna comstatus !== 'ready'. Hipótese: persistiropenem mudanças imediatas (efeito separado só paraopen, sem gate de status, reusandowriteSollinhaChatSessioncom as mensagens atuais do último commit), deixando o gate destatusapenas para as mensagens do stream. Validar interação comsessionRestorede com o write do settle (que já escreve tudo junto). - Precedente a olhar:
src/lib/sollinhaChatSession.ts(write/read fail-closed),docs/plans/sollinha-contexto-sessao-janela.md(B188) esollinha-drawer-fecha-link-mobile-impl.md(B198). - Risco: escrever a sessão com um
messagesmid-stream congelaria uma resposta pela metade no storage — se o write deopene o write demessagesforem o mesmowriteSollinhaChatSession, o gate de mensagens precisa continuar (ou o write de close pode usar o último snapshotreadyde mensagens).
Dependências
- #660 (B198) — o fluxo de fechamento por link é o amplificador; destrava sozinha quando o pai flipar
done.
Fora de escopo
- Mudar o mecanismo de restore/
openBy(OPS22/B188) — só o write de fechamento. - Persistir mensagens mid-stream (mantém a regra atual de só gravar stream completo).
Rabbit holes de produto
- Escrever o stream pela metade. Se o ajuste gravar
messagesmid-stream, um reload perde a cauda do turno. Corte: fechamento persisteopen: falsecom o snapshot de mensagens do último estadoready(ou o array atual se vazio/estável). - Reabrir a raça de restore. Mexer em
openBypara "consertar" isto troca um bug raro por outro. Corte: só oopenmuda;openBycontinua como hoje.
Questões em aberto (produto)
- Nenhuma — comportamento: fechar é fechar, em qualquer fase do turno.
Referências
- GitHub Issue # (pai: #660)
src/components/campaign/shell/ai/CampaignAISidebarContext.tsx,src/lib/sollinhaChatSession.tsdocs/plans/sollinha-contexto-sessao-janela.md,docs/plans/sollinha-drawer-fecha-link-mobile-impl.md