Pular para conteúdo

Sprint 008 — Review

Período: 2026-08-12 a 2026-08-18 Feature: 009 — marca que se cura PR: #230, MERGEABLE · CLEAN, CI verde em 2m4s

Resumo

Planejado Entregue
User stories 3 3
Tarefas 9 9
Entregáveis aceitos 9 9

10 gates verdes por código de saída, 430 testes, 28 próprios da feature. A avaliação critério por critério está em aceitacao.md: 12 de 12 SC atendidos, dois com a ressalva de não terem sido exercitados em produção.

O que foi feito

Tarefa Issue Entregável Aceito
T001 #220 inaccessible_reason de varchar(255) para text, truncagem na borda sim
T002 #221 transient?/1 julga erro de GraphQL, com o payload real no teste sim
T003 #222 a falha do momento não marca — asserção no banco sim
T004 #223 list_collectable/2 rejeita só o excluído; 96 → 135 coletáveis sim
T005 #224 a data preserva o começo; o motivo carrega a última falha sim
T006 #225 a marca sai e as issues entram na mesma execução sim
T007 #226 a coleta conclui com tudo falhando; o excluído não recebe requisição sim
T008 #227 repositories_unreachable, incrementado a cada falha sim
T009 #228 a lista diz desde quando e por quê, legível sem cor sim

O que não foi feito

Item Motivo Destino
a prova no dado real — as 39 marcas limpas por uma coleta exige a chave mestra e o token, que são da pessoa mantenedora. Eu não os peço nem os recebo pendente, com o procedimento em V2 e V3 do quickstart
iteration própria do sprint configurar iterations recria as existentes — L11 product backlog, #176
verificação visual da tela em 360 px a célula de estado cresceu, e ninguém olhou conferir antes de fechar o próximo sprint

O que a análise mudou, antes do código

Seis correções, e a crítica não era sobre a lógica da feature:

inaccessible_reason era varchar(255). O maior motivo gravado tem 181 caracteres, e o da falha interna dá ~228 com o prefixo — 27 de folga. Sem validate_length, o valor longo vai ao banco e levanta; registrar_ou_seguir/2 cobre changeset inválido, não exceção do driver: a fase de coleta cairia.

O que tornou isso urgente foi a própria feature: ela faz a plataforma escrever esse campo a cada coleta que falhar, em vez de uma vez.

E três suspeitas foram derrubadas por medida em vez de aceitas: o orçamento da origem (cost = 1, 160 pontos de 5 000), outro consumidor de list_collectable/2 (um de produção, zero em teste), e clear_inaccessible/2 deixando o motivo para trás (limpa os dois campos).

Evidências

$ mix gates > /tmp/g9b.txt 2>&1; echo "código de saída: $?"
código de saída: 0
10 gates verdes.
Result: 430 passed
observados=135   coletáveis antes=96   coletáveis depois=135
voltam a ser tentados=39   issues dentro deles=899

Dois defeitos conferidos por reprovação, invertendo o código de propósito:

defeito reintroduzido testes que reprovaram
list_collectable/2 voltando a filtrar o inacessível 3
erro de GraphQL sempre permanente 3

Dívida gerada

Dívida Por quê
repositório apagado na origem será consultado a cada coleta é o preço de não desistir; o critério de revisão é a natureza do erro, nunca o tempo
a classificação da falha interna depende do texto da mensagem texto de terceiro muda; o teste usa o payload real, e a cura torna a marca reversível
a prova no dado real é pendente depende de credencial que não é minha

O defeito que este sprint achou fora dele

O @doc órfão que eu introduzi no commit da feature 008 passou pelos dez gates e pelo CI. Numa compilação limpa, main reprova:

$ git stash && rm -rf _build/dev/lib/the_band && mix compile --warnings-as-errors
main limpo: código de saída 1
redefining @doc attribute previously set at line 395

E o mecanismo que eu publiquei estava errado. O experimento que o isolou, feito depois, mostrou outra coisa: o aviso é emitido — três vezes na saída —, e o gate sai zero porque execute({:mix, ...}) descartava o retorno de Mix.Task.run/2. mix compile --warnings-as-errors não levanta: devolve {:error, diagnostics}.

O gate de compilação nunca reprovou por aviso, nem local nem no CI. Registrado em #229, Bug, P0, com o diagnóstico corrigido, e a L36 reescrita.

A correção veio junto nesta branch porque sem ela a própria feature não passa nos gates — e está declarado no PR.

O defeito do gate, corrigido depois

O achado do @doc órfão levou a uma segunda correção, no PR #231: o gate de compilação nunca reprovou por aviso, porque execute({:mix, ...}) descartava o retorno de Mix.Task.run/2 — e isso valia para todo gate {:mix, ...}.

O veredito passou a ser o código de saída, com cada gate em subprocesso. mix gates completo em 78,6 s, e 2,46 s para reprovar com o defeito presente. O CI ficou verde com o gate honesto, o que significa que nenhuma dívida invisível estava escondida atrás dele.

Lições deste sprint

L36 — gate que descarta o retorno da task não é gate. E o corolário sobre método: quando duas medidas verdadeiras parecem se contradizer, o elo entre elas é hipótese, não conclusão.

L37 — a coluna estreita só cai quando a escrita fica frequente.

Detalhamento em licoes-aprendidas.md.