Skip to content

fix(agent): sdk-core 0.3.6, fala do turno em curso nao duplica no history depois do loadOlder (#140) - #141

Merged
JoaoLuisQuirino merged 2 commits into
mainfrom
fix/140-fala-duplicada
Oct 27, 2026
Merged

JoaoLuisQuirino merged 2 commits into
mainfrom
fix/140-fala-duplicada

Conversation

@JoaoLuisQuirino

Copy link
Copy Markdown
Contributor

Fecha #140.

O que muda

Toda conexão com a box traz de novo o stepStart do turno que roda, com a sequência original e o clientMessageId da fala (o sendState do sidecar). Uma sessão que carrega o history enquanto esse turno roda (F5, outra aba, ou o app reabrindo a sessão depois de o canal cair de vez) recebe a página mais nova, que num turno longo começa depois da fala. O liveQuestion não achava a fala pelo clientMessageId e criava live:user:<id> no fim; o loadOlder depois trazia a linha original, e a fala ficava duas vezes.

Agora o liveQuestion não cria a fala cuja sequência fica antes da linha mais antiga que o history tem, quando o servidor ainda tem linhas mais antigas (hasOlder). Ela é uma dessas linhas e entra pelo loadOlder, no lugar dela, então o history fica igual ao do servidor. Duas exceções mantêm o comportamento de antes:

  • fala mandada por esta sessão (está em sentTexts);
  • box cujo log recomeçou: um hello com lastSequence menor que o fim do history marca que as sequências não são mais comparáveis com as linhas (a mesma regra que o canal usa para pedir o replay do zero). Essa marca volta a valer a cada replaceHistory.

Mudança em liveQuestion, replaceHistory, handleEvent (case hello) e beforeLoadedRows (src/agentSession.ts).

Causa confirmada

  • Banco do copilot alpha (só SELECT): a tarefa ecdf5e92... tinha 121 linhas depois da fala dfa3f96e... até 20:48 UTC, então a página de 100 do reload não a contém.
  • O caminho do reconcilePersistedTurn sozinho não duplica: com o turno em curso na sessão, o stepStart repetido cai em repeatsTheRunningTurn antes do liveQuestion (testado). O que duplica é a sessão que carrega o history com o turno já rodando, onde esse filtro não vale porque a sessão ainda não segue turno nenhum. O bug existe na 0.3.5.

Mudança de comportamento

  • Quem abre um chat no meio de um turno longo não vê mais a fala do turno no fim do history, nem duplicada depois do loadOlder. Antes do loadOlder ela não aparece, como no servidor, porque está numa página de trás.

Release

sdk-core 0.3.6, com o corpus contracts/v0.3.6 (mesmos casos da 0.3.5) e current no manifest. O #139 também está marcado como 0.3.6: quem mergear primeiro fica com o número. Publicação fica com o Joao.

Testes

npm run check verde: 739 de 739 (16 arquivos), format:check, lint, typecheck, build, check:exports, check:contracts, pack:check e smoke:package. Testes novos em agentSession.test.ts ("a turn whose question is older than the loaded page"):

  1. Sessão aberta por taskId com página que começa depois da fala e hasOlder, hello do mesmo log, stepStart repetido do turno: o history não ganha a fala no fim; depois do loadOlder ela aparece uma vez, antes das linhas do turno. Falha sem a correção (live:user:cm-q no fim).
  2. Mesma página, hello de um log que recomeçou e stepStart de sequência baixa: a fala aparece ao vivo, como antes.

A prova na dev por tarball contra a box viva vai nos comentários.

🤖 Generated with Claude Code

…tory depois do loadOlder (#140)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@JoaoLuisQuirino
JoaoLuisQuirino requested a review from a team as a code owner October 27, 2026 21:05
@JoaoLuisQuirino

Copy link
Copy Markdown
Contributor Author

/gandalf

Comment thread src/agentSession.ts
@gandalf-the-grey-bot

Copy link
Copy Markdown

Gandalf, análise 1 · commit 0066b018

Pega-rápido

  • Mérito: aprovado. O PR corrige na origem a duplicação da pergunta entre o fluxo ao vivo e o carregamento do histórico, mantendo a mudança no Core SDK.
  • CI: verde no run #40583827056: 16 arquivos e 739 testes passaram, incluindo 57 testes de agentSession.

Insights

Forma

  • O helper fica privado e próximo de liveQuestion, sem ampliar a API pública nem trazer churn alheio ao escopo.
  • major A supressão em src/agentSession.ts:2144 pode descartar uma pergunta legítima do log atual: se as linhas paginadas pertencem a outro boxLog e o hello.lastSequence é igual ou maior que o histórico antigo, a troca de log não invalida rowsInChannelLog. Um stepStart novo com sequência baixa é filtrado, e loadOlder não o recupera. Use a identidade do log efetivamente escolhida pelo canal antes de suprimir linhas e cubra a combinação com hasOlder em teste. Conversa na linha.

Score: 8,91/10.

Confiança: alta no achado, sustentado pelo fluxo determinístico e pelo contrato local; a validação de código foi estática, com CI verde.

Decisão: não aprovaria até corrigir o major.

Aqui o meu check é obrigatório: depois de um push, comente /gandalf para eu revisar o commit novo e liberar o merge.

Comment thread src/agentSession.ts Outdated
@gandalf-the-grey-bot

gandalf-the-grey-bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Gandalf, análise 1 · commit 0066b01

Pega-rápido
A issue #140 descreve perguntas duplicadas no histórico da sessão. O PR tenta impedir a duplicação quando a pergunta chega pelo fluxo ao vivo antes de a página antiga ser carregada.

Insights
O problema e o consumidor afetado estão identificados, então a mudança faz sentido. O repositório já distingue a identidade do boxLog ao decidir o replay; a nova supressão precisa usar essa mesma distinção para não esconder uma pergunta nova. Reconciliar a linha carregada por clientMessageId também é uma alternativa a considerar, mas não é um bloqueio separado.

Forma da implementação
Aprovada com ressalvas. A mudança é localizada e os testes existentes cobrem a duplicação original e a troca para um log novo mais curto. A ressalva é manter rowsInChannelLog coerente com a identidade do log já conhecida pelo canal.

Findings

Confiança e CI
Confiança alta no caminho de regressão pela leitura estática do fluxo; moderada na validação ponta a ponta. O Actions no SHA revisado terminou verde, incluindo build, test e as duas matrizes de compatibilidade; o job de teste reportou 739 testes aprovados.

Decisão: Não aprovaria até corrigir o ponto major descrito acima.

Aqui o meu check é obrigatório: depois de um push, comente /gandalf para eu revisar o commit novo e liberar o merge.

…no log das linhas (#140)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@JoaoLuisQuirino

Copy link
Copy Markdown
Contributor Author

/gandalf

@gandalf-the-grey-bot gandalf-the-grey-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nenhum comentário de linha nesta rodada.

@gandalf-the-grey-bot

Copy link
Copy Markdown

Gandalf, análise 2 · commit d4d2608

Desde a análise 1: os dois achados major foram corrigidos (ponto 1, ponto 2); não encontrei achado novo. As duas conversas ainda aparecem abertas no GitHub e precisam ser resolvidas para liberar a regra de resolução de threads.

Decisão: mergeável depois de resolver as duas conversas corrigidas.

Aqui o meu check é obrigatório: depois de um push, comente /gandalf para eu revisar o commit novo e liberar o merge.

@JoaoLuisQuirino

Copy link
Copy Markdown
Contributor Author

Prova na dev (box viva)

Script p140.mjs, agente de teste com Codex. A sessão A manda uma fala que pede 75 comandos de terminal separados. Com o turno ainda rodando e mais de 100 linhas depois da fala no servidor, o script fecha o socket de A no lado do cliente, como numa queda de rede. Depois abre a sessão B pelo taskId no turno em curso, que é o reload do app depois da queda, um F5 ou outra aba. Copilot da dev: main + #559; template: main + #788.

0.3.5 publicada (task 3dbda636) tarball 0.3.6 d4d2608 (task e54ccd5e)
Linhas depois da fala com o turno rodando 111 111
A depois da queda do socket fala 1 vez fala 1 vez
B antes do loadOlder (página de 50, hasOlder) fala no fim (live:user:425579a9, item 52 de 68) fala fora da página, 0 vezes
B depois do loadOlder fala 2 vezes: a linha 97fe7d16 no topo e a live:user no meio do trabalho fala 1 vez, a linha 8d5006f3 no lugar dela
Falas de usuário em B contra o servidor 2 contra 1 1 contra 1
Resultado 3/6 6/6

A queda do socket com reconcile não duplica em nenhuma das duas versões. O que duplica na 0.3.5 é a sessão que carrega o history com o turno já rodando, como descrito na issue.

Regressão curta na dev

Cenário 0.2.13 0.3.5 publicada tarball 0.3.6
reg old 12/12 12/12 12/12
t116 (providers, loadOlder/hasOlder) 19/19 19/19
close2 29/29 29/29

A primeira rodada do close2 deu 11/17 nas duas versões, com as mesmas falhas: a box Codex do usuário de teste tinha sido aberta por uma task de outro teste, e a trava do script não deixou matá-la. O dono liberou a box, e a segunda rodada deu 29/29 nas duas.

@JoaoLuisQuirino
JoaoLuisQuirino merged commit a17cbc6 into main Oct 27, 2026
6 checks passed
@JoaoLuisQuirino
JoaoLuisQuirino deleted the fix/140-fala-duplicada branch October 27, 2026 22:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants