Decisions/2026-05-07-scout-subrouter-pattern.md
ADR: Scout as Pipeline Sub-Agent Pattern
Date: 2026-05-07 Status: Partially Accepted (1A implemented; others pending design)
Context
Scout (crawl4ai + Playwright stealth) was originally built as a standalone intelligence tool. After adding stealth mode (2026-05-07), it reliably bypasses Cloudflare and Akamai — the two most common WAF vendors encountered in the audit pipeline. This opens Scout as a general-purpose crawl layer inside PRISM intelligence modules, not just a standalone tool.
The question is: should Scout be wired into individual modules as a sub-agent, and if so, how?
Decision
Scout will be integrated as a sub-agent inside select PRISM intelligence modules using the following pattern:
Module script (collect-*.py)
→ calls scout_company.run(url, output_dir) or scout_company.scrape_url(url)
→ Scout returns structured JSON (not raw markdown)
→ Module's pre-filter layer extracts signal keywords/fields
→ Only the filtered signal (not the full crawl) is passed to LLM enrichment
Scout is NOT a replacement for WebSearch or WebFetch. It is the crawl layer for pages that block those methods (Akamai, Cloudflare, JS-rendered pages, ATS portals).
Rationale
-
The WAF problem is real and pervasive. SimilarWeb API expired, BuiltWith returns JS loading state, basic WebFetch fails on ATS portals, IAG's investor page was Cloudflare-blocked. Scout stealth resolves all of these.
-
The data volume problem requires a pre-filter layer. Raw crawl output cannot go directly to LLM — context burns. The filter layer (keyword matching, CSS schema extraction, token budgets) must be defined per module before implementation.
-
Module 1A is the proof of concept. Scout Step 4 in collect-company.py is live, tested, and working. It fills null fields without overwriting existing data. This pattern is the template for all other modules.
Alternatives Considered
| Option | Why rejected |
|---|---|
| Replace WebFetch entirely with Scout | Over-engineering. WebFetch is faster and sufficient for non-WAF pages. Scout adds latency (2-5s stealth mode). Use Scout only where needed. |
| Use Scout for everything in parallel | Would burn Crawl4AI compute and slow pipelines. Sequential, targeted calls are better. |
| Apify for all crawling | Expensive per-run costs. Scout is self-hosted, unlimited. |
Consequences
scout_company.pyis the shared Scout adapter. Modules import it rather than calling Scout API directly.- Each module that adopts Scout must define: (a) which URLs to crawl, (b) token budget for crawl content, (c) pre-filter keywords/schema, (d) fallback behavior when Scout returns sparse content.
- The pre-filter layer must be implemented BEFORE any new Scout integration ships. Raw markdown → LLM is explicitly prohibited.
- Cache TTL per content type must be defined before 1H or 1D integration.
Next Design Steps (before implementing 1H or 1D)
- Define the
ScoutSignalcanonical output schema — compact JSON, not raw markdown - Define per-module token budgets
- Define the pre-filter layer (keyword regex vs CSS schema vs embedding similarity)
- Define cache TTLs per content type (JDs: 7 days, newsrooms: 1 day, IR pages: 30 days)
- Define tiered model routing (Haiku for filtering, Sonnet for synthesis)