Skip to content

feat: File bundling — parallel per-bundle review for large changesets #115

Description

@ajianaz

Problem

Cora v0.2.0 sends the entire diff as a single LLM call. This creates two problems:

  1. Token limit — Large PRs (>50K chars) exceed max_diff_size and fail entirely. We worked around this with --max-diff-size (feat: v0.2 batch — 6 issues (#106 #79 #85 #80 #74 #108) #112), but sending 200K chars to an LLM degrades quality — the model can't focus on everything equally.
  2. Quality degradation — Even when the diff fits, a monolithic prompt means the LLM splits attention across all files. Files reviewed last in the prompt get less attention than files reviewed first (positional bias).

Alibaba's Open Code Review solves this with "Smart file bundling" — grouping related files into isolated review units, each reviewed by a separate sub-agent with its own context window.

Proposed Solution: Deterministic File Bundling

How It Works

Input: 50 changed files in a PR

Step 1: File Selection (deterministic, zero tokens)
──────────────────────────────────────────────────
  - Filter: apply ignore patterns, binary files, generated files
  - Keep: only code files that need review

Step 2: Grouping (deterministic, zero tokens)
───────────────────────────────────────────────────
  Bundle 1: { src/auth/login.rs, src/auth/validate.rs, src/auth/types.rs }
    → Grouped by module path prefix (src/auth/)

  Bundle 2: { src/api/handler.rs, src/api/middleware.rs, src/api/routes.rs }
    → Grouped by module path prefix (src/api/)

  Bundle 3: { migrations/001_create_users.sql }
    → Standalone (no related files)

  Bundle 4: { tests/auth_test.rs, tests/api_test.rs }
    → Test files grouped together

  Bundle 5: { Cargo.toml, Cargo.lock }
    → Config files grouped together

Step 3: Parallel Review (N concurrent LLM calls)
───────────────────────────────────────────────────
  tokio::join!(
    review_bundle(bundle_1),
    review_bundle(bundle_2),
    review_bundle(bundle_3),
    review_bundle(bundle_4),
    review_bundle(bundle_5),
  )

Step 4: Merge Results
─────────────────────
  All bundle results → deduplicate → single output
  Format same as current: JSON array of issues, SARIF, terminal

Bundling Strategies

Three strategies, applied in order:

Strategy Rule Example
Module path Files sharing the same directory prefix (depth 2+) src/auth/*.rs → one bundle
Extension pairs .properties language pairs (i18n) messages_en.properties + messages_zh.properties
Test mapping {file} + tests/{file}_test.rs or __tests__/{file}.test.ts handler.rs + handler_test.rs
Standalone Any file not matching above rules Cargo.toml, README.md

Concurrency Model

// Configurable concurrency limit (default: 3)
// Prevents API rate limiting and cost spikes
const MAX_CONCURRENT_REVIEWS: usize = 3;

// Use tokio::sync::Semaphore for bounded parallelism
let semaphore = Arc::new(Semaphore::new(MAX_CONCURRENT_REVIEWS));

let handles: Vec<_> = bundles
    .into_iter()
    .map(|bundle| {
        let sem = semaphore.clone();
        tokio::spawn(async move {
            let _permit = sem.acquire().await;
            review_bundle(bundle).await
        })
    })
    .collect();

Token Cost Analysis

Scenario Current (single call) Bundled (parallel) Speed
Small PR (5 files, 500 lines) ~8K tokens, 1 call ~8K tokens, 1-2 calls Same
Medium PR (15 files, 2000 lines) ~13K tokens, 1 call ~5K tokens × 4 calls 2x faster
Large PR (50 files, 5000 lines) ~25K tokens, 1 call ~5K tokens × 12 calls 4x faster
Very large PR (100+ files) FAILS or degraded ~4K tokens × 25 calls 8x faster

Key insight: Bundling doesn't reduce total tokens — it parallelizes them. Quality improves because each bundle gets the LLM's full attention. Speed improves proportionally to bundle count.

Interaction with Context Chain (#114)

When both features are enabled:

Each bundle gets its OWN context chain extraction.
Bundle { auth/*.rs } → extracts auth-related context
Bundle { api/*.rs }  → extracts api-related context

Context budget (3000 tokens) applies PER BUNDLE, not globally.
This means each bundle has focused, relevant context.

Configuration

# .cora.yaml
review:
  bundling:
    enabled: true           # default: true for PRs with 5+ files
    min_bundle_size: 2      # files below this count → standalone
    max_concurrency: 3      # max parallel LLM calls
    group_by: "module"      # "module" | "extension" | "flat"
    
  # Auto-disable bundling for small diffs (unnecessary overhead)
  # Bundling activates when file count >= this threshold
  bundle_threshold: 5

CLI Flags

cora review --base origin/develop --bundle          # enable (auto for 5+ files)
cora review --base origin/develop --no-bundle       # disable
cora review --base origin/develop --max-concurrency 5  # override concurrency

--progress Events (Enhanced)

{"type":"bundle_started","bundle_id":1,"files":["src/auth/login.rs","src/auth/validate.rs"],"token_estimate":5200}
{"type":"bundle_complete","bundle_id":1,"issues_found":3,"tokens_used":4800}
{"type":"bundle_started","bundle_id":2,"files":["src/api/handler.rs"],"token_estimate":3100}
{"type":"bundle_complete","bundle_id":2,"issues_found":1,"tokens_used":2900}
{"type":"merging_results","total_issues":4}
{"type":"complete","total_issues":4,"total_tokens":7700,"total_bundles":2}

Acceptance Criteria

  • Files grouped by module path with configurable depth
  • Related test files mapped and grouped with source files
  • Bounded concurrency via tokio::sync::Semaphore
  • Bundle results merged and deduplicated (same issue across bundles)
  • --progress emits per-bundle events
  • review.bundling.enabled: false disables feature (backward compat)
  • Small PRs (<5 files) skip bundling (no overhead)
  • Integration test: 20-file mock PR → 4-6 bundles, all reviewed
  • Cache key includes bundle boundaries

Risks

Risk Mitigation
Related files split across bundles Module grouping keeps related files together
Same issue reported in multiple bundles Deduplication by (file, line, severity) tuple
API rate limiting max_concurrency config + semaphore
Cost increase (more calls = more system prompt overhead) Negligible: 600 tokens × N bundles vs quality gain

References

  • Alibaba Open Code Review: "Smart file bundling" — groups related files into single review units
  • Cursor: file-level context bundling for focused reviews
  • CodeRabbit: per-file parallel review pattern---

Part of #147 (v0.4 Engine Pipeline) — Phase 2

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions