AUR-540 — O seletor E2E do AUR-467 volta a passar
Problema
tests/e2e/AUR-467.sh E2EAUR467, seletor de um card em done, saía com 1 em
main:
AUR-467/E2E/no-regression-gate-exit:0
A revisão do AUR-539 (2026-10-02) já tinha medido que a causa não era
orçamento de tokens. Este card mede a causa real e corrige o lado certo sem
alterar o que o AUR-467 prometeu (classificação código/prosa e declaração de
cobertura, ambas em internal/prompt).
Medição
tests/e2e/AUR-467.sh builda o binário real e roda:
aurumcode review --base HEAD~1 --fail-on error
contra o fixture tests/fixtures/repos/git-demo/repo.git, injetando uma
única resposta de LLM canônica via AURUMCODE_LLM_FIXTURE (um achado
error/security/command-injection em config/demo-tokens.txt:4). O
script espera rc == 3 (exitFindings, cmd/aurumcode/main.go:1316) — o
código que --fail-on usa quando um achado da severidade escolhida
sobrevive.
Rodando o script sem alteração em main (dentro do container
aurum-go via go-shared exec, nunca no host):
$ go-shared exec -w <worktree> bash tests/e2e/AUR-467.sh E2EAUR467
## Code Review Summary
**Verdict:** Approve
...
No issues found.
aurumcode review: 1 finding(s) descartado(s) pelo gate de escopo e evidencia: 1 sem evidencia concreta
AUR-467/E2E/no-regression-gate-exit:0
exit 1
O binário saiu 0 (aprovado, zero achados) em vez de 3. O achado do
fixture foi descartado antes de chegar ao --fail-on, pela mensagem
"gate de escopo e evidencia" / "sem evidencia concreta".
Essa mensagem vem de internal/review/scope.go:
// filterModelIssues enforces the review's precision contract after parsing
// and redaction, before rule citations or publication. A finding is useful
// structurally publishable when it points to an addition or deletion and
// supplies evidence, impact and a verification proposal.
func filterModelIssues(diff *types.Diff, issues []types.ReviewIssue) ([]types.ReviewIssue, scopeDiscardSummary) {
...
if strings.TrimSpace(issue.Evidence) == "" {
discarded.MissingEvidence++
continue
}
if strings.TrimSpace(issue.Impact) == "" {
discarded.MissingImpact++
continue
}
if strings.TrimSpace(issue.Verification) == "" {
discarded.MissingVerification++
continue
}
...
internal/review é read_paths neste card, não paths: este gate é
produto já fechado e correto, de um card anterior, e não está em escopo
para alteração aqui. O achado do fixture do AUR-467 só tinha file, line,
severity, rule_id, message, suggestion — nenhum evidence,
impact ou verification. pkg/types.ReviewIssue já declara os três
campos (json:"evidence,omitempty", json:"impact,omitempty",
json:"verification,omitempty"); o fixture nunca os preencheu.
A primeira verificação do gate (se o achado cai dentro de uma linha
adicionada/removida do diff, OutsideAddedLines) passou — o relatório só
contou 1 ocorrência de MissingEvidence, nenhuma de OutsideAddedLines.
Confirma que o achado tinha localização válida e foi descartado
especificamente por faltar evidence/impact/verification.
Nem todo fixture de e2e deste repositório ficou atrás do contrato de
evidência: tests/e2e/AUR-439.sh já preenche evidence/impact/
verification nos dois achados sintéticos que escreve. Mas
tests/e2e/AUR-461.sh — de onde AUR-467.sh herdou o formato do double de
LLM — não preenche nenhum dos três, assim como AUR-467.sh antes desta
correção; não foi verificado neste card se AUR-461.sh e os demais
fixtures de AURUMCODE_LLM_FIXTURE sem esses campos (fora dos paths
deste card) também passam a sair RED sob o gate atual — ficam como
candidatos a card de follow-up, não corrigidos aqui.
Causa
Não é regressão do produto dentro do que o AUR-467 prometeu. O gate de
escopo e evidência (internal/review/scope.go) é comportamento correto e
fora do escopo do AUR-467 — ele decide se um achado do modelo é
estruturalmente publicável (localização + evidência + impacto +
verificação), não se um arquivo é código ou prosa. O fixture do E2E do
AUR-467 ficou obsoleto: foi escrito antes desse contrato de forma do
achado e nunca ganhou os três campos.
Correção
tests/e2e/AUR-467.sh: o JSON do fixture ($run_dir/fixtures/finding.json)
ganhou evidence, impact e verification não vazios, descrevendo a
mesma injeção de comando que o achado já alegava — sem citar conteúdo do
arquivo de fixture config/demo-tokens.txt e sem reaproveitar o texto das
duas asserções já existentes (user input reaches a shell,
security/command-injection), para que elas continuem provando que a
mensagem e a citação de regra renderizadas vieram do achado, não do texto
de evidência:
"evidence": "linha 4 concatena uma variavel de ambiente direto na string de comando",
"impact": "um valor hostil na variavel vira comando arbitrario no shell",
"verification": "reexecutar com um valor contendo ; e confirmar que nao e mais interpretado pelo shell"
Com isso o achado sobrevive a filterModelIssues, chega ao --fail-on
error, e o binário sai 3 como o E2E sempre esperou.
Por que preserva a promessa do AUR-467
- Nenhuma linha de
internal/prompt,internal/reviewou do mecanismo de classificação código/prosa foi tocada. - AC-001/002/003 do AUR-467 (exclusão de regra de código em prosa,
cobertura de código mantida, declaração de cobertura parcial) continuam
provados por
tests/acceptance/AUR-467.shetests/unit/AUR-467.godiretamente contraPromptBuilder.BuildPrompt— nenhum dos dois foi alterado por este card. - O E2E do AUR-467 nunca foi prova de AC: o próprio cabeçalho do script
documenta que é um controle de não regressão da tubulação
cmd/aurumcode -> internal/review -> internal/prompt, porque o double de LLM retorna sempre o mesmo JSON independente do prompt. Dar forma válida ao achado do fixture sob o contrato atual de evidência não muda o que esse controle mede.
Mutação cética
MUT-001 (tests/acceptance/AUR-540.sh AC-001-MUT-001): numa cópia staged,
remove de volta os três campos (evidence, impact, verification) do
fixture, reproduzindo exatamente a forma pré-correção. O achado volta a
não ter evidência, o gate volta a descartá-lo, e
tests/e2e/AUR-467.sh E2EAUR467 sai 1
(AUR-467/E2E/no-regression-gate-exit:0) de novo na cópia mutada —
confirmando que a correção, e não outra coisa, é o que torna AC-001 verde.
Validação
go test -count=1 ./...— verde, sem alteração de comportamento de produto.tests/e2e/AUR-467.sh E2EAUR467(viago-shared exec, Go só em container) — exit 0 (era 1).tests/acceptance/AUR-540.sh(viago-shared exec) —all,AC-001,AC-001-MUT-001exit 0; seletor desconhecido (bogus) exit 64../.board/bin/oci-run --profile bootstrap-readonly-v1 --card AUR-467— exit 0. Esteacceptrodatests/acceptance/AUR-467.shcom o seletor padrãoAC-001(nominal_case), nãoE2EAUR467; não prova o seletor E2E dentro do container selado, só que a propriedade original do AUR-467 continua de pé.oci-runnão aceita selecionarE2EAUR467diretamente (só--profile/--card)../.board/bin/oci-run --profile bootstrap-readonly-v1 --card AUR-540— exit 79 antes da correção do card, infraestrutura, ver "Gap medido" abaixo. Não era um RED comportamental.
Non-goals (herdados do card)
Nada em internal/prompt, internal/review ou na promessa do AUR-467 foi
alterado. O gate de escopo e evidência permanece exatamente como estava.
Gap medido (resolvido): oci-run --card AUR-540 saía 79
./.board/bin/oci-run --profile bootstrap-readonly-v1 --card AUR-540 sai
79 com AUR-540/all/infrastructure/missing-input:tests/fixtures/repos/git-demo/repo.git.
A materialização do oci-run só copia paths ∪ read_paths do frontmatter
do card (.board/bin/oci-run, allowed_roots=("${owned_paths[@]}"
"${read_paths[@]}")). Durante este card, o coordenador já corrigiu uma
lacuna de read_paths (faltava internal/config, do qual cmd/aurumcode
depende — confirmado com go list -deps ./cmd/aurumcode) e eu mergeei
esse fix (main -> este worktree) antes de rodar de novo. Mas o oci-run
ainda sai 79: tests/fixtures/repos/git-demo/repo.git -- exigido pelo
próprio required_inputs de tests/e2e/AUR-467.sh -- continua fora de
paths e read_paths do frontmatter de AUR-540. Isso está em
.board/cards, fora dos paths que este card pode escrever e fora do que
esta sessão foi instruída a tocar; precisa de outra correção de
read_paths igual à que o coordenador já fez para internal/config
(adicionar tests/fixtures/repos/git-demo).
./.board/bin/oci-run --profile bootstrap-readonly-v1 --card AUR-467 sai
0, mas isso NÃO prova o seletor E2E dentro do container selado: o
accept daquele card roda tests/acceptance/AUR-467.sh sem argumento, que
por sua vez usa o seletor padrão AC-001 (nominal_case), não
E2EAUR467; e oci-run não aceita escolher outro seletor (só
--profile/--card). Então nenhum dos dois oci-run hoje exercita
E2EAUR467 dentro do container selado com os limites reais do perfil
(256 MB, 120 s, 1 CPU, rootfs somente leitura, uid 65534).
Com isso, o AC-001 do AUR-540 está provado fora do oci-run — rodando
tests/e2e/AUR-467.sh E2EAUR467 e tests/acceptance/AUR-540.sh all com Go
só em container (go-shared exec, nunca no host), nunca no host — mas o
accept declarado no card (oci-run --card AUR-540) continua vermelho por
infraestrutura. Isso é um bloqueador para done, não um gap aceito: falta
uma segunda correção de read_paths que esta sessão não pode aplicar.
Resolução (integração): o card passou a declarar tests/fixtures/repos/git-demo em read_paths. Com isso, ./.board/bin/oci-run --profile bootstrap-readonly-v1 --card AUR-540 sai com 0 (AUR-540/all/pass) no SHA integrado ao main, e o aceite do AUR-467 continua com 0.