ADR 0168 — O golden-set do QA passa a rodar em CI, agendado e semanal
Status
Accepted. A cadência foi decidida pelo mantenedor em 2026-09-27 (AT-149): semanal. Referencia o ADR 0123 (o golden-set do julgamento semântico do QA, nascido manual) e o ADR 0138 (o molde do RAG, que declarou o do QA "inteiramente manual"). Não edita nenhum dos dois.
Context
O ADR 0123 deixou o golden-set do QA como TODO(humano) para CI, dizendo que
ligá-lo exigia "segredo de LLM de API ou infra nova (GPU)". O ADR 0138 repetiu
a frase ao separar o RAG (embedding, determinístico) do QA (chat, julgamento).
Duas medições mudaram o quadro, e as duas estão em
docs/explanation/gates.md:
- AT-067 (2026-09-13): num
ubuntu-latestsem GPU, com oqwen2.5-coderpuxado no Ollama do próprio job, o relógio cabe (19–23 min demix golden_set.qa). A metade "infra nova" caiu. Mas o placar era 0/6 por construção: desde a RN-502 onpm testde um projetocontainersem containerrunningé recusado, e o seed nunca subia um. - AT-076 (2026-09-27): o seed passou a subir o container pelo caminho de
produção (
container_startproposto e aprovado, broker, máquina de estados), sem tocar a RN-502. Duas rodadas em CI: 3/6 e 3/6 contra o piso de 1/6, 52 e 86 min de job.
Com o instrumento medindo, o que faltava era a cadência.
A rodada de prova deste workflow (run 36327281774, gatilho temporário de
push, removido antes do PR) deu 3/6 outra vez, por outro conjunto de casos,
com zero recusas da RN-502, em 13 min 41 s de job — nenhum caso repetiu
comando até o teto desta vez. O tempo vai de 14 a 86 min entre rodadas.
Decision
.github/workflows/golden-set-qa.yml, separado de ci.yml e do RAG,
schedule semanal + workflow_dispatch, nunca pull_request. O mesmo
argumento do ADR 0138: ci.yml é só pull_request, e um golden-set que julga
com um modelo de chat não fica no caminho do merge. Sábado, 01:17 UTC: fora da
janela diária do RAG (06:00), do domingo das provas de propriedade (04:00) e da
segunda do check de links (06:00); sábado para o vermelho esperar a semana
começar; o minuto 17 porque o schedule do Actions atrasa e pode descartar
disparos no topo da hora.
Semanal e não noturno. O do RAG custa ~20 min e é determinístico: uma
regressão de ontem é uma regressão. Este custa 52–86 min medidos e VARIA — as
duas rodadas da AT-076 deram o mesmo total com casos diferentes. Uma rodada por
dia gastaria horas de runner por semana para ler o mesmo sinal de tendência
com mais ruído; uma por semana é o que o piso (ratchet) precisa para acusar uma
queda, e o workflow_dispatch cobre quem mexeu no prompt do QA e quer a
resposta na hora.
timeout-minutes: 150. O maior job medido levou 86 min; a variância é do
modelo (um caso repetiu npm test 30 vezes antes de terminar
blocked(modelo)). 150 cobre uma rodada em que mais de um caso esbarra no teto
de iterações sem virar vermelho por relógio, que não é o que o job mede.
O veredito é o do teste. O ExUnit reprova quando os acertos ficam abaixo do
passRate de floor.json para o modelo, e quando a api responde mas o seed
falha. O workflow acrescenta duas guardas de instrumento, nenhuma de
julgamento: a linha de placar precisa existir (o teste PULA quando a api não
responde, e pular numa rodada agendada é verde que não mediu), e o event log
não pode ter recusa da RN-502 (a assinatura conhecida da AT-067). Em falha,
faz o que o do RAG faz: imprime os logs. Não abre issue.
Api, engine e broker nativos no runner, e o socket do Docker só no broker.
É o desenho medido na AT-076. A mesma pasta gerenciada nos três lados, porque
api e engine rodam no host (PROJECT_WORKSPACES_ROOT =
PROJECT_WORKSPACES_HOST_ROOT). A imagem dos casos é puxada antes do seed, e a
referência vem do próprio seed (IMAGEM_DO_GOLDEN_SET_QA, presa por digest do
índice), não de um segundo literal no workflow: o docker run do broker roda
sob o teto de controle de 30 s da porta de Docker
(RN-604), e uma divergência entre o que o job
puxa e o que o seed pede poria o pull dentro desse teto.
Consequences
O TODO(humano) do ADR 0123 para CI fecha. O que segue com dono humano é o
que o próprio ADR 0123 já dizia: o piso (floor.json) só é escrito por humano,
depois de medir. Este ADR não o mexe, nem o modelo.
Nenhum gate muda. O golden-set do QA não tem gate próprio no registro
(docs/gates.yml); ele é sinal de tendência do passo semântico de
qa-verificada, e um job agendado não trava merge nenhum.
O broker do CI não tem as cinco camadas do compose. Num runner efêmero não
há rede internal nem porta fechada: o que resta de contenção é o que o broker
ACEITA (cinco operações, spec computada pela api a partir do artefato). O
runner é descartado ao fim do job e não guarda segredo nenhum deste workflow
(permissions: contents: read, token de serviço literal de CI).
Declarado, não corrigido aqui: o heartbeat do engine fecha as sessões do
seed (heartbeat_timeout) ~30 s depois de criadas, enquanto o QA segue
propondo comandos nelas (ele roda no processo do mix, fora de sessão), sem
efeito visível no placar da AT-076. E pull_request que mexe neste workflow
não o executa — verde no PR não é prova; a prova é o workflow_dispatch ou o
primeiro sábado.