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 porrunReviewerunPRReview) 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:
- Sem
gate:declarado (GateConfig.Declared() == false), e um no-op completo -- nenhuma chamada nova, nenhuma linha nova. - Sem
AURUMCODE_CACHE_DIRconfigurado (AC-004), nada e lido nem escrito; a execucao imprime e publica (result.Limitations) a declaracao de que o reaproveitamento esta indisponivel. - 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. - Caso contrario: um HIT substitui
result.Issuespelo 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.Getja 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.versionmais, quando o toolchain Go as embutiu,vcs.revision/vcs.modifieddedebug.ReadBuildInfo()-- um binario antigo nunca reaproveita, nem e reaproveitado por, um binario atual; - identidade do repositorio (
owner/repoNameno--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 deevaluateGate.cmd/aurumcode/aur524_test.go: AC-001..AC-004 viarunReviewerunPRReview, mais um teste de unidade que ancora a sensibilidade da chave ao digest de politica (MUT-001).tests/acceptance/AUR-524.sh: selectorsAC-001..AC-004,AC-002-MUT-001,all.