Repository navigation
ci: review oversized PR diffs in complete file-boundary batches - #237
Conversation
A filtered diff over one review call's 6,000-line/128 KiB limit was rejected before any model call, which left large integration PRs unmergeable under the required AI review. review_batches.py now splits such a diff at file boundaries into at most 8 batches (each within 7/8 of the call limits), admits every batch with the existing input_scope checks, and the panel and chair steps run one review per batch in parallel. Each batch keeps its own GITHUB_ENV flags and a failure flag from any batch stays blocking; the combined review passes only if every batch passes. Diffs within the limit keep the single-review path unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Address the automated security review of the batching change: workflow, review-script, Terraform/IAM, edge and auth diffs are always packed together into batch 1 (more than one batch of them is rejected before any model call), the cross-batch note marks file lists as untrusted data, and it never lowers the severity of a security finding. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Address the follow-up security review: sensitivity now inspects the diff --git, rename/copy and ---/+++ lines (old and new ends) instead of a display label, and C-quoted, escaped, non-UTF-8 or missing headers count as sensitive so they are always reviewed in batch 1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
🤖 AI Code Review (two independent reviewers)_Reviewer responses: codex/ALL claude/ALL _ Status: PASSED — No blocking issues found Both panels responded and the key claims are verified. Here is the synthesized review. PR #237 — ci: review oversized PR diffs in complete file-boundary batchesIMAGE_COVERAGE: NOT_REQUIRED (The manifest lists no images, no unavailable entries and no omissions; both panel reports also declared NOT_REQUIRED.) 1. SummaryThis PR replaces outright rejection of oversized filtered diffs with a fail-closed batched review: at most 8 file-boundary batches, security-sensitive files co-located in batch 1, two waves of at most 4 parallel batches each behind fresh one-hour AWS sessions, and a combined verdict that passes only when every batch passes. Both independent reviewers found no CRITICAL or MAJOR issues; I independently re-derived the budget arithmetic (BATCH_BYTES 102,400 + 12,288-byte header ≤ 128 KiB; 5,600 + 400 lines = 6,000; 928 KiB pre-read cap; 190-minute timeout with ~40 minutes of slack), traced the GitHub Actions 2. Issues per lensL2 — Code correctnessNo CRITICAL/MAJOR. Codex reported no findings; Claude reported only MINORs, which I verified against the diff and adopt:
I additionally confirmed the fail-closed paths Claude traced: a missing per-batch L3 — Security / AWS mutation safetyNo CRITICAL/MAJOR — both models agree. The patch adds no mutating AWS call, IAM policy or secret; the two new credential steps duplicate the existing
L4 — Observability / data-integration correctnessNo Steampipe/CIS/AgentCore logic is touched — both models agree; the lens reduces to the pipeline's own diagnostics. I confirmed the flag algebra: the four blocking flags OR across batches (a later batch cannot clear an earlier failure),
L5 — Docs/ADR consistencyNo CRITICAL/MAJOR — both models agree docs are consistent. Numeric claims in 3. Suggestions
4. VerdictNo CRITICAL or MAJOR findings from either independent reviewer survived verification; all adopted findings are MINOR hardening/documentation items. Image evidence is not required for this PR (no staged images). The batching design is fail-closed at every degraded path I traced. Triggered by commit |
- Sensitive grouping now covers agent/lambda, remediation and middleware paths plus source files named for IAM, policy, auth, guard, cross-account or remediation code (Markdown by prefix only), with the batch note and runbook stating exactly that set. - chair_failed is set only when every batch's chair failed, so the "not a code problem" badge cannot mask another batch's real finding. - Scrubbed per-batch chair reports are printed in log groups and the comment limit comes from review-limits.json (comment_bytes). - Remove the runbook and scripts context sentences that still said batching was not implemented; list the new suite in the documented test command. - Background panel/chair runs read /dev/null and helpers run with python3 -I. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Batched runs check out the PR HEAD as a separate read-only worktree (core.symlinks=false, no hooks or LFS smudge, symlinks fail the step) and expose it to Claude with --add-dir; each batch is told to verify cross-batch dependencies there and to review producers and consumers of every IAM, permission, auth, ownership, mutation or secret change. Models still run in BASE and the tree is removed after the job. - Batch 1 also groups agent/, web/app/api/ and worker dispatcher, handler, action-catalog and connector-target files, plus admin/connector/mutation- named sources; docs and CHANGELOG state the exact set. - chair_failed treats a batch without the key as a produced verdict, so one failed chair cannot relabel another batch's finding. - The batch's own file list precedes the other batches' list, log dumps neutralize workflow commands, and tests cover regroup completeness and the exported-flag allowlist. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Wrap each per-batch review log dump in a random ::stop-commands:: token, strip CRs and neutralize indented :: lines. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Symlink guard no longer runs a find|grep pipeline under pipefail (SIGPIPE could invert it); worktree registrations are pruned before and after. - Classifier also covers *dispatcher worker files and secret, credential, RBAC, redaction and cross-account names; headers without a parseable path count as sensitive; the note, code and runbook call the list a prioritization rather than a co-location guarantee. - combine() fails closed on an empty batch list; the chair passes --add-dir only for an existing tree; the log token uses /dev/urandom; a helper crash can no longer publish an empty responded value; batch work dirs and raw panel slots are removed after a batched run. - branch-strategy.md now says bounded batching exists; docs note Codex sandbox access and the HEAD-tree sanitization asymmetry. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The read-only PR HEAD worktree gave reviewers and the chair raw head content that bypasses the diff filter, admission bounds and scrubbing, and its setup/cleanup ordering could leave data or wedge later runs. Remove it: models again see only their batch's diff and the BASE checkout, cross-batch dependencies are reported as unverified, and docs state that joint review of split changes is not guaranteed (Merge Verify still builds and tests the whole PR). Batch work directories are now removed unconditionally. Also: combine() reads the last non-blank line like the chair validator, the chair step guards an empty batch list, the log dump fallback works without pipefail, comment_bytes is validated against GitHub's comment cap, the batch note scopes PR-wide image evidence, and the older gate tests rewrite every /tmp path. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Fix the older gate tests' double /tmp rewrite, which broke them wherever TMPDIR defaults to /tmp. - The trusted batch note no longer lists PR file names (they stay in the diff), and restates that the verdict/output rules still apply. - At most max_parallel_batches (4) batches run at once in the panel and chair steps, sized for the runner pod (2 CPU, 8 GiB request, 12 GiB limit); the job timeout covers two worst-case waves per phase. - Refresh stale admission messages and the workflow comment, use explicit UTF-8 in combine, and carry the batch-1 overflow rule into scripts/AGENTS.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The review role's sessions last at most one hour, so a two-wave phase could outlive its credentials. Each wave of up to four batches now runs in its own panel or chair step (scripts/pr-review/run-batches.sh) after a fresh configure-aws-credentials step; the gate takes the phase result from the last wave. Admission refuses a diff over 8 x 128 KiB before reading it, the line budget no longer applies redaction headroom, per-batch exit codes are recorded without errexit, and the job timeout covers two waves per phase. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Each batch diff now starts with a data header (stdin channel, never the trusted prompt) listing the sanitized paths changed in the other batches, so a dependency no listed path provides stays blocking instead of being reported as an unverified cross-batch dependency. - Batch 1 also groups scripts/v2/agentcore/ and frozen/skill/allowlist-named files; Markdown and unit tests no longer count as permission surfaces. - Batch budgets reserve the header, admission writes owner-only copies and bounds its read by real capacity, each wave logs its chair reports before checking exit codes, and cleanup also removes the batch index and gate scratch file. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The sensitivity name terms matched only the final file name, so directory- organized surfaces (secret-rotation/redeploy.py, eks/auto_register.py, incident/writeback.py) and web/lib/aws-assume.ts were not co-located. Terms now match any path segment at a path/-/_/. boundary and include assume, writeback and redeploy; explicit prefixes cover secret-rotation, eks, incident, worker entrypoints, SG-rule workers and web AWS clients. Remove the dead top-level edge-lambda/ prefix (terraform/ covers it). The paths header lists both rename/copy ends and reserves its lines in the packing budget. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The paths data header lists each file once from its ---/+++ and rename/copy lines; the ambiguous diff --git split candidates stay classification-only. - The header (fences and truncation marker included) stays within its 400-line and 12 KiB reservation. - max_parallel_batches is validated at import. - Runbook: state the real pre-read bound and drop a duplicated sentence. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Newer Docker reports a missing object as "no such object" instead of "No such container", which failed Merge Verify's codec sandbox test and would make sandbox cleanup report a spurious failure. Match "no such" case-insensitively. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A bare "no such" match would treat transport or storage failures ("no
such host", "no such file or directory") as successful cleanup of the
untrusted-image decoder. Match "no such container|image|object" only.
Also move the batch cleanup step after the HEAD image cleanup (keeps the
gate-reason test slice intact), give the job timeout ~10 minutes of slack,
and tighten limit/exception validation in review_batches.py.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When some batch chairs fail (model timeout/error) and others produce verdicts, chair_failed stays 0, so the comment used the CRITICAL/MAJOR badge for an infrastructure failure. Aggregation now also exports chair_failed_any, which selects a badge saying review generation failed for one or more batches and the workflow should be re-run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Summary
The required AI review rejects any filtered diff over 6,000 lines / 128 KiB before a model is called ("split the change or implement complete bounded review batches"). #227 now carries ~733 KB of review input (runtime refresh + #233 + #235) and cannot pass at all. This implements the bounded batches the gate message asks for.
scripts/pr-review/review_batches.pyadmit: a diff within one call's limit keeps the single review path unchanged (same work dir, diff file and checklists). A larger diff is split atdiff --gitboundaries into at most 8 batches, each within 7/8 of the byte/line limits (redaction headroom), and every batch is admitted with the existinginput_scope.admission()checks. One file over the per-batch budget, or more than 8 batches, is rejected before credentials/models. Stale batch dirs and single-pathreview.md/github.envare removed on each admission (self-hosted runner/tmppersists).run-panel.sh/synthesize.shper batch in parallel, so the existing timeout budget and one-hour credential sessions still hold. Each batch gets its ownGITHUB_ENVfile;review_batches.py envkeeps per-batch last-write semantics and ORs every failure flag across batches, so a later batch's=0can never clear an earlier batch's=1.combinewrites one section per batch andVERDICT: PASSonly if every batch's chair ended withVERDICT: PASS(missing/invalid verdict = FAIL). The gate prints diagnostics for every incomplete batch.Test plan
scripts/v2/test_pr_review_batches.py(13 tests): single-path unchanged, lossless ordered split, oversized-file / too-many-batches / omitted-source rejection, stale cleanup, cross-batch flag OR, per-batch last-write, combined verdict, comment fit, workflow wiring, multi-batch gate diagnostics.VERDICT: FAIL.test_pr_review_pipeline.py/test_pr_review_head_images.py: same failures as basedevlocally (codec sandbox needs Docker/Pillow here); one assertion updated for the new admission command.tests/structure/test-pr-review-*.shunchanged results (panel-promptfails on base too).Risk: a batched run starts up to 16 CLI processes at once on the runner pod and issues that many concurrent Bedrock calls.
🤖 Generated with Claude Code