Skip to content

bug: Avoid redundant article fetches for incomplete PR Newswire windows #144

Description

@keli-wen

🐛 Bug Description

PR Newswire collection fetches every discovered article before returning that the listing window is incomplete. A downstream caller that bisects incomplete windows therefore discards an expensive parent result and downloads the same articles again. Article fetching is the measured bottleneck, and its per-host concurrency is currently fixed in the private fetch policy rather than configurable through the public NewsCollectionCfg.

🔄 Steps to Reproduce

  1. Call collect_news(NewsWindow(source="pr-newswire", start=..., end=...)) with a window whose listing exceeds the current 20-page × 100-row discovery budget.
  2. Observe that _discover_with_fetcher reaches the page budget and produces complete=false.
  3. Observe that _collect_pr_newswire still runs asyncio.gather over every discovered observation before returning the incomplete NewsBatch.
  4. Have a completeness-preserving caller bisect the incomplete window and retry its children; the parent articles are fetched again because the incomplete parent documents cannot prove full coverage.

✅ Expected Behavior

An incomplete discovery result should be returned before expensive article fetching, or the public operation should otherwise provide a reusable staged result that prevents callers from downloading the same articles again during bisection. The public collection config should also provide a validated way to tune bounded per-host article-fetch concurrency for live-source workloads.

❌ Actual Behavior

The collector can download up to 2,000 article bodies at the fixed per-host concurrency of 2, then return complete=false. A caller that correctly retries smaller windows repeats those article downloads. In LLMQuant Data PR #696, a 14-day recovery window exposed this behavior; the downstream mitigation is to prepartition the requested range into windows of at most 24 hours before retaining the existing fail-closed bisection.

📋 Environment

  • QuantMind Version: git aafa8904
  • Python Version: 3.11.13
  • Operating System: macOS 15.6 arm64
  • Installation Method: uv, git-pinned dependency from source

📝 Additional Context

A read-only benchmark used the fixed window 2026-08-06T12:00:00Z → 2026-08-06T14:00:00Z with 252 observed articles. Each run returned 252 documents, complete=true, and zero failures. Wall times were: concurrency 2 = 57.279s; concurrency 4 = 44.237s and 92.277s; concurrency 8 = 55.562s and 48.548s. This shows that 8 was stable in both samples, while source latency remained variable; the benchmark changed the private _DEFAULT_FETCH_POLICY only for measurement and is not a suitable production integration.

Error Logs

No request failures were observed in the bounded benchmark. The defect is redundant successful article fetching, not an exception.

Configuration

listing_page_size: 100
listing_max_pages: 20
current_max_concurrency_per_host: 2
downstream_recovery_max_window_seconds: 86400

🛠️ Possible Solution

Options for maintainer discussion: separate discovery completeness from article collection; return immediately without article fetching when discovery is incomplete; or expose a reusable staged discovery artifact. Independently, add a validated public NewsCollectionCfg field for bounded per-host fetch concurrency and thread it through collect_news to the source fetch policy instead of requiring callers to touch private module state.


Checklist

  • I have searched existing issues to avoid duplicates
  • I have provided a clear and concise description
  • I have included steps to reproduce the issue
  • I have included my environment information
  • I have included relevant error logs or screenshots

Metadata

Metadata

Assignees

Labels

area: preprocessDeterministic acquisition, parsing, cleaning, and source handlingimpact: live-networkChanges or depends on real public-network behavior or smoke teststype: bugExisting behavior violates its contract or documented expectation

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions