Pular para conteúdo

AUR-524: o mesmo PR com a mesma politica tem o mesmo veredito

Problema

Antes deste cartao, --base e --pr nunca compartilhavam nada: o cache por arquivo do AUR-513/AUR-441 (cmd/aurumcode/review_cache.go, internal/review/cache) so e consultado por --base, e tanto --base quanto --pr chamam o modelo de novo a cada execucao. Como gate de compliance, isso significa que reexecutar o gate sobre o MESMO SHA revisado, sob a MESMA politica central, podia produzir um veredito diferente apenas porque o modelo respondeu de forma diferente na segunda chamada -- e, especificamente, podia produzir um veredito diferente entre a chamada --pr (o gate de CI) e uma chamada --base local sobre o mesmo commit.

Design

Um segundo reaproveitamento, mais grosso que o do AUR-513: o conjunto de achados ja filtrado que o gate avalia (result.Issues, exatamente o que evaluateGate recebe) e guardado e devolvido por uma chave que combina:

  • a identidade do modelo, incluindo a base URL (modelCacheKey, reaproveitada sem duplicacao);
  • a linguagem, o contexto de codebase, as notas de memoria, a selecao de perfil, o digest do bloco de contexto e o digest do catalogo de regras (reviewContextCacheKey, a mesma funcao que o AUR-513 ja publica);
  • o digest da politica central ativa (render.PolicyDigest, o mesmo digest que o AUR-521 ja grava na trilha de auditoria) -- "" quando nenhuma politica esta ativa;
  • a identidade do SHA revisado (GITHUB_SHA, lido identicamente por runReview e runPRReview) ou, na ausencia de um SHA, um digest de conteudo do diff revisado.

gateVerdictCacheKey (cmd/aurumcode/aur524.go) combina esses dois grupos: chama reviewContextCacheKey sem duplicar sua logica e funde o digest de politica e a identidade revisada por cima, com SHA-256. A mudanca de QUALQUER um desses insumos produz uma chave diferente, logo o veredito nunca atravessa uma politica, uma skill, um modelo ou um commit diferentes (AC-002).

O armazenamento reusa o MESMO internal/review/cache.Cache que o AUR-441 ja define -- o mesmo diretorio (AURUMCODE_CACHE_DIR), o mesmo cache.Entry (campo Issues), nenhum store novo. A entrada de veredito usa Path: "aur524-gate-verdict" apenas como rotulo de leitura; a chave em si (nao o Path) e o que separa uma entrada de veredito de uma entrada por arquivo do AUR-513.

reuseOrStoreGateVerdict e o unico ponto de chamada, usado identicamente por runReview (main.go) e runPRReview (pr.go), logo apos gateInconclusiveReason ser decidido e logo antes de evaluateGate rodar:

  1. Sem gate: declarado (GateConfig.Declared() == false), e um no-op completo -- nenhuma chamada nova, nenhuma linha nova.
  2. Sem AURUMCODE_CACHE_DIR configurado (AC-004), nada e lido nem escrito; a execucao imprime e publica (result.Limitations) a declaracao de que o reaproveitamento esta indisponivel.
  3. Com uma execucao atual inconclusiva (gateInconclusiveReason != "" -- falha de provedor, parse degradado, cobertura parcial), a funcao nunca le nem escreve (AC-003): um veredito inconclusivo nunca mascara, e nunca e mascarado por, um veredito anterior.
  4. Caso contrario: um HIT substitui result.Issues pelo conjunto persistido (e a execucao anuncia o reaproveitamento em stderr); um MISS grava o conjunto desta execucao para uma execucao futura reaproveitar.

Em runReview, a chamada fica logo antes de gateIssues ser formado (result.Issues + os achados deterministicos de seguranca), para que o relatorio impresso (result.Issues) e o que o gate avaliou nunca divirjam -- os achados deterministicos de seguranca sao recomputados a cada execucao (deterministicos a partir do mesmo diff, nunca dependem do modelo) e se somam por cima do conjunto reaproveitado ou fresco.

Em runPRReview, o cartao acrescenta tres identidades que o --pr nunca precisou antes (porque nunca cacheava nada): baseModelIdentity (capturada antes do provider ser contexto-envolvido, como o AUR-513 ja faz em runReview), contextBlockDigest e ruleCatalogDigest (as mesmas funcoes contextBlockCacheDigest/ruleCatalogCacheDigest do review_cache.go). A selecao de perfil (--perfis) nao existe em --pr, logo esse campo da chave e sempre "" -- o mesmo valor que --base produz quando nenhum perfil e escolhido, entao os dois caminhos colidem corretamente na mesma chave para o mesmo SHA/politica/contexto/ modelo.

Checklist do gate (verificado pelos testes)

  • Um veredito reaproveitado nunca transforma uma avaliacao inconclusiva em aprovacao: o reaproveitamento so acontece quando a execucao ATUAL ja e conclusiva por si so.
  • Um veredito nunca atravessa politica, skills, modelo ou SHA diferentes: qualquer diferenca muda a chave.
  • Uma entrada corrompida (JSON invalido) degrada para uma revisao fresca, nunca para um crash ou uma aprovacao falsa (cache.Cache.Get ja trata erro de parse como miss).
  • Uma execucao sem gate declarado permanece byte-idêntica a antes deste cartao.

Nao-objetivos

Tornar o modelo deterministico; reaproveitar entre SHAs diferentes (mesmo que produzam o mesmo diff, dois SHAs diferentes nunca compartilham uma entrada so por coincidencia de conteudo -- a identidade preferida e sempre o SHA quando ele existe).

CI: persistindo o cache entre --base e --pr

Sem AURUMCODE_CACHE_DIR apontando para um diretorio que sobrevive entre os dois jobs (ou entre as duas invocacoes do mesmo job), o reaproveitamento simplesmente nunca acontece -- cada execucao declara isso (AC-004) e segue revisando normalmente; nunca e um erro fatal. Para habilitar o reaproveitamento entre um job que roda --base (ex.: um workflow de PR local) e outro que roda --pr (o gate de CI) sobre o MESMO commit, o workflow pode:

- uses: actions/cache@<SHA fixado>
  with:
    path: .aurumcode-cache
    key: aurumcode-gate-${{ github.sha }}
- run: aurumcode review --pr ${{ github.event.pull_request.number }} --check
  env:
    AURUMCODE_CACHE_DIR: ${{ github.workspace }}/.aurumcode-cache

Isso e apenas documentacao de como uma organizacao PODE configurar seu proprio workflow -- este cartao nao adiciona nenhuma chamada de rede nova nem assume que actions/cache esta disponivel; sem ele, o programa simplesmente nunca encontra AURUMCODE_CACHE_DIR e declara reaproveitamento indisponivel, exatamente como em qualquer outro ambiente sem esse diretorio.

Versao 2 (2026-10-02, apos revisao independente)

A revisao independente do SHA e624e7d (versao 1 deste cartao) reprovou o design por um motivo de confianca: AURUMCODE_CACHE_DIR e entrada nao confiavel em CI. Num runner ou escopo de cache compartilhado, outro workflow (ou um passo com privilegio diferente no mesmo workflow) pode escrever no MESMO diretorio que este cartao le. O design da versao 1 ("reaproveitar o veredito inteiro, substituindo os achados atuais") deixava uma entrada forjada -- por exemplo uma com "issues":[] -- livre para transformar uma reprovacao real numa aprovacao silenciosa. Isso e exatamente o oposto do que um gate de compliance existe para impedir (CR-TRUST-001).

A versao 2 torna o reaproveitamento monotonico: uma entrada guardada so pode ACRESCENTAR achados aos que a execucao atual encontrou por conta propria -- nunca substituir ou remover. O pior que uma entrada forjada/velha/de outro repositorio pode fazer e acrescentar uma reprovacao espuria (um falso negativo se torna visivel, alto, como mais um achado a julgar) -- nunca fabricar uma aprovacao. A uniao usa igualdade exata (types.ReviewIssue e uma struct totalmente comparavel).

O que e guardado

O que storeGateVerdict persiste e sempre o conjunto RAW de achados -- exatamente result.Issues como estava IMEDIATAMENTE ANTES de config.ApplyRuleConfig rodar (main.go, antes da linha que aplica repoCfg; pr.go, antes da linha equivalente com reviewConfig) -- nunca o conjunto ja filtrado pelo config da execucao que escreveu a entrada. Num HIT, esse conjunto RAW e reaplicado contra o config da execucao ATUAL (config.ApplyRuleConfig(stored, cfg)) antes de entrar na uniao (AC-006): uma regra que o repositorio desabilitou depois de a entrada ter sido escrita para de disparar mesmo vinda do cache; uma regra reabilitada depois de ter sido desabilitada volta a disparar.

A chave (ampliada)

Alem dos termos ja existentes na versao 1 (identidade do modelo via reviewContextCacheKey/modelCacheKey, incluindo a base URL; digest da politica central via render.PolicyDigest), a versao 2 acrescenta:

  • digest do diff (diffContentDigest), SEMPRE presente -- nao e mais SHA-ou-digest; e digest-sempre, mais o SHA quando ele existe, nunca o SHA sozinho;
  • SHA revisado (GITHUB_SHA), quando conhecido, somado ao digest do diff, nunca no lugar dele;
  • digest da versao do prompt (AUR-543, newCacheDigestBuilder().FixedContentDigest()) -- o MESMO valor que o cache por arquivo do AUR-513 ja usa, reaproveitado aqui;
  • identidade do binario (binaryIdentity): a flag -ldflags -X main.version mais, quando o toolchain Go as embutiu, vcs.revision/vcs.modified de debug.ReadBuildInfo() -- um binario antigo nunca reaproveita, nem e reaproveitado por, um binario atual;
  • identidade do repositorio (owner/repoName no --pr, localRepoIdentity(cwd) no --base, "" quando desconhecida -- nunca colide com uma identidade conhecida).

Checklist do gate (reafirmado para a versao 2)

  • Uma entrada reaproveitada nunca transforma uma avaliacao inconclusiva em aprovacao -- e, sob o design monotonico, nunca poderia transformar nada em aprovacao de qualquer forma: ela so acrescenta.
  • Uma entrada nunca atravessa politica, skills, modelo, versao de prompt, binario, repositorio, SHA ou digest de diff diferentes.
  • Uma entrada nunca e lida como aprovacao quando o gate atual reprovou: o gate decide sobre a UNIAO, e a execucao atual nunca e descartada.
  • Uma entrada corrompida ou forjada (lista vazia, por exemplo) degrada para "nada a acrescentar", nunca para uma aprovacao ou um crash.

Risco residual documentado, nao corrigido por este cartao

O cache por arquivo do AUR-513 (internal/review/cache, usado so por --base) vive no MESMO diretorio (AURUMCODE_CACHE_DIR) e e, ele proprio, confiado pelo --base para decidir se uma aprovacao e valida: um HIT de arquivo pula a chamada ao modelo inteiramente e tracta o conteudo anterior como "revisado e limpo". Se AURUMCODE_CACHE_DIR apontar para um cache restaurado de CI (por exemplo via actions/cache) que um atacante ou um workflow de outro privilegio possa ter escrito, o AUR-513 pode, sozinho, produzir uma aprovacao que nunca chamou o modelo de verdade -- independentemente de qualquer coisa este cartao faz. Este cartao NAO corrige esse risco (fora do seu escopo: AUR-513 e um cartao diferente, ja done); a mitigacao operacional e: --base nunca deve apontar AURUMCODE_CACHE_DIR para um cache restaurado de CI quando usado como gate. O uso como gate de CI pretendido por este cartao e via --pr (que nunca usa o cache do AUR-513) ou via --base com um AURUMCODE_CACHE_DIR que o proprio job escreve e nunca restaura de um estado anterior nao verificado.

Arquivos

  • cmd/aurumcode/aur524.go: a chave, o armazenamento/leitura e o unico ponto de chamada (reuseOrStoreGateVerdict).
  • cmd/aurumcode/main.go, cmd/aurumcode/pr.go: a chamada, logo antes de evaluateGate.
  • cmd/aurumcode/aur524_test.go: AC-001..AC-004 via runReview e runPRReview, mais um teste de unidade que ancora a sensibilidade da chave ao digest de politica (MUT-001).
  • tests/acceptance/AUR-524.sh: selectors AC-001..AC-004, AC-002-MUT-001, all.