0

Quase reportamos um bug que não existia — e isso mudou como validamos conflitos em repositórios

Eu escrevo uma ferramenta que procura sinais contraditórios dentro de um repositório, principalmente relacionados a gerenciadores de pacotes. A parte difícil nunca foi encontrar dois lockfiles. Isso é trivial. A parte difícil é decidir se aquilo é realmente um conflito.

Essa distinção quase me custou caro.

O caso que quase virou uma issue

Analisando um repositório, tudo que estava visível apontava para npm:

  • npm ci nos três workflows
  • npm install no README e no CONTRIBUTING
  • nenhum campo packageManager declarado

O detector classificou como "conflito comprovado". Eu estava a um clique de abrir uma issue.

Antes disso, fui olhar na mão. Existia um pnpm-workspace.yaml com configurações que só o pnpm lê — allowBuilds e minimumReleaseAgeExclude — com três semanas de histórico de commits atrás delas.

O pnpm era intencional. O npm também era um caminho de CI intencional. Os dois conviviam de propósito.

Não abri a issue. Consertei o detector.

O que mudou

Passei a ler arquivos de configuração específicos de cada gerenciador (pnpm-workspace.yaml, .yarnrc.yml, bunfig.toml, chaves de .npmrc que só o pnpm entende) como evidência de apoio, não como veredito.

A regra ficou deliberadamente estreita:

  1. Se existe o campo packageManager, ele manda. Qualquer coisa que discorde dele é a contradição — não o contrário.
  2. Passos de instalação que discordam entre si também são contradição. Testar com um gerenciador e publicar com outro é entregar algo que você não testou.
  3. O que não dá para julgar é quando não há packageManager declarado, um gerenciador carrega configuração real e o outro é operado de forma independente. Isso é ou suporte duplo intencional, ou uma migração que parou no meio — e o repositório não diz qual.

Só no caso 3 o achado sai do nível "comprovado" e vira "ambíguo". Nunca é suprimido, apenas rebaixado.

Um detalhe que importa: a configuração é lida pelo conteúdo, não pela existência. Um pnpm-workspace.yaml que só tem uma lista packages: sobrevive a uma migração exatamente como um lockfile velho sobrevive. Tratar a presença como intenção recria o mesmo falso positivo um nível acima.

Medindo em 222 repositórios alcançáveis: 44 tinham configuração específica de gerenciador, 6 foram rebaixados, 216 continuaram como estavam. Cada rebaixamento custa uma detecção real, então a regra precisa ser estreita.

O problema que eu ainda não resolvi

O uPlot atualiza package-lock.json e bun.lock no mesmo commit, de propósito. Tem bunfig.toml também.

Meu detector ainda erra nesse caso. E não dá para corrigir com um remendo: no estado estático de um único commit, suporte duplo intencional é indistinguível de "migrei e esqueci de apagar o lockfile antigo". Para separar os dois você precisa do histórico de commits, e esse detector não lê histórico.

Essa foi a conclusão mais útil de todo o trabalho: estado estático de repositório nem sempre determina intenção.

Por que isso importa mais agora

Um mantenedor humano carrega a resposta não escrita na cabeça: "esse projeto é pnpm, aquele lockfile é lixo". Um agente de código não carrega. Ele lê o repositório, vê dois sinais, escolhe um — normalmente o último que viu — e segue com confiança.

AGENTS.md ajuda, mas é contexto, não imposição. Se a implementação divergiu do que está escrito lá, só uma verificação determinística percebe.

Resultado prático

Dois mantenedores confirmaram e corrigiram casos reais que reportei; um terceiro corrigiu e ainda pediu mais sugestões — revisei o repositório inteiro e respondi honestamente que não havia mais nada. Rejeitei mais candidatos do que publiquei, e continuo achando que essa é a métrica certa.

A ferramenta

A ferramenta é o CrossCheck. MIT, sem dependências de runtime, sem chamadas de rede durante o scan:

npx @zfinia/crosscheck
npx @zfinia/crosscheck --base origin/main

https://github.com/zFinia/crosscheck

Tem também um scan rápido para repositórios públicos, sem login e sem cadastro (é bem mais estreito que o CLI — só olha sinais de gerenciador de pacote na raiz):

https://www.zfinia.com/cursed?utm_source=tabnews&utm_medium=community&utm_campaign=global_distribution_20260924&utm_content=quase_bug_inexistente

Divulgação: sou o Paul, fundador da zFinia, na Austrália. Existe um plano pago para monitoramento contínuo, mas o CLI e a GitHub Action são gratuitos e não exigem conta.

Se alguém aqui tiver um contraexemplo — um repositório onde essa lógica erra — é exatamente disso que eu preciso. A interface do CrossCheck ainda está só em inglês.

Carregando publicação patrocinada...