Pular para conteúdo

AUR-591: o Aurum revisa o Aurum por PR, com configuração mínima e skills de convenção

Medido antes (base b29ca936)

  • .aurumcode/config.yml só declarava review.language: pt-BR e ignore: tests/**: sem gate, sem scanners, sem deliberação, sem skills. .aurumcode/skills/ não existia.
  • .github/workflows/code-review.yml chama ./.github/workflows/review.yml (check obrigatório de main: review / Review pull request). Os secrets LLM_API_KEY/LLM_BASE_URL existem no repositório; a última execução (2026-10-02, PR do dependabot, sem secrets) falhou no passo "Check provider configuration" sem publicar aurumcode/policy-gate com o motivo.
  • Os cards eram integrados por merge local e push direto em main.
  • Medido com o binário da base (container go-shared): uma skill em .aurumcode/skills/<nome>/SKILL.md chega ao prompt, mas um achado que a cita (tamanho#... ou SKILL#...) é descartado (1 citing an unknown rule_id). Regras citáveis vêm só de review.context.skills, com id <nome-do-arquivo>#<slug>; listado, o SKILL.md gera SKILL#<slug> e o achado reprova o gate (rc 3).
  • Medido na imagem do produto construída deste worktree (aurumcode-review:aur591, a mesma receita do review.yml), primeiro num repositório de um arquivo e depois sobre a árvore real (clone do worktree, fixture de modelo sem achados, review --base):
  • govet ligado: lint_unavailable em toda revisão (a imagem não traz go) → inconclusivo → bloqueio.
  • semgrep (árvore inteira, p/security-audit + p/owasp-top-ten): sast_invalid_output, porque o relatório traz 13 erros de parse nível warn (scripts bash, action.yml, locks YAML), e 9 achados ERROR em arquivos que a PR não tocou (Dockerfiles, demo/bad-code.go, secrets: inherit do code-review.yml). Ligado, bloquearia toda PR.
  • gitleaks (intervalo da PR): sem achados nem inconclusivo.
  • Num intervalo que toca docs/assets/capturas/capturas.json: achados analysis/hardcoded-secret (as capturas reproduzem os canários dos tutoriais) → docs/assets/capturas/** entrou no ignore.
  • Cobertura parcial: um arquivo oculto por ignore (ex.: tests/acceptance/AUR-591.sh) ou um binário (PNG das capturas, mesmo sob caminho ignorado) conta como cobertura parcial (partial_coverage), que com inconclusive: block reprova. O gate imprime só o motivo de maior rank: na primeira medição ele estava escondido atrás do sast_*.

O que mudou

  • .aurumcode/config.yml (20 linhas úteis, comentado, depois do merge do AUR-593): review.language, ignore com tests/** e docs/assets/capturas/**, gate: {fail_on: [error], inconclusive: block}, quality_gates.scanners com gitleaks (required: true), semgrep e govet (fail_on: warning), e deliberation ligada com max_rounds: 3, max_cost_tokens: 60000, per_tool_timeout_seconds: 120. Nenhuma skill listada em review.context.skills: as de diretório já são citáveis.
  • .aurumcode/skills/<nome>/SKILL.md (formato do AUR-565, paths entre aspas), uma por convenção, cada regra com id próprio, exemplo de violação e de correção:
  • arquitetura: ARQ-001 lógica em internal/, cmd/ só monta (warning).
  • tamanho: TAM-001 função ≤150 linhas, TAM-002 arquivo ≤400 (error).
  • sem-numero-de-card: CARD-001 sem id de card em código/doc de pacote (warning).
  • repo-publico: PUB-001 sem empresa/domínio interno/repo privado, PUB-002 sem segredo (error).
  • sem-bool-exportado: BOOL-001 sem parâmetro bool em função exportada (error).
  • gate-fail-closed: GATE-001 erro/ausência/dúvida nunca aprova (error). Ids citáveis <dir>#<slug>: arquitetura#arq-001-logica-fica-em-internal-cmd-so-monta, tamanho#tam-001-funcao-com-no-maximo-150-linhas, etc. Cada skill só entra no prompt quando um arquivo alterado casa com o seu paths. As regras subjetivas (ARQ-001, CARD-001) ficam em warning (publicam sem reprovar); as objetivas têm severity: error como piso.
  • .github/workflows/code-review.yml: comentário explicando que, num pull_request, job.workflow_sha do review.yml local é o commit de merge da PR (o binário que revisa é o da PR); novo job paralelo provider (sem needs, porque check pulado conta como aprovado) que, sem LLM_API_KEY/LLM_BASE_URL, publica aurumcode/policy-gate = failure com o motivo e sai 1; com os secrets, sai 0 sem publicar nada (quem decide o status é o review). review.yml não mudou.
  • .board/README.md, .board/AGENT_PLAYBOOK.md, CLAUDE.md: integração por PR (card → branch → PR → CI + self review → merge só com tudo verde); push direto em main só para commits que tocam apenas .board/.
  • docs/getting-started.md: a seção "Configuração mínima" é a do AUR-593 (conflito do merge resolvido com o texto do main), mais uma linha apontando a configuração do próprio AurumCode.
  • tests/acceptance/AUR-591.sh: seletores all, AC-003, MUT-001, MUT-002.

Depois do AUR-593 (merge de main em card/AUR-591)

O AUR-593 tirou ignorados e binários da conta de cobertura, limitou Semgrep e go vet às linhas adicionadas, tornou as skills de diretório citáveis como <dir>#<slug> e pôs Go na imagem de review. As lacunas registradas acima (cobertura parcial por ignore, Semgrep na árvore inteira, govet sem go, skill de diretório não citável) foram medidas antes dele e estão resolvidas; semgrep e govet voltaram ligados e a listagem em review.context.skills saiu.

Medição do intervalo deste card (review --base origin/main, merge-base 6d722898) na imagem do produto construída do worktree (aurumcode-review:aur591), com a configuração nova, fixture de modelo sem achados e, como no review.yml, os módulos baixados antes com go mod download para o cache montado em /go/pkg/mod:

rc=0
aurumcode review: deliberation: o provedor não chama ferramentas; os scanners opcionais rodam sem consulta ao modelo
- 1 arquivo(s) ignorados (configuração `ignore` do repositório ou binários): ficam fora da conta de cobertura ...
  - tests/acceptance/AUR-591.sh
No issues found.

Sem inconclusivo de cobertura nem de scanner. Sem o cache de módulos (sem o passo de prefetch), o govet sai lint_execution_error e bloqueia: é o fail-closed esperado, e o review.yml faz o prefetch.

Pendências

  • Binário de main como referência (Outcome item 4): não feito. Um segundo chamador publicaria um segundo aurumcode/policy-gate no mesmo contexto, e o último a terminar venceria.
  • O teto max_cost_tokens: 60000 não foi conferido contra o uso real de tokens (o provedor de fixture não chama ferramentas); conferir na auditoria da PR do AC-001.
  • A PR deste card é revisada com a configuração e as skills de main (base); o novo code-review.yml vale já (workflow vem do merge ref). Por isso a AC-002 exige uma segunda PR, aberta depois do merge.

Prova (binário do worktree)

O perfil selado não tem git; o aceite escreve o repositório de teste como objetos soltos com um auxiliar Go só de biblioteca padrão (mkrepo, gerado pelo próprio script) e o produto lê os objetos sem a CLI do git. Executado em go-shared e num container --network=none sem git (aurum-bootstrap-go-bash:1.27.1-2026-10-06, imagem candidata do AUR-590):

$ bash tests/acceptance/AUR-591.sh all
useful lines: 20
real rc=1          # config real: Parse estrito ok; gitleaks/semgrep ausentes no perfil -> inconclusivo, bloqueia
typo rc=1          # fail_on -> fial_on: "field fial_on not found", nenhum prompt
skills rc=3        # skills selecionadas por paths no prompt; achado cita a skill e reprova
longa.go:4: [warning] Funcao Longa tem 160 linhas. (rule tamanho#tam-001-funcao-com-no-maximo-150-linhas: TAM-001 Funcao com no maximo 150 linhas)
mutation rc=0      # MUT-001: sem tamanho/SKILL.md o mesmo achado some
aurumcode review: 1 finding(s) discarded: 1 citing an unknown rule_id (tamanho#tam-001-funcao-com-no-maximo-150-linhas)
api --method POST repos/example/repo/statuses/0123...4567 -f state=failure -f context=aurumcode/policy-gate -f description=self review nao executado: secrets LLM_API_KEY e LLM_BASE_URL ausentes
mutated guard exit=0 caught   # MUT-002: guarda com `exit 0` no lugar de `exit 1` é pega
AUR-591/all/pass
  • AC-003: AUR-591.sh AC-003 (≤30 linhas úteis, chaves declaradas, Parse estrito real e com erro de digitação, skills no prompt, gate cita a skill). Sobre a árvore real, na imagem do produto: ver "Depois do AUR-593" (rc 0).
  • MUT-001: o fixture do modelo é fixo; o que muda é só a presença do arquivo da skill. Sem ele o achado é descartado como regra desconhecida e o check fica verde (rc 0): o achado depende da skill, não de regra fixa.
  • oci-run --profile go-unit-offline-v1 --card AUR-591: inconclusivo nesta máquina (pinned profile image is not present locally: aurum-bootstrap-go-bash@sha256:3aa0ce99...) na primeira rodada; repetido depois do merge do AUR-590 com a imagem fixada b678437d...: exit_code: 0, stdout AUR-591/all/pass.
  • MUT-002: a guarda é extraída de code-review.yml entre os marcadores provider-guard-begin/end e executada com um gh falso que registra os argumentos.

Roteiro do coordenador (AC-001, AC-002, AC-004)

Pré-requisito: secrets LLM_API_KEY, LLM_BASE_URL e variável LLM_MODEL presentes (gh secret list; gh variable list).

AC-001 e AC-004 (PR deste card):

cd /home/paape/repos/AurumCode
git push -u origin card/AUR-591
gh pr create --base main --head card/AUR-591 \
  --title "AUR-591: o Aurum revisa o Aurum por PR" \
  --body "Card AUR-591. Spec: docs/specs/AUR-591.md"
N=$(gh pr view card/AUR-591 --json number --jq .number)
gh pr checks "$N" --watch
HEAD=$(gh pr view "$N" --json headRefOid --jq .headRefOid)
gh api "repos/Mpaape/AurumCode/commits/$HEAD/statuses" \
  --jq '.[] | select(.context=="aurumcode/policy-gate") | [.state,.description] | @tsv'
gh pr view "$N" --json reviews,comments --jq '[.reviews[].state, (.comments|length)]'
# o binário revisor é o da PR: o SHA do "Checkout AurumCode" é o merge commit
gh api "repos/Mpaape/AurumCode/pulls/$N" --jq .merge_commit_sha
RUN=$(gh run list --workflow code-review.yml --branch card/AUR-591 -L1 --json databaseId --jq '.[0].databaseId')
gh run view "$RUN" --log | grep -A3 'Checkout AurumCode' | grep -E 'ref|sha' | sed -n '1,5p'
# merge só com tudo verde; --merge preserva os SHAs do Delivery record
gh pr merge "$N" --merge --delete-branch
git fetch origin && git merge-base --is-ancestor <sha-do-delivery> origin/main && echo ancestor

Registre na spec/card: URL da PR, estado do review, linha do aurumcode/policy-gate, e o merge_commit_sha comparado ao SHA do checkout.

AC-002 (depois do merge, com a configuração nova já na base):

cd /home/paape/repos/AurumCode
git fetch origin && git switch -c teste/convencao-funcao-longa origin/main
mkdir -p internal/sondaconvencao
{ printf 'package sondaconvencao\n\n// Longa soma uma unidade por passo.\nfunc Longa() int {\n\ttotal := 0\n'
  for i in $(seq 155); do printf '\ttotal++\n'; done
  printf '\treturn total\n}\n'; } > internal/sondaconvencao/longa.go
git add internal/sondaconvencao/longa.go
git commit -m "teste: funcao de 160 linhas para provar a convencao de tamanho"
git push -u origin teste/convencao-funcao-longa
gh pr create --base main --head teste/convencao-funcao-longa \
  --title "teste de convencao (nao integrar)" --body "Prova da AC-002 do AUR-591; sera fechada sem merge."
M=$(gh pr view teste/convencao-funcao-longa --json number --jq .number)
gh pr checks "$M" --watch   # esperado: review / Review pull request = fail
gh pr view "$M" --json reviews --jq '.reviews[].body' | grep -o 'tamanho#tam-001-funcao-com-no-maximo-150-linhas' | sed -n '1p'
HEAD=$(gh pr view "$M" --json headRefOid --jq .headRefOid)
gh api "repos/Mpaape/AurumCode/commits/$HEAD/statuses" \
  --jq '.[] | select(.context=="aurumcode/policy-gate") | [.state,.description] | @tsv'
gh pr close "$M" --delete-branch
git switch main && git branch -D teste/convencao-funcao-longa

Se o modelo não citar a skill, a AC-002 não está provada (não reabra com outro texto até passar: registre o parecer recebido). MUT-001 real, opcional: na mesma branch de teste, um commit que apaga .aurumcode/skills/tamanho/SKILL.md não muda o parecer (a configuração vem da base); por isso o MUT-001 é provado localmente no aceite.

Correções vindas da PR #87

  • CI Build and test in OCI vermelho (TestAUR548*, TestAUR567GateLine…, TestAUR572Conclusive…, TestAUR575SAST…: exit 1, "LLM quality review did not run"): reproduzido na imagem golang:1.27.1-alpine3.24 do CI só com bash; com bash git o mesmo go test passa. Causa: desde o AUR-593 os scanners mapeiam as linhas adicionadas com git diff, e sem git o scan é inconclusivo. Os comandos do job passaram para scripts/ci/build-test.sh (POSIX sh, instala bash git quando faltam) e scripts/ci/race.sh (imagem bookworm, que já traz git), chamados por sh/bash scripts/ci/... sem sh -c longo. O script completo passou na imagem do CI.
  • Auditoria e SARIF não gravados (audit_write_failed/sarif_write_failed: permission denied): reproduzido localmente. O container roda como root, mas o arquivo montado ficava no /tmp do container (sticky e gravável por todos) e era de outro uid (o do runner); com fs.protected_regular o open(O_CREAT) falha com EACCES mesmo para root. review.yml agora monta o diretório ${RUNNER_TEMP}/aurumcode-out em /aurumcode-out (fora de /tmp), grava auditoria, SARIF e saída do changelog ali, devolve os arquivos ao uid do runner com um chown no fim e mantém o código de saída do review. Na PR, a autorrevisão passou a gravar os dois artefatos.
  • "go mod download failed": não houve falha. A linha aparece no log só porque o GitHub imprime o script do passo antes de executá-lo; nenhum ##[warning] foi emitido, e o govet concluiu (a revisão chegou a um achado, não a lint_* inconclusivo).
  • Achado SCR-001 em ci.yml:52 (sh -c com sequência longa como command injection): a string não interpola nenhuma entrada externa (nem ${{ github.event.* }}, nem variável do PR); é texto fixo do workflow. Como insumo de realimentação da regra: um sh -c com literal fixo, sem expressão do GitHub nem variável controlada pelo PR, não é injeção. O refactor foi feito mesmo assim (scripts versionados). A linha antiga sh -c 'npm install ... node tests/docs/mkdocs.test.cjs' do job de documentação não foi alterada por esta PR e ficou: o aceite do AUR-588 exige o texto node tests/docs/mkdocs.test.cjs dentro do ci.yml.
  • scripts/ci/ está fora dos paths do card; criado por pedido do coordenador.
  • Pré-existente, não corrigido: tests/acceptance/AUR-496.sh AC-003 já falha em main (runs-pr-code) por causa do passo de prefetch de módulos do AUR-593.