O plano de correções da API — o que ficou aberto na v0.9.0¶
Escrito em 2026-09-23, depois de a API pública chegar a produção.
Nada aqui é defeito que quebre quem usa. São sete pendências, cada uma medida e declarada em algum PR, spec ou registro de aceitação — e a lista existe porque declarar sete coisas em sete lugares diferentes é o mesmo que não declarar nenhuma.
O resumo¶
| # | O que | Onde nasceu | Severidade | Estado |
|---|---|---|---|---|
| 1 | a janela do limite de taxa era fixa | PR #936, declarado | alta | corrigido, aguardando release |
| 2 | o CD compara 0.9.0 com v0.9.0 |
CD da v0.9.0 | alta | corrigido, aguardando release |
| 3 | ~~o log não distingue as causas de recusa~~ | SC-004 | — | corrigido em 2026-09-23: cinco motivos distintos |
| 4 | as medidas não trazem proveniência | SC-007 | média | depende da 062 |
| 5 | ~~ninguém poda api_access_reads~~ |
PR #936 | — | decidido em 2026-09-23: guardar indefinidamente |
| 6 | ~~nenhuma tela lê o registro de acesso~~ | PR #936 | — | entregue em 2026-09-23: o painel de uso abre na linha de /api-tokens, por rota e com a janela dita |
| 7 | dois testes instáveis | gates | baixa | varredura não os reproduziu; diagnóstico acrescentado |
E uma que não é correção e não deve ser esquecida: não houve revisão independente do desenho da API nem da avaliação de segurança. Quatro tentativas por agente falharam em 21 e 22 de setembro.
A ordem, e a razão dela¶
Primeiro — o que já está pronto e só espera uma release¶
1 e 2 estão corrigidos na development. Não há trabalho a fazer: há uma release a
soltar.
O item 2 tem urgência própria e não óbvia: enquanto ele não chegar a main, toda release
termina vermelha com o deploy funcionando. Um CD que reprova por engano treina quem lê a
ignorá-lo — e a próxima falha real passa despercebida.
Custo: uma release. Ganho: o limite de taxa deixa de permitir o dobro na virada, e o CD volta a dizer a verdade.
Segundo — ~~a poda~~ fechado por decisão, não por código¶
5 — o registro de leitura é guardado indefinidamente. Decidido pela pessoa mantenedora
em 2026-09-23, depois de a alternativa de 90 dias ser proposta e recusada. Registrado em
api.access.thresholds, regra access_log_retention.
Nada a implementar. O item existia porque ninguém tinha decidido o prazo — e agora foi decidido. A diferença entre "guardamos para sempre" e "ninguém pensou em podar" é a diferença entre escolha e descuido, e até aquela data era a segunda.
Uma correção ao que este plano dizia antes: eu havia listado isto como problema de
crescimento, afirmando que seria "a tabela que mais cresce na base". Medi, e não é.
190 bytes por linha; 100 mil linhas ocupam 18 MB, contra 68 MB de
spo_performed_project_activities. Chegar a 1 GB exige 5,5 milhões de leituras.
O argumento real nunca foi espaço — era retenção de dado sobre pessoas. Com o prazo indefinido, o registro permite reconstruir sem limite de tempo que alguém consultou o painel de outra pessoa. Está escrito na regra, para que seja consequência conhecida e não descoberta.
Terceiro — o que devolve visibilidade a quem opera¶
6 — nenhuma tela lê o registro. A tabela existe, contar_por_rota/3 responde, e quem
opera precisa consultar à mão.
O valor não é conveniência: a FR-024 aponta o registro de acesso como o caminho para perceber agregação. Um caminho que só existe por consulta manual é um caminho que ninguém percorre.
Escopo mínimo: na tela de tokens que já existe, por linha, quantas leituras na última janela e em quais rotas. Não precisa de tela nova.
Entregue em 2026-09-23, exatamente nesse escopo: a linha abre um painel — não uma nona
coluna, porque as oito da R2.1 são a régua do QA. Por rota, nunca um total, com a janela
escolhida e dita (24 h, 7 dias, 30 dias), e a janela sem leitura escrevendo a ausência em vez
de zero.
Três coisas que o painel diz sobre si mesmo, e que não são nota de rodapé: não mostra o
corpo da resposta; não mostra chamada recusada (essa já está no log interno, com o
motivo); e ele é, ele mesmo, registro sobre pessoas — mostra que a credencial de alguém
leu o painel de outra pessoa, guardado indefinidamente, atrás de require_admin.
Quarto — o que a investigação de abuso precisa¶
3 — o log não distingue inexistente, revogado e expirado. Todas produzem
motivo=credencial_recusada.
A informação existe e é descartada: Token.estado/2 já calcula, e o _ do else em
autenticar/1 joga fora. O conserto é de uma linha, e o cuidado é não quebrar o SC-003 — a
resposta ao cliente deve continuar byte a byte idêntica nos três casos.
Por que importa: "alguém está tentando um token que nunca existiu" — varredura — é investigação diferente de "alguém está usando um token revogado" — credencial que vazou antes da revogação. Hoje as duas começam iguais.
Quinto — o que corrói a confiança no instrumento¶
7 — dois testes instáveis, e não se sabe se há outros. Os dois passam sozinhos e reprovam sob carga.
Não quebra produção. Corrói o gate, que é o instrumento com que tudo o mais é conferido: um gate que reprova por acaso treina quem lê a reexecutar até passar, e a próxima reprovação real vira mais uma instabilidade.
Primeiro passo, e ele não conserta nada: fazer a próxima ocorrência dizer o que aconteceu, em vez de morrer numa asserção crua. Sem isso, cada reincidência custa a investigação do zero.
Segundo: uma varredura — rodar a suíte N vezes e contar reprovações por arquivo — para saber se são dois ou vinte.
Sexto — o que depende de outra feature¶
4 — as medidas não trazem proveniência nem as limitações declaradas da base.
É o SC-007, e não é defeito: o envelope de proveniência foi desenhado para o servidor
MCP, na feature 062, e não chegou à API. Consertar exige KnowledgeBase.measurement/1, que
não existe — é a tarefa T003 daquela feature.
Ordem natural: quando a 062 for implementada, o envelope nasce lá. A API passa a usar o que já existir, em vez de duplicar.
O que a API tem hoje: notas escritas à mão no controlador, e a janela declarada. São honestas, e não são proveniência declarada.
O que esta lista NÃO inclui, de propósito¶
As seis armadilhas do documento da API. Elas não são erros a corrigir: são a forma do dado.
Uma pessoa em duas organizações é uma pessoa. Uma espera em curso não tem duração final. Ausência de leitura não é zero. Achatar qualquer uma delas daria uma API mais simples e mentirosa.
O que se escolheu foi dizer, e a advertência viaja dentro da resposta — medians_note,
counts_note, gaps.note, competencies_note, organizations.note — e não em
documentação que ninguém abre.
Uma exceção parcial: a armadilha 4, os dois denominadores em /projects. Ela tem
denominator_note, mas a assimetria de fundo continua — issues conta por repositório,
start_criterion conta por quadro. Se algum dia os dois passarem a contar pelo mesmo
caminho, a nota some junto. Não está na lista porque ninguém pediu, e porque mudar o
denominador muda o significado de um número que já está publicado.
A pendência que não é técnica¶
Revisão independente não ocorreu. Nem do desenho da API, nem da avaliação de segurança que originou os itens 1 e 5, nem da aceitação da 061 — que foi percorrida por quem implementou.
Quatro tentativas por agente falharam em 21 e 22 de setembro: duas travaram sem escrever nada, duas foram recusadas por indisponibilidade da ferramenta.
Nenhum merge cumpre isso, e está declarado como lacuna em todos os PRs. Classificação correta: revisão não ocorreu.
É a pendência mais barata de resolver e a mais fácil de esquecer, porque não tem código.