Study guides / CCAR-F / Domain 4

Prompt Engineering & Structured Output · Lesson 6 of 6

4.6 - Multi-Instance and Multi-Pass Review

Design multi-instance and multi-pass review architectures

When Claude reviews its own output, it starts at a disadvantage: it still carries the reasoning it used to generate that output. The model remembers why it made each decision and is less likely to question it. That's not a bug. It's just how self-review works inside a single session. The job is to design around it.

The Self-Review Limitation

A model reviewing its own output in the same conversation session retains its original reasoning chain. It already "knows" why it chose each approach, classified each finding at a particular severity, or selected certain values. When asked to review, it tends to confirm rather than challenge those decisions.

An independent instance - a separate Claude invocation without the prior reasoning context - approaches the output fresh. It judges the code, findings, or extraction on what it sees alone, without the bias of "I chose this because..." That's what makes independent review so much better at catching subtle issues.

The exam tests this directly. When presented with options for improving review quality, the correct answer involves using a separate model instance, not adding "please review carefully" instructions to the same session or relying on extended thinking within the generating session.

// Anti-pattern: self-review in the same session
const generation = await client.messages.create({
  messages: [
    { role: "user", content: "Write a function to process orders" },
    { role: "assistant", content: generatedCode },
    { role: "user", content: "Now review your code for bugs" }
    // Model retains its reasoning - less likely to find its own mistakes
  ]
});

// Correct: independent review instance
const review = await client.messages.create({
  messages: [
    {
      role: "user",
      content: `Review this code for bugs, security issues, and edge cases:\n\n${generatedCode}`
    }
    // Fresh instance - no prior reasoning context
  ]
});

Multi-Pass Review Architecture

Large reviews (multi-file PRs, complex extraction pipelines, broad code audits) suffer from attention dilution when processed in a single pass. The symptoms are specific and recognisable:

The fix is to split the review into focused passes:

Pass 1: Per-file local analysis. Analyse each file individually with a focused review prompt. This ensures consistent depth across all files. Each invocation examines only one file, so the model gives it full attention.

Pass 2: Cross-file integration. After all per-file analyses are complete, run a separate pass that receives all per-file findings and checks for cross-file issues: data flow between modules, consistent API usage across services, dependency conflicts, and contradictions in the per-file findings themselves.

// Pass 1: Per-file analysis
const perFileFindings = await Promise.all(
  files.map(file =>
    client.messages.create({
      messages: [{
        role: "user",
        content: `Review this file for local issues (bugs, security, logic errors):\n\n${file.content}`
      }]
    })
  )
);

// Pass 2: Cross-file integration
const integrationReview = await client.messages.create({
  messages: [{
    role: "user",
    content: `Given these per-file findings, identify cross-file issues:\n` +
      `- Data flow inconsistencies between modules\n` +
      `- Contradictory patterns flagged in different files\n` +
      `- API contract violations across service boundaries\n\n` +
      `Findings:\n${JSON.stringify(perFileFindings)}`
  }]
});

This architecture directly addresses the three symptoms of attention dilution. Per-file passes ensure consistent depth. The integration pass catches cross-file issues that no single-file review would identify. And the separation prevents contradictory findings from appearing in the same output.

Why Larger Context Windows Do Not Fix This

The exam includes a specific distractor: "switch to a higher-tier model with a larger context window." This sounds reasonable - if the model can't handle 14 files at once, give it more capacity. But the problem isn't context size. It's attention quality. A bigger context window won't stop the model from spreading its attention unevenly across files. Only focused, per-file passes ensure consistent depth.

Confidence-Based Routing

For findings that are uncertain, the model can self-report confidence alongside each finding. This enables a routing strategy:

{
  "finding": "Potential race condition in order processing",
  "severity": "major",
  "confidence": 0.65,
  "reasoning": "The lock acquisition pattern appears correct but the unlock timing depends on an async callback whose ordering I cannot fully verify.",
  "route": "human_review"
}

The confidence score isn't self-reported accuracy. It's the model's read on its own certainty. Calibrate it by running labelled examples (where you already know the answer) through the system and measuring how reported confidence tracks actual accuracy. Then adjust routing thresholds from that data.

The exam distinguishes between raw confidence scores (uncalibrated, unreliable for automated decisions) and calibrated confidence thresholds (validated against labelled sets, suitable for routing). Using uncalibrated confidence for automated decisions is an anti-pattern.

Key Concept

A model reviewing its own output in the same session retains reasoning context and is less likely to question its decisions. Use independent instances for review. Split large reviews into per-file local passes plus a cross-file integration pass to prevent attention dilution. Calibrate confidence thresholds using labelled validation sets before using them for routing.

Putting It All Together

A production review architecture combines all three concepts:

  1. Generation: First instance generates code, extraction, or analysis
  2. Per-file review: Independent instances review each output unit individually
  3. Integration review: Separate instance checks cross-unit consistency
  4. Confidence routing: Low-confidence findings go to human review
  5. Calibration loop: Labelled validation sets continuously calibrate confidence thresholds

This architecture is more expensive than single-pass review. The trade-off is worth it when review quality directly affects production reliability - CI/CD pipelines, financial extraction, compliance analysis, and any system where missed issues have downstream consequences.

Exam traps

Practice question

A pull request modifying 14 files receives inconsistent review: detailed feedback on some files, superficial comments on others, obvious bugs missed, and contradictory findings - the same pattern is flagged as problematic in one file but approved in another. How should you restructure the review?

  • A Switch to a higher-tier model with a much larger context window so that all 14 files receive adequate attention within a single review pass

    Larger context windows do not solve attention quality issues. The model can hold more text but still gives uneven attention across files. This is an attention dilution problem, not a context size problem.

  • B Split into per-file local analysis passes for consistent depth, then run a separate cross-file integration pass for data flow issues Correct

    Per-file analysis ensures every file gets consistent, focused attention. The separate integration pass catches cross-file issues that no single-file review would identify. This directly addresses all three symptoms: inconsistent depth, missed bugs, and contradictory findings.

  • C Run three independent review passes over the full PR and only flag those issues that at least two of the three separate runs agree on

    This suppresses real bug detection by requiring consensus on issues that may only be caught intermittently. It trades sensitivity for false consistency.

  • D Require developers to split large pull requests into smaller submissions of 3-4 files before the automated review runs

    This shifts burden to developers and changes the team workflow without improving the review system itself. The system should handle large PRs through better architecture.

Build exercise: Build a Multi-Pass Code Review System

Advanced · 60 minutes

You'll practice:

  1. Create a single-pass review prompt and run it against a 10-file mock PR - document instances of inconsistent depth, missed issues, and contradictory findings

    Establishing the single-pass baseline demonstrates the three symptoms of attention dilution: inconsistent depth across files, missed bugs in the middle of the review, and contradictory findings flagging the same pattern differently in different files.

    You should see: Detailed feedback on some files (typically first and last) but superficial comments on others, at least one obvious bug missed in a middle file, and at least one contradictory finding where the same code pattern is flagged as problematic in one file but approved in another.

    Hints
    1. Create 10 files with known bugs distributed evenly. Plant an obvious SQL injection in file 6 or 7 to test middle-of-review attention.
    2. Track three metrics per file: number of findings, depth of analysis (detailed vs superficial), and whether planted bugs were caught.
    3. const files = generateMockPR(10); // 10 files with known bugs
      const singlePassResult = await client.messages.create({
        messages: [{
          role: "user",
          content: `Review this PR for bugs, security issues, and logic errors:\n\n${files.map(f => `--- ${f.name} ---\n${f.content}`).join("\n\n")}`
        }]
      });
      
      // Document attention dilution symptoms
      const perFileDepth = files.map(f => assessReviewDepth(singlePassResult, f.name));
  2. Implement per-file local analysis: iterate over each file with a focused review prompt that examines only that file for bugs, security issues, and logic errors

    Per-file analysis ensures every file receives consistent, focused attention. Each invocation examines only one file, eliminating the attention dilution that causes inconsistent depth and missed bugs in single-pass reviews.

    You should see: Consistent review depth across all 10 files. Bugs that were missed in the single-pass review should now be caught, especially those in the middle files. Each review should be focused and thorough.

    Hints
    1. Use Promise.all to run per-file reviews in parallel for efficiency. Each review gets its own fresh context with only one file.
    2. The review prompt should be specific: focus on bugs, security issues, and logic errors within this single file. Do not ask it to consider cross-file concerns yet.
    3. const perFileFindings = await Promise.all(
        files.map(file =>
          client.messages.create({
            messages: [{
              role: "user",
              content: `Review this file for bugs, security issues, and logic errors:\n\n${file.content}`
            }]
          })
        )
      );
      
      // Compare depth and bug detection against single-pass results
      const improved = files.map((f, i) => ({
        file: f.name,
        singlePassFindings: countFindings(singlePassResult, f.name),
        perFileFindings: countFindings(perFileFindings[i])
      }));
  3. Implement a cross-file integration pass: feed all per-file findings into a separate prompt that checks for data flow inconsistencies, contradictory findings across files, and API contract violations

    Per-file analysis catches local issues but misses cross-file concerns: data flow between modules, consistent API usage, and contradictions in per-file findings. The integration pass is a separate invocation that receives all findings and checks for systemic issues.

    You should see: A synthesis output identifying cross-file issues that no single-file review could catch: data passed between modules in incompatible formats, contradictory findings from per-file reviews, and API contracts violated across service boundaries.

    Hints
    1. Structure the integration prompt to check three specific categories: data flow inconsistencies, contradictory findings, and API contract violations.
    2. Feed the per-file findings as structured data, not raw text, so the integration pass can compare findings systematically.
    3. const integrationReview = await client.messages.create({
        messages: [{
          role: "user",
          content: `Given these per-file review findings, identify cross-file issues:\n` +
            `- Data flow inconsistencies between modules\n` +
            `- Contradictory patterns flagged differently in different files\n` +
            `- API contract violations across service boundaries\n\n` +
            `Per-file findings:\n${JSON.stringify(perFileFindings, null, 2)}`
        }]
      });
  4. Add confidence scoring to each finding (0.0-1.0) and implement routing: high confidence findings go directly to the developer, low confidence findings go to a human review queue

    Confidence-based routing directs limited human reviewer attention to the findings that need it most. The exam distinguishes raw uncalibrated confidence from calibrated thresholds validated against labelled sets.

    You should see: Each finding annotated with a confidence score, reasoning for the score, and a routing decision (direct_report or human_review). The routing threshold should separate clear-cut findings from uncertain ones.

    Hints
    1. Include a reasoning field alongside the confidence score so you can later analyse why the model was uncertain on specific findings.
    2. Start with a routing threshold of 0.80 as a baseline. Findings above go to developers, below go to human review. You will calibrate this in the next step.
    3. const reviewPrompt = `Review this file. For each finding, include:\n` +
        `- finding: description of the issue\n` +
        `- severity: critical/major/minor\n` +
        `- confidence: 0.0-1.0 score\n` +
        `- reasoning: why you are confident or uncertain\n\n`;
      
      function routeFinding(finding) {
        return {
          ...finding,
          route: finding.confidence >= 0.80 ? "direct_report" : "human_review"
        };
      }
  5. Use a separate Claude instance (fresh session, no prior context) to review a subset of the generated findings and compare its assessment to the original confidence scores for calibration

    Independent review instances approach output fresh without the bias of I chose this because reasoning. This step calibrates confidence thresholds by comparing self-reported confidence against independent assessment, the method the exam identifies as the correct approach.

    You should see: A calibration dataset showing the relationship between reported confidence scores and independent verification results. Some high-confidence findings may be overturned, revealing calibration gaps that adjust your routing thresholds.

    Hints
    1. The independent instance must have NO access to the original reasoning. Send only the code and the finding, not the original review context.
    2. Build a calibration curve: for each confidence band (0.6-0.7, 0.7-0.8, 0.8-0.9, 0.9-1.0), calculate what percentage of findings were confirmed by independent review.
    3. // Independent review - fresh instance, no prior context
      const independentVerification = await Promise.all(
        sampleFindings.map(finding =>
          client.messages.create({
            messages: [{
              role: "user",
              content: `Review this code and assess whether this finding is valid:\n\nCode:\n${finding.code}\n\nFinding: ${finding.description}\nSeverity: ${finding.severity}\n\nIs this finding valid? Explain your assessment.`
            }]
          })
        )
      );
      
      // Build calibration curve
      const calibration = buildCalibrationCurve(sampleFindings, independentVerification);
      // Adjust routing threshold based on calibration data

Sources