Project 35: “The Deep Reasoner (Claude Code)” — Algorithmic Reasoning

Attribute Value
File KIRO_CLI_LEARNING_PROJECTS.md
Main Programming Language Python
Coolness Level Level 4: Hardcore Tech Flex
Difficulty Level 3: Advanced
Knowledge Area Algorithmic Reasoning

What you’ll build: Solve a complex algorithmic problem using a scratchpad file and Opus model.

Why it teaches Reasoning: You use high-end reasoning as a tool.

Success criteria:

  • The solution passes a non-trivial test set.

Real World Outcome

You’ll have a Kiro workflow that solves complex algorithmic problems by:

  1. Breaking down problems into sub-problems (problem decomposition)
  2. Using a scratchpad to work through examples and edge cases
  3. Switching to Opus (Claude Opus 4.5) for deep reasoning tasks
  4. Iterating on solutions based on test failures
  5. Generating test cases to validate correctness

Example Session:

$ kiro "Solve LeetCode #1235: Maximum Profit in Job Scheduling using dynamic programming with binary search. Use Opus for reasoning."

🧠 Deep Reasoner - Algorithmic Problem Solving
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

📋 Problem Analysis Phase (using Opus)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Switching to Claude Opus 4.5 for deep reasoning...

Problem: Maximum Profit in Job Scheduling
Inputs:
  - startTime: [1,2,3,3]
  - endTime: [3,4,5,6]
  - profit: [50,10,40,70]
Output: 120 (jobs 0 and 3)

Constraint: Cannot take two overlapping jobs.

📝 Creating Scratchpad: /tmp/job_scheduling_scratchpad.md

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
SCRATCHPAD: Job Scheduling Problem
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

## Step 1: Understanding the Problem

Jobs:
Job 0: [1, 3] profit=50
Job 1: [2, 4] profit=10
Job 2: [3, 5] profit=40
Job 3: [3, 6] profit=70

Overlapping check:
- Job 0 [1,3] overlaps with Job 1 [2,4]? YES (2 < 3)
- Job 0 [1,3] overlaps with Job 2 [3,5]? NO (3 >= 3)
- Job 0 [1,3] overlaps with Job 3 [3,6]? NO (3 >= 3)

## Step 2: Brute Force Approach

Try all subsets: 2^n = 16 combinations
- {} → profit = 0
- {0} → profit = 50
- {1} → profit = 10
- {0,2} → profit = 90 (0 ends at 3, 2 starts at 3 ✓)
- {0,3} → profit = 120 (0 ends at 3, 3 starts at 3 ✓) ← MAX!

Brute force works but O(2^n) - too slow for n=50,000!

## Step 3: Dynamic Programming Insight

Key observation: If we sort jobs by end time, we can use DP!

dp[i] = max profit using jobs 0..i

For each job i:
  Option 1: Skip job i → dp[i] = dp[i-1]
  Option 2: Take job i → dp[i] = profit[i] + dp[last_compatible_job]

Finding last_compatible_job:
  - Need job j where endTime[j] <= startTime[i]
  - Binary search on sorted end times! (O(log n))

## Step 4: Algorithm Design

1. Sort jobs by end time
2. For each job i:
   - Binary search to find latest job j where endTime[j] <= startTime[i]
   - dp[i] = max(dp[i-1], profit[i] + dp[j])
3. Return dp[n-1]

Time: O(n log n) - sorting + binary search per job
Space: O(n) - DP array

## Step 5: Edge Cases to Test

- Single job → profit = job profit
- All jobs overlap → profit = max single job
- No jobs → profit = 0
- Jobs already sorted vs unsorted
- Identical start/end times

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

✅ Reasoning complete. Generating solution...

🐍 Generating Python Implementation...

def jobScheduling(startTime, endTime, profit):
    # Combine and sort jobs by end time
    jobs = sorted(zip(startTime, endTime, profit), key=lambda x: x[1])
    n = len(jobs)

    # Binary search for last compatible job
    def find_last_compatible(i):
        left, right = 0, i - 1
        result = -1
        while left <= right:
            mid = (left + right) // 2
            if jobs[mid][1] <= jobs[i][0]:  # endTime[mid] <= startTime[i]
                result = mid
                left = mid + 1
            else:
                right = mid - 1
        return result

    # DP array
    dp = [0] * n
    dp[0] = jobs[0][2]  # First job profit

    for i in range(1, n):
        # Option 1: Skip job i
        skip = dp[i - 1]

        # Option 2: Take job i
        take = jobs[i][2]  # Current job profit
        last_compatible = find_last_compatible(i)
        if last_compatible != -1:
            take += dp[last_compatible]

        dp[i] = max(skip, take)

    return dp[n - 1]

✅ Solution generated: solution.py

🧪 Generating Test Cases...

# Test 1: Example from problem
assert jobScheduling([1,2,3,3], [3,4,5,6], [50,10,40,70]) == 120

# Test 2: All overlapping jobs
assert jobScheduling([1,1,1], [2,3,4], [5,6,4]) == 6

# Test 3: No overlapping jobs
assert jobScheduling([1,2,3], [2,3,4], [5,6,4]) == 15

# Test 4: Single job
assert jobScheduling([1], [2], [100]) == 100

# Test 5: Two identical jobs
assert jobScheduling([1,1], [2,2], [50,50]) == 50

Running tests...
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✅ Test 1: PASSED (120 == 120)
✅ Test 2: PASSED (6 == 6)
✅ Test 3: PASSED (15 == 15)
✅ Test 4: PASSED (100 == 100)
✅ Test 5: PASSED (50 == 50)

All tests passed! ✓

📊 Complexity Analysis:
├─ Time: O(n log n) - sorting + binary search
├─ Space: O(n) - DP array
└─ Verified against 50,000 job test set ✓

💡 Key Insights (from scratchpad):
1. Sorting by end time enables DP
2. Binary search finds last compatible job in O(log n)
3. Trade-off: Skip vs Take each job

Would you like me to:
1. Add more test cases?
2. Optimize space to O(1)?
3. Explain the binary search in more detail?

What You See:

  • Step-by-step problem breakdown in scratchpad file
  • Opus-powered reasoning for algorithm design
  • Generated Python solution with binary search optimization
  • Comprehensive test suite with edge cases
  • Complexity analysis and verification

The Core Question You’re Answering

“How can AI leverage extended reasoning (Claude Opus) and structured scratchpad files to solve problems that require multi-step logical deduction?”

This project teaches metacognitive problem-solving: using an AI system that knows when to slow down and think deeply. Switching to Opus for hard problems is analogous to a developer pausing to whiteboard a solution before coding.


Concepts You Must Understand First

Stop and research these before coding:

  1. Dynamic Programming Fundamentals
    • What is overlapping subproblems and optimal substructure?
    • How do you identify when DP is applicable?
    • What is the difference between top-down (memoization) and bottom-up (tabulation)?
    • Book Reference: “Introduction to Algorithms” (CLRS) - Ch. 15
  2. Binary Search and Divide-and-Conquer
    • How does binary search achieve O(log n) time?
    • What invariants must be maintained during binary search?
    • When is binary search applicable (sorted data, monotonic function)?
    • Book Reference: “Algorithms” by Sedgewick & Wayne - Ch. 3.1
  3. Problem Decomposition Strategies
    • How do you break a complex problem into smaller sub-problems?
    • What is the “simplify and generalize” technique?
    • How do you identify base cases vs recursive cases?
    • Book Reference: “How to Solve It” by George Pólya - Ch. 1-2
  4. Complexity Analysis
    • How do you calculate time complexity (Big-O notation)?
    • What is the difference between time complexity and space complexity?
    • How do you identify bottlenecks (profiling, asymptotic analysis)?
    • Book Reference: “Grokking Algorithms” by Aditya Bhargava - Ch. 1-2

Questions to Guide Your Design

Before implementing, think through these:

  1. Model Selection Strategy
    • When should you use Opus (slow, expensive, deep reasoning)?
    • When should you use Sonnet (fast, cheaper, good for most tasks)?
    • How do you detect that a problem requires deep reasoning?
    • How do you balance cost vs solution quality?
  2. Scratchpad Design
    • What should go in the scratchpad (examples, edge cases, algorithm sketches)?
    • How do you structure the scratchpad (markdown sections, code blocks)?
    • How do you prevent the scratchpad from growing too large (context limits)?
    • How do you use the scratchpad to validate the final solution?
  3. Test Case Generation
    • How do you generate edge cases (empty input, single element, all same values)?
    • How do you generate stress tests (large n, worst-case inputs)?
    • How do you verify correctness (test against brute force, known solutions)?
    • How do you measure coverage (all branches, all edge cases)?
  4. Solution Validation
    • How do you verify time complexity matches the theoretical analysis?
    • How do you test on large inputs (n=50,000, n=100,000)?
    • How do you handle time limit exceeded (TLE) failures?
    • How do you iterate when tests fail (analyze failure, refine algorithm)?

Thinking Exercise

Exercise: Solve a Two-Pointer Problem with Scratchpad

Problem: “Three Sum - Find all unique triplets in an array that sum to zero”

# Input: nums = [-1, 0, 1, 2, -1, -4]
# Output: [[-1, -1, 2], [-1, 0, 1]]

Scratchpad Template:

## Step 1: Understand the Problem
- Input: Array of integers (may have duplicates)
- Output: All unique triplets that sum to 0
- Constraint: No duplicate triplets in result

## Step 2: Examples and Edge Cases
Example 1: [-1, 0, 1, 2, -1, -4]
  Sort: [-4, -1, -1, 0, 1, 2]
  Fix -4: need two numbers that sum to 4 → no solution
  Fix -1: need two numbers that sum to 1 → (-1, 0, 1) ✓ and (-1, 2, -1) ✓

Edge cases:
- All zeros: [0, 0, 0] → [[0, 0, 0]]
- No solution: [1, 2, 3] → []
- Duplicates: [-1, -1, 0, 1, 1] → [[-1, 0, 1]]

## Step 3: Brute Force
Try all triplets: O(n³)
for i in range(n):
  for j in range(i+1, n):
    for k in range(j+1, n):
      if nums[i] + nums[j] + nums[k] == 0:
        result.append([nums[i], nums[j], nums[k]])

Too slow for n=3000!

## Step 4: Optimized Approach
Sort array: O(n log n)
Fix first element, use two pointers for remaining: O(n²)

Questions to answer:

  • How do you avoid duplicate triplets (skip same values)?
  • How do you move the two pointers (left++, right–)?
  • What is the time complexity (O(n²) after sorting)?
  • How do you test this (unit tests, large inputs)?

The Interview Questions They’ll Ask

  1. “Explain the difference between using Claude Sonnet vs Claude Opus. When would you choose each?”

  2. “How would you design a scratchpad system that prevents context overflow (scratchpad growing too large)?”

  3. “Walk me through how you’d debug a dynamic programming solution that passes small tests but fails large ones.”

  4. “How would you validate that an AI-generated algorithm matches the claimed time complexity?”

  5. “Explain how you’d use test-driven development with AI code generation (write tests first, then generate solution).”

  6. “How would you prevent AI from overfitting to test cases (generating code that passes tests but is wrong)?”


Hints in Layers

Hint 1: Opus Model Selection Use Opus when:

  • Problem requires multi-step logical reasoning (DP, graph algorithms)
  • Problem has non-obvious insights (greedy choice, invariant)
  • Initial Sonnet solution fails tests and you need deeper analysis
  • You need detailed explanations for teaching/understanding

Use Sonnet when:

  • Problem is straightforward (two-pointer, hash map)
  • You need fast iteration (testing multiple approaches)
  • Cost is a concern (Opus is 5x more expensive)

Hint 2: Scratchpad Structure

# Problem: [Title]

## Step 1: Problem Understanding
[Restate in own words, identify constraints]

## Step 2: Examples (3-5 examples, including edge cases)
[Work through examples by hand]

## Step 3: Brute Force
[Naive O(n²) or O(2^n) solution, analyze why it's slow]

## Step 4: Optimization Insight
[Key observation that leads to faster solution]

## Step 5: Algorithm Design
[Pseudocode or step-by-step description]

## Step 6: Complexity Analysis
[Time and space complexity with justification]

## Step 7: Edge Cases
[List all edge cases to test]

Hint 3: Test Generation Strategy Generate tests in this order:

  1. Example from problem statement (sanity check)
  2. Edge cases (empty, single element, all same)
  3. Boundary cases (min/max values, size limits)
  4. Stress tests (large n, worst-case complexity)
  5. Random tests (fuzzing for unexpected bugs)

Hint 4: Solution Validation Loop

1. Generate solution using Opus + scratchpad
2. Run tests (example + edge + stress)
3. If all pass → done ✓
4. If some fail:
   a. Analyze failure (wrong output, TLE, crash)
   b. Add failing case to scratchpad
   c. Ask Opus to refine solution
   d. Go to step 2

Hint 5: Complexity Verification

import time

# Test time complexity empirically
for n in [100, 1000, 10000, 100000]:
    input_data = generate_test_input(n)
    start = time.time()
    result = jobScheduling(input_data)
    elapsed = time.time() - start
    print(f"n={n:6d} time={elapsed:.4f}s")

# Expected for O(n log n):
# n=100 time=0.0001s
# n=1000 time=0.0012s (10x input → ~10x time)
# n=10000 time=0.015s (10x input → ~13x time)
# n=100000 time=0.20s (10x input → ~13x time)

Books That Will Help

Topic Book Chapter
Dynamic programming “Introduction to Algorithms” (CLRS) Ch. 15 (Dynamic Programming)
Problem-solving strategies “How to Solve It” by George Pólya Ch. 1-2 (Understanding, Devising a Plan)
Algorithm design “Algorithms” by Sedgewick & Wayne Ch. 3-6 (Sorting, Searching, DP)
Complexity analysis “Grokking Algorithms” by Aditya Bhargava Ch. 1-2 (Big-O Notation)
Competitive programming “Competitive Programming 4” by Steven Halim Ch. 3 (Problem Solving Paradigms)
Interview prep “Cracking the Coding Interview” by Gayle McDowell Ch. 8-9 (DP, Recursion)

Common Pitfalls & Debugging

Problem 1: “Solution passes small tests but gets Time Limit Exceeded (TLE) on large inputs”

  • Why: Algorithm is O(n³) or O(2^n), too slow for n=50,000
  • Fix: Analyze complexity in scratchpad, find O(n log n) or O(n²) solution
  • Quick test: Run on n=100, n=1000, n=10000 and measure time scaling

Problem 2: “Opus generates a solution but doesn’t explain the key insight”

  • Why: Opus jumps to solution without showing reasoning steps
  • Fix: Ask Opus to “work through examples in the scratchpad first, then generate code”
  • Pattern: Always request scratchpad-driven reasoning before code

Problem 3: “Generated solution is correct but hard to understand (no comments, cryptic variable names)”

  • Why: Optimization prioritized over readability
  • Fix: Ask for “readable solution with comments explaining each step”
  • Example: Change dp[i] = max(dp[i-1], p[i] + dp[f(i)]) to include comment explaining skip vs take

Problem 4: “Tests pass but solution fails on edge case not in test suite”

  • Why: Incomplete test coverage (missing edge case)
  • Fix: Add property-based testing or fuzz testing
  • Example: Use Hypothesis library to generate random test inputs

Problem 5: “Binary search has off-by-one error (infinite loop or wrong answer)”

  • Why: Incorrect loop invariant (left <= right vs left < right)
  • Fix: Trace through binary search by hand on small example
  • Debugging: Print left, right, mid at each iteration to see what’s wrong

Problem 6: “Opus uses too much context analyzing the problem (exceeds token limit)”

  • Why: Scratchpad includes too much detail (full trace of all examples)
  • Fix: Summarize examples instead of full traces
  • Pattern: “Show 2-3 key examples, not all 10 test cases”

Definition of Done

  • Scratchpad file shows step-by-step reasoning (problem understanding → brute force → optimization)
  • Solution is generated using Opus model (verify model selection in logs)
  • All example test cases from problem statement pass
  • Edge cases are identified in scratchpad and tested
  • Stress test with large input (n=50,000) completes in <1 second
  • Time complexity matches theoretical analysis (empirical verification)
  • Space complexity is documented and justified
  • Solution includes comments explaining key steps (binary search, DP transition)
  • Code is readable (meaningful variable names, no cryptic abbreviations)
  • Complexity analysis is included in output (Big-O with justification)
  • Solution generalizes beyond test cases (not hardcoded for specific inputs)