Parent
#46
What to build
Cutover the HttpSpoof path of Crawler::process_job from inline dispatch to runner.run(&job, &ctx).await. The SessionContext constructed in C3 is now consumed: the runner picks the spoof fetcher (held internally), runs fetch → ChallengeDetector-or-Fingerprinter → extract, and returns a JobOutcome. Crawler reads outcome.result, outcome.error, outcome.signals, outcome.retry, and continues with the existing post-processing (storage, frontier feed, retry decision honoring caps and cooldowns, session-state commit, run-level events).
The Render path stays inline for this PRD's scope. The Auto path delegates to spoof internally (per ADR-0002), so this cutover covers HttpSpoof + Auto via one code path.
Per-attempt events (JobStarted, FetchCompleted, ChallengeDetected, ExtractCompleted, JobFailed) now fire on the production NDJSON wire from inside the runner. The regression test from issue #16 is the byte-for-byte trip wire.
Acceptance criteria
Blocked by
Parent
#46
What to build
Cutover the HttpSpoof path of
Crawler::process_jobfrom inline dispatch torunner.run(&job, &ctx).await. TheSessionContextconstructed in C3 is now consumed: the runner picks the spoof fetcher (held internally), runs fetch → ChallengeDetector-or-Fingerprinter → extract, and returns aJobOutcome. Crawler readsoutcome.result,outcome.error,outcome.signals,outcome.retry, and continues with the existing post-processing (storage, frontier feed, retry decision honoring caps and cooldowns, session-state commit, run-level events).The Render path stays inline for this PRD's scope. The Auto path delegates to spoof internally (per ADR-0002), so this cutover covers HttpSpoof + Auto via one code path.
Per-attempt events (
JobStarted,FetchCompleted,ChallengeDetected,ExtractCompleted,JobFailed) now fire on the production NDJSON wire from inside the runner. The regression test from issue #16 is the byte-for-byte trip wire.Acceptance criteria
Crawler::process_jobHttpSpoof path replaces the inline spoof block withrunner.run(&job, &ctx).awaitrunner.run(per ADR-0002 — AutoFetcher would chain inline, which we don't do)cargo test --all-features --test runner_ndjson_regression: byte-stable — same event-kind sequencecargo test --all-features --test runner_integration: 4/4 still passcargo test --all-features --lib --no-fail-fast: zero new failures vs mainsrc/crawler.rs(target: drop measurably below 3600)tests/continue to passBlocked by