Pular para conteúdo

AUR-526: review lê arquivos e busca no repo em qualquer linguagem

Medido antes (leitura, base a2d71756)

  • A deliberação (AUR-580) já existia: internal/deliberation.Session com max_rounds, max_cost_tokens, per_tool_timeout_seconds, validação de argumentos e transcrição; internal/review/tools oferecia scanner_<engine>, codebase_context (só arquivos do diff) e skill_section. Nenhuma ferramenta lia outro arquivo, buscava texto ou símbolo.
  • O card lista internal/reviewtools; esse pacote não existe: as ferramentas vivem em internal/review/tools (coberto por internal/review em paths).
  • analyzer.Repo só exporta TrackedFiles (árvore do commit com id de blob e modo); a leitura de blob é privada. internal/grammar.Provider devolve os símbolos definidos por arquivo (sem posição), detecção de linguagem e IsComment.
  • config.contextInjectingProvider (internal/config/wrap.go) e profileProvider (cmd/aurumcode/profiles.go) alteram o pedido e não são llm.Unwrapper: com o contexto do repositório ligado, llm.AsToolCaller ficava falso e a deliberação nunca era oferecida.
  • Uma chamada de ferramenta sem id vinda do provedor passava; uma chamada de assistente sem id era enviada de volta (AC-008).

Decisão

  • Ferramentas novas em internal/review/tools, um arquivo por responsabilidade: read_file (linhas numeradas, até 200 por chamada), search_text (texto literal, até 50 ocorrências), find_symbol (arquivos cuja gramática declara o nome + ocorrências de palavra inteira fora de linha de comentário, decidida pela gramática; arquivo sem gramática entra como texto e diz isso) e changed_file_diff (diff revisado de outro arquivo alterado). Nenhuma lista de linguagem: tudo vem de grammar.Provider.
  • Revision (revision.go) é a única porta de leitura. Um arquivo só é lido se: o caminho é limpo, relativo e chave de TrackedFiles(HEAD) (o commit revisado: --base revisa base..HEAD; --pr usa o checkout já verificado como head da PR); o modo não é 120000; os.Lstat mostra arquivo regular (nunca segue link); o diretório resolvido fica dentro da raiz; o tamanho cabe; e o sha1 de blob git dos bytes é igual ao id do commit. Bytes que diferem (edição na árvore de trabalho, outro commit) são recusados: é a prova de "nunca a árvore de trabalho nem outro commit" sem ler blobs.
  • Arquivo de segredo: catálogo embutido internal/config/secret_paths.yml (dado, não código) + deliberation.secret_paths; arquivo de ignore pela mesma sintaxe de glob (Config.IgnoresPath). Sem catálogo, tudo é segredo (falha fechada). Todo resultado passa pela redação AUR-009 (na ferramenta e de novo na sessão).
  • Teto de bytes deliberation.max_read_bytes (padrão 256 KiB), dividido pelas ferramentas do repositório (ByteBudget). O resultado que passaria do teto não é devolvido; review.Deliberation.Partial (preenchido por tools.PartialLimit) converte o teto atingido em deliberation.LimitError{max_read_bytes} depois da conversa: a resposta é descartada e o caminho existente modelDeliberationLimit torna a revisão inconclusiva conforme gate.inconclusive. internal/deliberation não foi alterado.
  • AC-007: contextInjectingToolProvider e profileToolProvider só existem quando o provedor embrulhado é llm.ToolCaller; injetam o mesmo bloco (no primeiro user) ou prefixo (na primeira mensagem). Um provedor sem ferramentas continua sem (AC-006 intacto). Perfis continuam sem deliberação (toolsCapable), sem mudança de comportamento.
  • AC-008: llm.ValidateToolCall (id, nome e argumentos objeto) no Orchestrator.CompleteWithTools (qualquer provedor), no parse do LiteLLM e em toWireMessage para chamadas de assistente enviadas.
  • --pr com checkout não verificado (scanBlocked) ou raiz sem .git: as ferramentas do repositório não são oferecidas, com aviso no stderr.

Como cada AC/MUT é provado (tests/acceptance/AUR-526.sh)

  • AC-001: TestAUR526SymbolFindsTheJavaCallerOutsideTheDiff (fixture Java internal/review/tools/testdata/aur526: definição pela gramática, chamador Caller.java:6, linha de comentário fora), TestAUR526JavaCallerReadThroughToolsFindingAnchoredInDiff (o modelo pede find_symbol, recebe o chamador e o achado final fica em Contract.java:5), TestAUR526ReadFileReachesTheModelThroughTheCommand.
  • AC-002: TestAUR526ReadsTheReviewedRevisionNeverTheWorkingTree (edição depois do commit e arquivo não rastreado recusados; busca não os vê).
  • AC-003: TestAUR526RefusesEscapesIgnoredAndSecretFiles, TestAUR526SecretAndIgnoredPaths; redação verificada no conteúdo.
  • AC-004: TestAUR526ByteCeilingMakesTheReviewPartial, TestAUR526ByteCeilingIsADeliberationLimit, TestAUR526ReadCeilingIsInconclusive (exit != 0, gate inconclusive, limit=max_read_bytes, nada publicado) e o teto de rodadas do AUR-580.
  • AC-005: o achado do loop passa por applyGates como qualquer outro (o do chamador fora do diff nunca chega a Issues).
  • AC-006: caracterização (TestAUR580DeferredScannerRunsWhenToolsCannotBeOffered, TestAUR525ToolSupportDetectableBeforeCall).
  • AC-007: TestAUR526ContextWrapperForwardsToolCalling, TestAUR526ProfileDecoratorForwardsToolCalling.
  • AC-008: TestAUR526ValidateToolCall, TestAUR526OrchestratorRefusesIDLessCall, TestAUR526IncomingCallWithoutIDIsRefused, TestAUR526OutgoingInvalidCallIsNeverSent.
  • MUT-001: troca os.Lstat por os.Stat em Revision.Read; o fixture é um arquivo rastreado substituído no disco por link para fora com os mesmos bytes (o id de blob bate e o diretório é a raiz), então só a guarda de link o recusa e TestAUR526MUT001SymlinkWithMatchingBytesIsRefused fica vermelho.

Portão de recall (pendente de medição)

O Outcome exige recall maior no corpus do AUR-523 sem queda de precisão acima do limite. Limite registrado: queda de precisão de no máximo 2 pontos percentuais. A medição não foi executada neste stream (regra: escrever sem executar); fica para o fluxo único de testes. Sem ganho medido, o card volta ao backlog.

Documentação stale corrigida

  • docs/configuration.md (Deliberação): lista de ferramentas não citava leitura de arquivo/busca; limites sem max_read_bytes; chaves novas max_read_bytes e secret_paths no bloco yml.
  • docs/architecture.md: ferramentas oferecidas e limites; decoradores que agora repassam llm.ToolCaller.
  • docs/tutorials/deliberacao.md: objetivo, limites e "O que validar". As saídas gravadas em demo/tutoriais/deliberacao/out/ passam a listar as ferramentas do repositório em "oferecidas"; a regravação fica para o fluxo final (regra do stream).

Correção da revisão estática

  • Cache de arquivos com teto de memória deliberation.max_cache_bytes (padrão 64 MiB): passar dele recusa a leitura e esgota o orçamento com o limite max_cache_bytes, a mesma revisão parcial de max_read_bytes (TestAUR526CacheCeilingMakesTheReviewPartial).
  • Catálogo de segredo comparado sem diferenciar maiúsculas (.ENV, Credentials) e ampliado com kubeconfig, .kube/config, .docker/config.json e *service-account*.json (TestAUR526SecretPathsIgnoreCaseAndCoverCloudCredentials).