AUR-594: PR grande revisada (diff local quando a API recusa, lotes quando passa do orçamento)
Medido antes (base e81f63c5)
- PR #88 (intervalo
9038e70e...a7f2b0a6,origin/card/AUR-589): 117 arquivos, 1565 inserções e 30562 remoções. A API do GitHub recusa o diff acima de 20000 linhas comHTTP 406e o códigotoo_large;githubclient.GetPullRequestDiffdevolviaHTTP 406: ...como erro comum efetchPullRequestsaía 1 (fetching pull request diff: HTTP 406 ... too_large): nada revisado. - AUR-593 (
996732f0..8c7fcdc8, 111 arquivos,--base): 6 arquivos ficaram fora do prompt pelo limite de tokens (prompt único de 120000) e o gate ficoupartial_coverage.
O que mudou
- Recusa tipada (
internal/git/githubclient/diff_too_large.go,client.go): só406com"too_large"no corpo viraErrDiffTooLarge; outro406continua erro comum. Nenhum dos dois devolve diff.ParseUnifiedDiffexpõe o mesmo parser do diff da API;PullRequestMetadata.BaseSHAlêbase.sha. - Diff local (
internal/analyzer/rangediff.go,cmd/aurumcode/pr_local_diff.go,review_pr_inputs.go): na recusa, o checkout é verificado antes de qualquer leitura (repositório e head do PR,codebaseContextMismatch; árvore limpa,verifiedCleanCheckoutReason). O head é oHEADverificado; a base éAURUMCODE_BASE_SHAquando é um commit do checkout, senãobase.shada API.Repo.RangeDiffrodagit diff base...head(base de merge, janelas de contexto como a API,--no-ext-diff --no-textconv,core.fsmonitor=false) e o texto passa porParseUnifiedDiff: mesmos caminhos, lados e hunks do diff da API; binário sai sem hunk e segue a classificação de sempre do--pr. Semgit(internal/analyzer/mergebase.go,windowed.go): a base de merge é lida do banco de objetos (os ancestrais comuns que não são ancestrais de outro comum; zero ou mais de um, como num histórico cruzado, é erro, nunca um palpite) e os hunks são montados com as mesmas janelas de 3 linhas;TestRangeDiffFromObjectsMatchesGitprova arquivo a arquivo e hunk a hunk que o resultado é o dogit diff base...head, inclusive com a base andando depois do ponto de ramificação. Base ausente ou checkout não verificado: saída 1, nada enviado nem publicado. - Parser de hunk (
githubclient.parseHunkHeader): o cabeçalho de um arquivo de uma linha apagado (@@ -1 +0,0 @@, que a API e ogitemitem) derrubava o processo (slice bounds out of range); agora um lado sem contagem vale uma linha e um cabeçalho malformado dá zeros (TestParseHunkHeaderWithoutCount). - Diff local sempre? Avaliado e recusado: a CLI direta e os testes com GitHub falso não têm checkout verificado, então "sempre local" trocaria uma revisão que hoje funciona por uma falha; e com a API respondendo, o diff dela é a fonte que o PR mostra. O local é só o caminho da recusa por tamanho.
- Lotes (
internal/review/batches.go,batch_merge.go,reviewer.go):GenerateReviewWithContextmonta o prompt único como antes; só quando ele deixaria um arquivo com patch omitido ou parcial (arquivo sem patch não conta) a revisão planeja lotes: arquivos agrupados por diretório, diretório inteiro num lote quando cabe, senão o lote é completado arquivo a arquivo; arquivo grande demais sozinho é um lote próprio (parcial, declarado). Cada lote passa pelas mesmas quatro etapas (reviewPrepared) com a evidência dos seus arquivos (ids atribuídos uma vez, os mesmos em todos os lotes). Os resultados são consolidados: listas concatenadas, contagens somadas, avisos juntados, flag ligada continua ligada, veredito mais severo; um gate. Transcrições de deliberação são somadas. Um lote que falha faz a revisão falhar. O prompt único que cabe fica byte a byte igual. - Tetos (
internal/prompt/templates/limits.yml:batch_max_count: 4,batch_max_prompt_tokens: 480000;internal/config/batches.go: seçãobatchescommax_batchesemax_prompt_tokens, validada, linha emgovernedSections, política decide sozinha). A partir do primeiro lote que passa de um teto, nenhum lote é revisado: os arquivos vão paracode_files_omittedecode_files_omitted_paths, o aviso de cobertura os lista (passes.go,review_coverage.go), a auditoria os põe emcoverage.omitted_files, e a cobertura parcial retém a aprovação. - Auditoria (
internal/render/audit.go,cmd/aurumcode/review_batches.go,compliance_artifacts.go,review_evidence.go): campobatches(índice, arquivos, tokens estimados); stderrreviewed in N batches by directory (files per batch: ...). - Tutorial
revisao, casopr-workflow: GitHub falso (_lib/github-falso.pyaceita406eBASE;_lib/pr.shganhatut_pr_servidor_recusa) com três passos: 406 + diff local + 3 lotes (exit 0, APPROVE); tetobatches.max_batches: 1(exit 1,partial_coverage, 4 arquivos listados); checkout sujo (exit 1, nada publicado). O caso ficou dentro depr-workflow(um caso novo exigiria registro emdocs/specs/AUR-561.md, fora depaths). - Docs:
configuration.md(seção "PR grande" e tabela),getting-started.md,architecture.md,tutorials/revisao.md.
Prova
Aceite (tests/acceptance/AUR-594.sh, go-shared)
| Item | Teste | Última linha |
|---|---|---|
| AC-001 | checkout montado sem git (objetos escritos em Go): TestTooLargeDiffIsReadFromObjectCheckout (o prompt carrega o arquivo novo e +\treturn 2) e TestTooLargeDiffWithUnverifiedObjectCheckoutFails (arquivo não versionado: exit 1, nada enviado nem publicado); no selado (sem git) elas exercem o leitor do banco de objetos; com git (go-shared) também TestTooLargeDiffIsReadFromTheVerifiedCheckout, TestTooLargeDiffWithUnverifiedCheckoutFails (repositório real) e TestRangeDiffFromObjectsMatchesGit (paridade dos dois leitores); TestDiffRefusedAsTooLargeIsTyped, TestParseHunkHeaderWithoutCount |
AUR-594/AC-001/pass (git and object database); no selado, (object database; no git binary here) |
| AC-002 | TestDiffAboveTheBudgetIsReviewedInBatches (6 arquivos grandes em 3 diretórios: reviewed in 2 batches (4, 2), exit 0 sob block, auditoria com batches, "complete": true, "decision": "pass") |
AUR-594/AC-002/pass |
| AC-003 | TestBatchCeilingLeavesFilesOutAndWithholdsApproval (max_batches: 1 e max_prompt_tokens: 120000: exit 1, partial_coverage, c/one.go e c/two.go listados, auditoria "complete": false) |
AUR-594/AC-003/pass |
| AC-004 | caso do tutorial gravado e esta spec | AUR-594/AC-004/pass |
| MUT-001 | 406 vira diff vazio (return &Diff{}, nil): TestTooLargeDiffIsReadFromObjectCheckout vermelho |
AUR-594/MUT-001/rejected |
| MUT-002 | declareOutside não declara nada: exit=0, want 1 with partial_coverage and no approval |
AUR-594/MUT-002/rejected |
| MUT-003 | zero lotes admitidos consolidando um resultado vazio (if len(plan.batches) < 0): TestNoBatchAdmittedIsNeverApproved vermelho |
AUR-594/MUT-003/rejected |
Testes de unidade: TestPackUnitsKeepsDirectoriesAndFillsBatches,
TestMergeResultsConsolidatesBatches, TestFilesWithoutPatchRideInTheFirstBatch.
AC-004 na imagem do produto: a PR #88 deste repositório
Imagem aurum-tutoriais:aur594 construída deste worktree. Clone do
repositório com HEAD em a7f2b0a6 (head da PR #88), origin =
https://github.com/Mpaape/AurumCode.git; GitHub falso local
(github-falso.py PORTA 406 LOG a7f2b0a6... 9038e70e...) recusando o diff;
modelo de fixture com aprovação limpa; review --pr 88 --repo
Mpaape/AurumCode --publicar --modo-publicacao review --politica /policy
--auditoria ....
Com a configuração do repositório como política (ignore: tests/**,
docs/assets/capturas/**, gate fail_on: [error], inconclusive: block):
aurumcode review: the API refused the pull request diff as too large; reviewing the same range computed from the verified checkout (9038e70e6d38...a7f2b0a63e4e, 117 file(s))
review formal "APPROVE" publicado no pull request #88 (0 comentário(s) na linha).
rc=0
audit: "gate": {"decision": "pass"}, "coverage": {"complete": true, "omitted_files": [113 ignorados]}
Sem ignore (os 117 arquivos, 30 mil linhas removidas, só o gate):
aurumcode review: the API refused the pull request diff as too large; reviewing the same range computed from the verified checkout (9038e70e6d38...a7f2b0a63e4e, 117 file(s))
aurumcode review: the diff did not fit one prompt; reviewed in 4 batches by directory (files per batch: 27, 55, 24, 11)
review formal "APPROVE" publicado no pull request #88 (0 comentário(s) na linha).
rc=0
audit: {"decision": "pass"}, "complete": true, omitted 0
batches: (1, 27 arquivos, 117599 tokens) (2, 55, 119858) (3, 24, 107352) (4, 11, 70482)
Medido no caminho: com o padrão de 400000 tokens o quarto lote passava do
teto e 11 arquivos ficavam fora (rc 1, partial_coverage, listados); o padrão
virou 480000 (quatro prompts cheios), e o número de lotes é o teto que manda.
Antes disso o empacotamento fechava o lote a cada diretório que não cabia
(lotes de 5, 22, 43 e 29 arquivos, o primeiro com 14930 tokens); agora o lote
é completado arquivo a arquivo.
O intervalo do AUR-593 (996732f0..8c7fcdc8)
--base 996732f0 na imagem, mesma política:
aurumcode review: the diff did not fit one prompt; reviewed in 2 batches by directory (files per batch: 72, 36)
Cobertura da revisão — 99 de 111 arquivo(s) do diff foram cobertos; 4 não foram revisados por completo.
- 4 arquivo(s) foram filtrados antes da revisão (gerado, grande demais, sem patch ou binário fora do catálogo de formatos)
rc=1
Nenhum arquivo fora pelo orçamento (eram 6); o parcial que resta são os 4
gerado.go dos tutoriais, gerados de propósito (legítimo). No --pr com o
diff local o mesmo intervalo cabe num prompt (janelas de contexto, não o
arquivo inteiro do --base): sem lotes, parcial só pelos gerado.go e
pelas PNG apagadas (sem patch). Medido também: antes de tirar os arquivos sem
patch da decisão, cada PNG apagada ocupava um lote próprio.
Regressão
go vet ./... && go test -p 1 ./...(go-shared): rc 0, nenhum pacote falhou.- Aceites (go-shared), última linha:
AUR-594/all/pass,AUR-576/all/pass(depois de tirar ot.Setenv("PATH")dos testes deste card: a guarda do AUR-576 conta essas chamadas),AUR-578/all/pass,AUR-593/all/pass,AUR-575/all/pass,AUR-588/all/pass; AUR-561, 562, 563 e 564 rc 0 (AC-002-MUT-001/ok (sem a linha que prova o caso, --check diverge em todos os casos)). O AUR-561 copia só_lib/tutorial.shpara as cópias da mutação: orun.shda revisão carrega_lib/pr.shsó dentro do caso do PR grande. - Tutoriais: os 18
run.sh allem paralelo comaurum-tutoriais:aur594(todos rc 0) e--checkde cada um: 18CHECK OK. Os commits do PR grande do tutorial têm data fixa, então os SHAs da saída são estáveis. bash scripts/docs/capturas.shrc 0 ebash scripts/docs/build.shcom 0 warnings.- Selado:
oci-run --profile go-unit-offline-v1 --card AUR-594,exit_code: 0(semgitno perfil: o AC-001 exerce o leitor do banco de objetos). - Teto e cache:
TestBatchCeilingLeavesFilesOutAndWithholdsApprovalroda a revisão duas vezes no mesmo diretório de cache: a revisão parcial não é gravada no cache por arquivo, e a segunda continua exit 1 com os mesmos arquivos fora. - CI real: o review publicado na PR #91 (AUR-595) não traz o aviso
"Contexto do repositório omitido", isto é, o checkout do workflow
(
.aurumcode-target,working-directoryda revisão) passa na verificação de identidade e limpeza; é o mesmo pré-requisito do diff local.
Decisões e pendências
- O caso do PR grande vive dentro de
pr-workflowdo tutorialrevisao: um caso novo exigiria registro emdocs/specs/AUR-561.md, fora depaths. runProfilePasses(comreview.profiles) monta revisores com os tetos padrão: a seçãobatchesnão chega a eles.-
Revisão (REQUEST_CHANGES, corrigido): com
max_prompt_tokensmenor que um prompt nenhum lote era planejado, o modelo nunca era chamado emergeResults([])dava um resultado vazio queformalReviewEventlê comoAPPROVE(exit 0 sem gate). Agora zero lotes éreview.ErrNoBatchReviewed, que embrulhallm.ErrAllProvidersFailed: o mesmo caminho de uma revisão de modelo que não rodou (--base: saída 1;--prsem gate: saída 1; com gate:quality_degraded, veredito nunca approve, gate inconclusivo).TestNoBatchAdmittedIsNeverApproved(max_prompt_tokens: 1, sem gate): exit 1 e sem "Approve"; MUT-003 (tirar o teste de zero lotes) o deixa vermelho (AUR-594/MUT-003/rejected).canonicalVerdictnão mudou: ele deriva do resultado (achados,quality_degraded, gate retido), não doVerdictdo modelo, e um resultado limpo semVerdicté a aprovação legítima de toda revisão sem achados (os tutoriais e fixtures dependem disso); o conserto é nunca produzir um resultado de modelo sem modelo. -
O aviso de cobertura dos arquivos fora pelo teto reaproveita a frase "left out of the review by the token budget" (o teto de lotes é orçamento de tokens); a lista nomeia cada arquivo.
- Teto global de custo:
batches.max_prompt_tokensjá é a soma dos prompts de todos os lotes, e--limiteusa um único rastreador de custo para todas as chamadas da revisão (o orquestrador é um só). Pendente: sódeliberation.max_cost_tokensvale por lote (uma sessão de deliberação por lote); uma revisão em quatro lotes pode gastar até quatro vezes esse teto. - O veredito reaproveitado (
verdict-reuse) não inclui os tetos de lotes na chave: uma revisão completa é a mesma com qualquer teto, e uma parcial não é reaproveitada.