Skip to content

A Scout Report is a read-only codebase investigation — structured output with exact file paths, line numbers, and root cause — so planners receive verified facts instead of assumptions.

Primitive

Scout Report

The artifact

A filled template: problem, files, root cause, resolution, priority.

scout-report · src/billing/

### SCOUT REPORT─────────────────────────────────────────────────────────────────PROBLEM STATEMENTStripe webhook events with `payment_intent.succeeded` are silentlydropped when the idempotency key already exists in the DB. No erroris raised; the subscription record is never updated to `active`.SEARCH SCOPE Directory : src/billing/ Analyzed : 14 files (6 handlers, 4 models, 3 services, 1 util)EXECUTIVE SUMMARYThe webhook router delegates to `handle_payment_intent` which calls`upsert_event()`. The upsert silently no-ops on conflict instead ofreturning the existing row status. Downstream subscription update isonly invoked on INSERT; UPDATE/CONFLICT paths are unreachable.FINDINGS ┌──────────────────────────────────────────────────────────────┐ │src/billing/handlers/webhook.py │ │L112–L128handle_payment_intent() │ │Relevance: DIRECT — calls upsert_event, ignores return value │ └──────────────────────────────────────────────────────────────┘ ┌──────────────────────────────────────────────────────────────┐ │src/billing/services/event_store.py │ │L47–L61upsert_event() │ │Relevance: DIRECT — ON CONFLICT DO NOTHING, returns None │ └──────────────────────────────────────────────────────────────┘DETAILED ANALYSISRoot cause: `upsert_event()` uses `ON CONFLICT DO NOTHING` (L55) andreturns `None` on conflict. `handle_payment_intent` at L119 brancheson `if event:` — the conflict path evaluates falsy. The subscriptionactivation call at L124 is never reached for duplicate deliveries,which Stripe sends on every retry of an unacknowledged webhook.SUGGESTED RESOLUTION [1] event_store.py L55 — change to ON CONFLICT DO UPDATE SET processed_at=NOW() RETURNING * (always return the row) [2] webhook.py L119 — remove falsy guard; always call activate_subscription() when intent status is `succeeded` Rationale: idempotency is preserved by the upsert; activation is safe to repeat because it checks current sub status before writing.PRIORITYHIGH — revenue-impacting; affects all retried webhook deliveries

Every field is load-bearing. Missing line numbers means a builder agent must re-investigate. Missing root cause means the fix guesses. The template exists to make gaps visible.

Two variants

Base digs deep. Fast sweeps wide. Pick by what you need.

Base variant

Deep single-area analysis

Modelclaude-opus

Scopeone module / area

Depthroot cause required

Duration~3–5 min

Outputfull 7-section report

Use Base when You know the problem area and need a trustworthy root cause before writing a plan. Billing bug. Auth failure. Data pipeline regression. The area is bounded; the depth must be real.

Fast variant

Broad multi-file coverage

Modelclaude-haiku

Scopewhole repo surface

Depthaffected files + lines

Duration~30 sec

Outputfindings + priority only

Use Fast when You are discovering what to investigate next. Onboarding to a new codebase. Triaging multiple possible impact zones. Speed matters more than depth; you need a map, not a diagnosis.

Fast gives you the map. Base gives you the root cause. A common pipeline runs Fast first to identify the area, then Base on the highest-priority finding.

Read-only boundary

Investigation that cannot mutate state is investigation you can trust.

scout-agent.mdRead-only constraint

Allowed tools

✓Read— file contents

✓Glob— file discovery

✓Grep— pattern search

Blocked tools

✗Write— creates files

✗Edit— modifies files

✗Bash— executes commands

The guarantee this boundary provides: The scout agent cannot introduce state changes during investigation. No accidental file creation. No test runs that alter fixtures. No shell commands with side effects. A planner receiving a scout report knows the codebase is in exactly the state it was before the scout ran. Without this boundary, investigation and mutation become indistinguishable.

The read-only constraint is declared in the agent frontmatter as tools: [Read, Glob, Grep]. Anything not listed is not available. The constraint is structural, not advisory.

Scout → Plan chain

Planners run on facts, not guesses. The scout makes facts.

Scout Agent

Reads the codebase

  • Reads file contents, greps patterns
  • Identifies affected files with line ranges
  • Traces call chain to root cause

Scout Report

Verified facts

  • Exact file paths confirmed on disk
  • Line numbers tested against source
  • Root cause stated with evidence

Planner Agent

Reads the report

  • Consumes verified paths + lines
  • Builds tasks from root cause
  • No re-investigation needed

Plan

Accurate tasks

  • Tasks reference real files
  • Line numbers propagated to builders
  • Rationale grounded in evidence

With scout

  • Planner receives confirmed file paths
  • Exact line numbers in every task
  • Root cause is stated, not theorized
  • Builder goes straight to the fix

Without scout

  • Planner guesses at file locations
  • Line numbers hallucinated or absent
  • Root cause is an assumption
  • Builder re-investigates before fixing

The scout is the only agent that reads for the planner. The planner is the only agent that plans for the builders. Neither role bleeds into the other's responsibility.

The guarantee

Planning agents receive verified input, not assumptions.

Hallucinated file paths are eliminated at the investigation stage. Every downstream task carries exact line numbers and a root cause grounded in evidence. The scout's read-only constraint means investigation never mutates state — the codebase you analyze is the codebase that gets fixed. Without the Scout Report, every plan is built on guesses; with it, every plan is built on facts you can point to.