Skip to main content

Web Market Discovery — convergence and boundaries

Driftless had two information surfaces: the governed warehouse (supplier discovery, opportunity discovery, market history) and targeted Web Evidence, which verifies a claim about an entity someone already named. Neither could answer the question a commercial user actually asks first:
Vendo estructuras metálicas. ¿Quién está construyendo plantas en Nuevo León?
Nobody has named the organization yet. There is no tender, because the project is private. There is no award, because nothing was procured. The signal exists only as something a publisher printed. That is the gap discover_web_market_signals closes, and this document is the boundary around it.

The invariant this change is for

One implementation of the Parallel Search transport, shared by every capability that needs it. Before, radar/adapters/parallel.adapter.ts implemented both DiscoveryProviderPort (FindAll — exhaustive discovery) and EvidenceWebProviderPort (Search). It was registered in two Nest modules, and Nest providers are module-scoped, so there were two objects and two circuit breakers over one upstream: targeted verification could keep calling a provider the Radar had already declared down, and vice versa. The Search half was extracted, not rewritten: research-providers/adapters/parallel-search.adapter.ts. The Radar’s FindAll adapter keeps only the verbs that start a discovery run, and the Radar’s own WebResearchGateway now injects the shared instance through WEB_RESEARCH_PROVIDER instead of re-binding the class. FindAll keeps its own breaker on purpose. A discovery run is minutes long and priced per match; a search is one cheap synchronous call. One shared breaker would let a discovery outage open the circuit on verification — availability coupling between two capabilities that share nothing but a vendor.

What the two web capabilities share, and what they do not

Shared, in exactly one place each: Separate, because the semantics are genuinely different:

Budget and stop conditions

Two successful provider calls per run, shared. Not two each. The run’s ResearchWebSurface owns the ceiling; both capabilities reserve from it, and a refused reservation is still recorded in the audit with its own platform id. A discovery protocol spends at most:
  1. one discovery search, and
  2. one grouped verification call over the top ≤5 candidates.
Stop conditions, all terminal, all checked:
  • the brief is invalid → refuse before spending;
  • the run’s web budget is spent → stop, audited;
  • the provider failed → stop, audited, warehouse untouched;
  • no candidate cleared extraction → stop with an honest zero;
  • target_results reached → verification covers the top slice;
  • verification finished → done.
There is no loop that can run twice. HTTP-level retry inside the adapter still applies to transient failures and is observable through the attempt count; it never re-runs a run-level call. Economic budget today is a call budget. The adapter prices a search and the shared executor refuses anything whose worst case exceeds WEB_SEARCH_MAX_USD_PER_CALL (0.05 USD), so cost is bounded — but the run does not yet accumulate spend across calls. The extension point is EvidenceWebUsage.costUsd, already returned by the adapter and already recorded per attempt; accumulating it into a per-run monetary ceiling is a small change in ResearchWebSurface and deliberately not made here rather than inventing an accounting that nothing consumes.

Deletion ledger

What is NOT reachable from this capability

Enforced mechanically, not by review:
  • FindAll / exhaustive discovery — the Search class does not have the verbs; research-providers.architecture.spec.ts sweeps findall, /v1beta, match_limit, createrun, streamrun, draftfrombrief across the whole layer, adapters included.
  • Contact enrichment / people search — swept in the same spec plus market-research.architecture.spec.ts; the brief refuses people vocabulary, and a planned query naming a person is dropped rather than repaired.
  • A second provider, a second HTTP surface, a second runner, a second synthesis — no controller, no entity, no repository in either layer, and the audit tool vocabulary is closed to the warehouse operations plus exactly two web capabilities.
  • A model-authored URL, id or date — every identifying field is re-derived from the platform’s own ledger, and a candidate citing an id the platform never issued is dropped before it can be rendered.

Contracts

Verification, unchanged and backwards-compatible:
Discovery, new:
additionalProperties: false. There is no provider, processor, mode, raw query list, host allowlist, output schema, enrichment flag, people field, warehouse row or SQL — every one of those is either a vendor concept or an unbounded-spend concept, and the platform owns all of them. A brief that cannot be answered honestly is refused recoverably:

Deduplication and independence

Merging is conservative and never fuzzy. Two candidates merge only when they share a verified domain (the host’s label matches the organization name) or the same normalized name AND the same stated geography.
  • “ABC Industrial” in Monterrey and in Puebla stay two candidates.
  • “Grupo Brisas” and “Grupo Hotelero Brisas” stay two candidates.
  • Two articles about the same project from the same company consolidate, keeping all the evidence, and record the alternate spellings and the merge rule.
Independence collapses syndication: one publisher is one origin however many of its pages came back, and the same headline on three hosts is one origin. A candidate is only promoted to verified when a new origin says something compatible.

Comparing this against Parallel’s Task API, later

Not implemented, and deliberately not integrated on intuition. When we run it:
  • Arm A — this workflow over Search: bounded planning, one discovery call, extraction under a strict schema, dedupe, ranking, one verification call.
  • Arm B — the Task API on the same broad question, same objective, same output criteria.
Score both on the metrics in the live-eval runbook: useful candidates in the top 10, human precision, duplicates, candidates without evidence, fabricated URLs, unresolvable evidence, calls, latency, cost, and lift over warehouse-only. Decision rule, agreed before the numbers exist:
  • if the Search workflow is close enough in quality at materially lower latency and cost, keep it and do not add a second provider surface;
  • if the Task API materially improves depth and coverage on broad questions, adopt it as an explicit deep-research escalation behind its own budget and approval — never as a silent replacement for this protocol.

See also

  • docs/market-data/web-market-discovery-live-eval.md — the runbook. Nothing in it has been run.
  • docs/market-data/web-evidence-live-smoke.md — the verification smoke test.
  • docs/market-data/commercial-intelligence-v1.md — the harness this capability composes into.