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 cinos três workflowsnpm installno README e no CONTRIBUTING- nenhum campo
packageManagerdeclarado
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:
- Se existe o campo
packageManager, ele manda. Qualquer coisa que discorde dele é a contradição — não o contrário. - 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.
- O que não dá para julgar é quando não há
packageManagerdeclarado, 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):
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.