Enable TraceMap to discover source candidates from a research topic even when the user does not provide manual URLs, while preserving the existing Source Intake / Fetch / Cache / Provider pipeline.
- Users can start an investigation from a plain research topic and still get evidence candidates.
- Manual URLs remain first-class and prioritized when present.
- Discovery can evolve from mock to real search providers without breaking provider integration.
- Add
SourceDiscoveryProviderboundary for pluggable source discovery. - Add
disabledandmockdiscovery providers. - Add discovery hook from research topic (
question) in source intake. - Merge manual URLs and discovered URLs into one normalized, deduplicated intake list.
- Reuse existing source cache / fetch pipeline (
resolveSourceCacheForUrl). - Pass resulting
sourceCandidatesto answer graph providers.
- Production external search API integration.
- RAG / embeddings / reranking.
- Background job orchestration.
- DB schema changes or Prisma migrations.
- Large UI redesign.
- Major OpenAI answer graph schema changes.
- Full-text crawling.
- Dedicated persistence table for search result history.
- Keep
AnalysisRun.questionand form fieldquestionunchanged. - Preserve existing Source Intake behavior for manual URLs.
- Keep run completion flow valid even when source discovery fails or yields no candidates.
- Do not bypass existing URL safety validation.
- Environment variable switch:
TRACEMAP_SOURCE_DISCOVERY_PROVIDER=disabled|mock. - Default is
disabled. mockprovider must return deterministic results from the research topic.- Provider boundary is designed to allow future providers (e.g. web search backends) without changing intake contracts.
- Extract manual URLs from research topic.
- Resolve source discovery provider.
- If provider is enabled, discover additional URLs from the same topic.
- Merge manual + discovered URLs (manual first).
- Normalize, safety-check, dedupe by normalized URL.
- Resolve cache/fetch metadata with
resolveSourceCacheForUrl. - Build
SourceCandidate[]and pass to answer graph provider input.
- Deduplicate by normalized URL.
- Manual URL candidates are evaluated before discovered candidates.
- When duplicates exist, keep first occurrence (manual precedence).
- Do not fetch or process the same normalized URL more than once.
- Discovery provider failures do not fail the run.
- Per-URL cache/fetch failures do not fail the run.
- Discovery and URL failures are captured in
ignoredUrlswith reasons. - Avoid excessive logging; never log secrets.
- Discovery outputs are treated as untrusted input.
- All discovered URLs must pass existing normalization and safety checks.
- Unsafe URLs are ignored and recorded, not fetched.
- Existing SSRF guard behavior remains authoritative.
DEFAULT_DISCOVERY_MAX_RESULTS = 5.DEFAULT_SOURCE_CANDIDATE_MAX_RESULTS = 5.- Keep provider source context compact (no raw full HTML).
- Keep excerpt truncation behavior unchanged in answer graph providers.
- Provider resolution defaults to disabled.
- Disabled provider yields no discovered candidates.
- Mock provider is deterministic and respects maxResults.
- Intake integration preserves manual URL-only behavior.
- Discovery can produce candidates when no manual URLs exist.
- Manual URLs are prioritized over discovered URLs.
- Duplicate URLs are deduplicated.
- Discovery failure does not fail intake.
- Unsafe discovered URLs are ignored.
- Provider integration remains valid when sourceCandidates is empty or discovered.
acceptance/source-discovery.featureacceptance/source-intake-and-fetching.feature