Pular para conteúdo

AUR-608 — O modelo decide sobre a regex: gate.triage model por padrão, fail-closed e declarado

Problema

Direção do dono (2026-10-08): "O llm deve decidir com raciocínio. Uma regra estática vai gerar falso positivo". Antes deste card:

  • gate.triage.<fonte> ausente valia none (internal/config/gate.go): o raciocínio do modelo sobre a evidência determinística só contava se o repositório escrevesse model.
  • Uma contestação (disputed) sem justification rebaixava igual a uma justificada.
  • A triagem agia mesmo sem gate declarado (um scanner retém a aprovação sem gate:), e sem resposta do modelo nenhuma linha dizia que a triagem não aconteceu.

Desenho

arquivo responsabilidade
internal/config/gate.go triageDefault = TriageModel; TriageMode/TriageModeFor devolvem o valor declarado ou model; toda chave que casa é lida e qualquer none vence (independe da ordem do mapa). TriageByModel/TriageByModelFor passam por elas.
internal/review/assessments_dispute.go DisputeCounts: só disputed com justification não vazia (após TrimSpace) pode rebaixar; sem ela vale needs_context.
cmd/aurumcode/review_gate.go triage(): com qualquer motivo de modelo não saudável (modelReason() != "": sem provedor, falha do provedor, parse falho ou degradado, limite de deliberação) não rebaixa nada; senão registra em Disputed só contestação justificada. triageSources(): por fonte, se o modelo pode rebaixar; só com gate declarado e sem política central; engines da mesma categoria combinam por E (um none mantém a categoria contando). modelReason(): o motivo do modelo pelo mesmo gate.RankReason.
cmd/aurumcode/review_triage_notice.go reportTriageNotRun: com evidência de fonte triável (triageSources) e modelo sem resposta limpa, escreve no stderr e nas limitações do parecer que a triagem não ocorreu e a evidência contou integralmente (e que o bloqueio foi mantido, quando o gate reprovou).
internal/i18n/catalog.yml notice.triage_not_run e notice.triage_not_run_clear em pt-BR e en.
internal/gate/triage.go Comentário de Triage atualizado (sem mudança de comportamento).
docs/configuration.md Seção gate.triage: padrão model, opt-out none, justificativa obrigatória, aviso sem modelo, política central inalterada.

disputedEvidence() não mudou: continua alimentando as exceções propostas, inclusive sob política central, então a saída sob política é a mesma (AC-006). O prompt já pedia justification; nada nele mudou.

Resultado consolidado degradado: sob --perfis ou em lotes, um perfil ou lote degradado marca o resultado inteiro como degraded_parse (profiles.go, internal/review/batch_merge.go), mas as avaliações dos perfis/lotes bons continuam lá. Sem a checagem de modelReason() em triage(), essas contestações rebaixavam e, com gate.inconclusive: warn, a execução podia passar (falha aberta) enquanto o aviso afirmava que a evidência contou integralmente. Agora a triagem inteira é desligada.

ACs e testes

tests/acceptance/AUR-608.sh (cópia de go.mod go.sum cmd internal pkg; pacotes internal/config, internal/review, cmd/aurumcode):

id prova
AC-001 TestAUR608DefaultTriageDemotesJustifiedDispute (--base real, gate declarado sem triage, analysis E1 e semgrep E2 contestados com justificativa: exit 0 e gate.triage (analysis: model) / gate.triage (sast: model) no stderr), TestAUR608TriageLinesReachTheParecer (a linha entra no corpo publicado), TestGateTriageParse, TestAUR608GateTriageByModelForScannerDefault
AC-002 TestAUR608ExplicitNoneKeepsEvidenceCounting (analysis: none, semgrep: none: exit 3, nada anunciado), TestAUR608GateTriageExplicitNoneOptsOut, TestAUR579PolicyFloorAndRepositoryTriage (caso analysis: none)
AC-003 TestAUR608DisputeWithoutJustificationStillCounts (justificativa vazia e só espaços: exit 3), TestAUR608DisputeCountsRequiresJustification
AC-004 TestAUR608WithoutModelKeepsDeterministicBlock (sem provedor, gate declarado: exit 3 e a linha no stderr, em pt-BR com review.language: pt-BR e em en-US no padrão), TestAUR608TriageLinesReachTheParecer (a linha entra nas limitações do parecer publicado; gate aprovado não fala em bloqueio), TestAUR608DegradedModelNeverDemotes (passos reais de gate e saída da sessão: resultado degraded_parse, contestação justificada, inconclusive: warn: exit diferente de 0, nada "deixou de contar", aviso (degraded_parse) no stderr e nas limitações)
AC-005 TestAUR608TriageSilentWithoutDeclaredGate (SAST sem gate: e contestação justificada: exit 3, nada anunciado; sem modelo e sem gate: nada anunciado)
AC-006 TestAUR608CentralPolicyUnchanged (política fail_on: [high] sem triage: contestação justificada continua reprovando e vira exceção proposta; sem modelo, nada anunciado), TestAUR579PolicyFloorAndRepositoryTriage

Mutações

id mutação teste que cai
MUT-001 const triageDefault = TriageModel → TriageNone TestAUR608DefaultTriageDemotesJustifiedDispute
MUT-002 strings.TrimSpace(a.Justification) != "" → != "mutant" (aceita contestação sem justificativa) TestAUR608DisputeWithoutJustificationStillCounts
MUT-003 if s.modelReason() != "" { → if false { em triage() (resposta degradada volta a rebaixar) TestAUR608DegradedModelNeverDemotes

Compatibilidade

  • Repositório com gate declarado e sem triage: uma contestação justificada do modelo passa a rebaixar a evidência determinística (antes contava). Para manter o comportamento anterior, escrever none por fonte.
  • Repositório que já escrevia model ou none: sem mudança, exceto que contestação sem justificativa deixa de rebaixar.
  • Sem gate declarado: nada é rebaixado (antes uma engine com triage: model podia rebaixar mesmo sem gate:).
  • Sob política central: nada muda.
  • Execuções sem modelo ou com resposta degradada, com evidência triável e gate declarado, ganham uma linha no stderr e nas limitações do parecer, no idioma de review.language; nelas nada é rebaixado.