Scout

wiki/sources/legacy/Design-2026-05-04-phase4-prism-integration.md

Scout Phase 4 — PRISM intel-hiring Integration

What shipped

Scout Phase 4 wired Scout as the Tier 2 stealth browser in PRISM's BrowserClient and built a 2-track pipeline for intel-hiring.

Component 1: prism_platform/browser/tier2_stealth.py

Replaced the Playwright stub with Scout. ScoutCrawler.scrape() is called with use_js=True. Maps ScrapeResponseFetchResult: - resp.metadata.urlFetchResult.url (redirect tracking) - resp.markdownFetchResult.text - resp.raw_htmlFetchResult.html - FetchTier.PLAYWRIGHT always - Content shorter than options.min_content_lengthis_bot_blocked=True

interactive_session remains a stub (search audit Phase 5+).

Component 2: prism_platform/v2/modules/intel_hiring/fetcher.py

fetch_careers_page(domain, timeout=20.0) -> HiringFetchResult with 3 phases: 1. httpx probe on /careers and /jobs (fast, cheap — exits if 200 OK with content) 2. Scout Playwright on 9 career path patterns 3. Scout Playwright on career subdomains (careers.{domain}, jobs.{domain}) 4. LinkedIn redirect detection: resp.metadata.url contains linkedin.comredirected_to_linkedin=True 5. Empty HiringFetchResult if all fail (non-fatal)

HiringFetchResult is a frozen Pydantic model.

Component 3: prism_platform/v2/modules/intel_hiring/playbook.md

Added ## Live Career Page Content section at the top with {upstream_careers_page} placeholder. Perplexity is instructed to treat the injected content as authoritative ground truth and fall back to web search if empty.

Component 4: prism_platform/orchestrator/activities.py

Added _run_intel_hiring_pipeline(): 1. Track 1: fetch_careers_page(input.domain) → inject careers_page into v2_context.upstream_results 2. LinkedIn redirect: log warning, continue (Perplexity still runs) 3. Track 2: ModuleExecutor.execute() — Perplexity research with injected content 4. No Track 3 (no synthesis — ground truth injected directly into Perplexity prompt)

Routing: added elif input.module_name == "intel-hiring" before the generic fallback.

fetch_careers_page is imported at module level (not locally) so unittest.mock.patch can intercept.

Bugs fixed in Scout during Phase 4

crawl.py / map.py — BFSDeepCrawlStrategy broken in 0.7.7

BFSDeepCrawlStrategy + arun_many() returns a list/generator of CrawlResultContainer objects where .dispatch_result.result is None — the actual crawl content is lost. This is a 0.7.7 internal API incompatibility.

Fix: Removed BFSDeepCrawlStrategy entirely. Implemented manual BFS: - arun() per URL (returns CrawlResultContainer with full content via attribute proxying) - Link discovery via result.links["internal"] and result.links["external"] - deque queue with (url, depth) tuples - Respects max_depth, max_pages, url_pattern, include_external

extract.py — LLM extraction returns list not dict

LLMExtractionStrategy wraps extracted items in a JSON array. result.extracted_content = '[{"field": "value"}]'. The ExtractResponse.data field is typed as dict, so parsing without unwrapping → Pydantic validation error.

Fix: After json.loads, check if result is list → use raw[0] if raw else {}.

core/init.py — ScoutCrawler not exported

from scout.core import ScoutCrawler raised ImportError__init__.py was empty.

Fix: Added from scout.core.crawler import ScoutCrawler; __all__ = ["ScoutCrawler"].

Tests

19 new tests in tests/v2/test_intel_hiring_phase4.py covering AC-1 through AC-12: - Layer 1 (unit): TestTier2Stealth (4 tests), TestHiringFetcher (7 tests) - Layer 2 (contract): TestHiringFetchResult (3 tests), TestPlaybookCareer (2 tests) - Layer 3 (integration): TestHiringPipeline (3 tests)

Full suite: 213/213 passing. Ruff: 0 errors in new files. Mypy: 0 errors.

Open items (deferred)

  1. Dockerfile: playwright install chromium needed in PRISM production container
  2. Scout production packaging: editable install (uv add --editable) works dev only
  3. Temporal timeout: intel-hiring activity timeout may need 120s → 180s
  4. Fast-follows: cap Scout at 3 paths (not 9), asyncio.gather for concurrent probing
  5. Scout Claude Code skill not yet installed in settings
  6. intel-hiring frontend not yet built (module not "complete" per definition)