Appearance
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.