fix(movements): read a query's pages as one answer and keep a partial one

- Each page was cached on its own, so a multi-page answer could join
  pages read at different times from a feed that gains rows at the top,
  repeating some rows and missing others. A query's pages are now read
  together and cached as one entry.
- A failed later page threw away the pages already read. They are now
  returned with incomplete saying why, and not cached.
- The library did not cap pages at MAX_PAGES itself; it clamps now.
- The hint said to raise --pages even at 10; at 10 it says to narrow the
  dates. -o jsonl, which streams rows only, gives the hint on stderr.
- A duplicate match arm in the CLI was dead; it is gone.

Claude-Session: https://claude.ai/code/session_01RqPPVeVDrnLoP7NUZmLRxY
This commit is contained in:
Rasyidan Akbar F. 2026-11-04 17:39:10 +07:00
commit 0c7320a343
11 changed files with 159 additions and 54 deletions

View file

@ -271,6 +271,10 @@ impl ShareholderMovementProvider for MockStockbitProvider {
page: u32,
) -> Result<MovementPage, IdxError> {
let subject = query.symbol.as_deref().unwrap_or("shareholder movements");
// `IDX_MOCK_MOVEMENTS_FAIL_PAGE=N` rate-limits page N alone.
if std::env::var("IDX_MOCK_MOVEMENTS_FAIL_PAGE").ok() == Some(page.to_string()) {
return Err(IdxError::RateLimited);
}
let mut name = "majorholder".to_string();
if let Some(symbol) = &query.symbol {
name.push('_');