Skip to content

fix: correct multi-status pagination in bugs list#301

Merged
sachiniyer merged 2 commits into
mainfrom
devin/1779259904-fix-multi-status-pagination
May 20, 2026
Merged

fix: correct multi-status pagination in bugs list#301
sachiniyer merged 2 commits into
mainfrom
devin/1779259904-fix-multi-status-pagination

Conversation

@devin-ai-integration
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot commented May 20, 2026

Summary

Fixes #300.

fetch_page_multi_status applied a shared offset to every per-status API call, then truncated the concatenated results to limit. This made bugs from later statuses permanently unreachable on page 2+ because their per-status offset skipped items that were only truncated away (never shown) on page 1.

Before: each status queried with (limit, offset) → concatenated → truncated to limit.
After: each status queried with (offset + limit, 0) → concatenated → drain first offset items → truncate to limit.

This ensures the pagination window is applied over the combined result set rather than per-status, so every bug is reachable regardless of status ordering.

Review & Testing Checklist for Human

  • Verify with a repo that has bugs in multiple statuses (e.g. 50+ pending, 40+ resolved): run detail bugs list --status pending --status resolved --limit 50 --page 1 and --page 2 and confirm all bugs appear across pages with no gaps
  • Check that single-status bugs list pagination is unaffected (this code path is only used for multi-status queries)
  • Confirm --page 1 still returns the correct first page of results

Notes

The fix uses saturating_add for offset + limit to avoid overflow on extreme page/limit values.

Link to Devin session: https://app.devin.ai/sessions/7a885031946844ed914b5740feff94fa
Requested by: @sachiniyer


Open in Devin Review

Summary by cubic

Fixes multi-status pagination in bugs list so pages are built over the combined results, making bugs from later statuses reachable on page 2+.

  • Bug Fixes
    • Fetch each status from offset 0 up to offset + limit, paginating in chunks to respect the API limit; merge, drop the first offset, then take limit.
    • Use saturating_add to prevent overflow on large page/limit values.

Written for commit 1002b2e. Summary will update on new commits. Review in cubic

fetch_page_multi_status previously applied the same shared offset to each
status query and then truncated the concatenated results.  This caused bugs
from later statuses to be permanently unreachable on page 2+ because the
per-status offset skipped items that were truncated away on page 1.

Now each status is fetched from offset 0 with a limit of offset+limit, the
results are combined, the first offset items are drained, and the remainder
is truncated to limit.  This ensures every bug is reachable through
pagination regardless of status ordering.

Closes #300

Co-Authored-By: Sachin Iyer <siyer@detail.dev>
@devin-ai-integration
Copy link
Copy Markdown
Contributor Author

Original prompt from Sachin

can you look at this github issue - #300

@devin-ai-integration
Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

Copy link
Copy Markdown
Contributor Author

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 3 potential issues.

Open in Devin Review

Comment thread src/commands/bugs.rs Outdated
Comment thread src/commands/bugs.rs
Comment thread src/commands/bugs.rs Outdated
Comment on lines 406 to 415
/// Fetch a single page of bugs across multiple `statuses`, concatenated in
/// order and then truncated to `limit` items.
/// order and then paginated from the combined result.
///
/// Each status is queried with the full `limit` and a shared `offset`
/// derived from `page` so that every status gets a fair chance to
/// contribute results regardless of how many bugs it contains. The merged
/// list is then truncated to at most `limit` items, preserving the
/// page-size contract.
/// Each status is queried starting at offset 0 with a fetch limit of
/// `offset + limit` so that the full combined window is available. The
/// merged list is then advanced past `offset` items and truncated to at
/// most `limit` items, ensuring every bug is reachable across pages.
///
/// Unlike `fetch_all_bugs_multi_status` this does NOT exhaust every page —
/// it issues one bounded request per status and merges the results, keeping
Copy link
Copy Markdown
Contributor Author

@devin-ai-integration devin-ai-integration Bot May 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: Performance regression on later pages for multi-status queries

The new fetch_page_multi_status computes fetch_limit = offset.saturating_add(limit) and fetches that many items per status starting from offset 0 (src/commands/bugs.rs:466). For page N with limit L, this means fetching up to N×L items per status instead of just L. For example, page 10 with limit 50 fetches up to 500 items per status (5 API calls each at BUG_PAGE_SIZE=100), compared to 1 API call per status with the old approach. This is a necessary tradeoff for correctness — the old approach silently dropped bugs — but could be noticeable on repos with many thousands of bugs across multiple statuses at high page numbers. The CLI's --limit cap of 100 and typical usage patterns (most users view page 1–3) mitigate this in practice.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed on the performance concern for deep pages. In practice, the CLI's --limit is capped at 100 and most users paginate through the first few pages, so fetch_limit stays small. For truly deep pages (e.g. page 100), the number of round-trips approaches what fetch_all_bugs_multi_status would do anyway. A fallback threshold could be added in a follow-up if needed, but keeping it simple for now.

cubic-dev-ai[bot]

This comment was marked as resolved.

The initial fix passed offset+limit as the API limit parameter, which can
exceed the server maximum of 100 on deeper pages.  Introduce
fetch_bugs_up_to which paginates each status in BUG_PAGE_SIZE (100) chunks,
collecting up to offset+limit items before applying the combined
drain+truncate.

Also add simulation tests that reproduce the original issue (#300) and
verify the new pagination returns all bugs across pages.

Co-Authored-By: Sachin Iyer <siyer@detail.dev>
Copy link
Copy Markdown
Contributor Author

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 new potential issue.

Open in Devin Review

Comment thread src/commands/bugs.rs
Comment on lines 468 to +473
let mut total: usize = 0;
for status in dedupe_statuses(statuses) {
let response = client
.list_bugs(repo_id, status, limit, offset, scan_id)
.await
.context("Failed to fetch bugs from repository")?;
total += usize::try_from(response.total.max(0)).unwrap_or(0);
combined.extend(response.bugs);
let (bugs, status_total) =
fetch_bugs_up_to(client, repo_id, status, fetch_limit, scan_id).await?;
total += status_total;
combined.extend(bugs);
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: total count remains a sum of per-status server totals, unaffected by combined pagination window

The total returned by fetch_page_multi_status (src/commands/bugs.rs:472) is the sum of each status's API-reported total, which is correct for computing total_pages in output_list. However, this total represents the sum of all bugs across queried statuses, while the page window is applied to the concatenated list. If a user pages through all results, they'll see exactly total bugs — the math is consistent. Worth noting: fetch_bugs_up_to overwrites its local total on each API page (src/commands/bugs.rs:432) rather than accumulating, which is correct because the API's total field is the server-side count for that status (constant across pages), not a per-page count.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@devin-ai-integration
Copy link
Copy Markdown
Contributor Author

Test Results

Tested the built CLI binary from PR branch against the live Detail API and ran all unit/integration tests. Devin session

Live E2E: Multi-status pagination (primary test)

Ran detail bugs list usedetail/cli --status pending --status resolved --limit 10 across all pages:

=== Page Summary ===
  Page 1: 10 items (total=36)
  Page 2: 10 items (total=36)
  Page 3: 10 items (total=36)
  Page 4: 6 items (total=36)

Total unique IDs collected: 36
Total IDs collected:       36
No duplicates found
Expected total: 36
Match: True

Page 5 correctly returned 0 items. All 36 bugs reachable across pages with zero duplicates. This is the exact scenario that would have failed with the old code (bugs from later statuses dropped on page 2+).

Unit test simulations (6 tests)
  • old_pagination_drops_resolved_bugs_issue_300 — PASSED (confirms old algorithm drops bugs: 50/90 found, 0 resolved)
  • new_pagination_returns_all_bugs_issue_300 — PASSED (new algorithm finds all 90: 50 pending + 40 resolved)
  • new_pagination_page1_returns_first_limit_bugs — PASSED (page 1 = 50 bugs, all pending)
  • new_pagination_page2_returns_remaining_bugs — PASSED (page 2 = 40 bugs, all resolved)
  • new_pagination_even_split_across_pages — PASSED (30+30 split: page 1 has 30 pending + 20 resolved, page 2 has 10 resolved)
  • new_pagination_past_last_page_is_empty — PASSED
Regression: Full test suite

295 unit tests + 18 integration tests — 0 failures.

@sachiniyer sachiniyer merged commit 03c9dab into main May 20, 2026
14 checks passed
@sachiniyer sachiniyer deleted the devin/1779259904-fix-multi-status-pagination branch May 20, 2026 07:44
sachiniyer added a commit that referenced this pull request May 21, 2026
## Summary
Patch release v0.2.4. Bumps version in `Cargo.toml` from 0.2.3 → 0.2.4.

Changes since v0.2.3:
- fix: correct multi-status pagination in `bugs list` (#301)
- style: ban inline imports and hoist all use statements to module level
(#299)

## Review & Testing Checklist for Human
- [ ] Verify `Cargo.toml` and `Cargo.lock` version is `0.2.4`
- [ ] Confirm CI passes (fmt, clippy, tests, vendored artifact checks)

### Notes
Q/A testing was performed before cutting this release — all features
verified working:
- `detail version`, `detail auth status`, `detail repos list`
- `detail bugs list` with multi-status, time filters, author filters,
scan-id, JSON output
- `detail bugs show` (table and JSON formats)
- `detail bugs close` / `detail bugs reopen` lifecycle
- `detail scans list` with status/time filters
- `detail rules list`, `detail rules requests list`
- `detail completions bash`
- Error handling (invalid bug IDs)

![QA Test Recording](home/ubuntu/screencasts/qa-test/qa-test-edited.mp4)

Link to Devin session:
https://app.devin.ai/sessions/b5a6272c900f4fc58425f4c555bec1e6
Requested by: @sachiniyer
<!-- devin-review-badge-begin -->

---

<a href="https://app.devin.ai/review/usedetail/cli/pull/302"
target="_blank">
  <picture>
<source media="(prefers-color-scheme: dark)"
srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1">
<img
src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1"
alt="Open in Devin Review">
  </picture>
</a>
<!-- devin-review-badge-end -->

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Bump `detail-cli` to v0.2.4 for a patch release. Includes a fix for
multi-status pagination in `bugs list` and a style cleanup that hoists
inline imports to module scope.

<sup>Written for commit 76dbbb9.
Summary will update on new commits. <a
href="https://cubic.dev/pr/usedetail/cli/pull/302?utm_source=github">Review
in cubic</a></sup>

<!-- End of auto-generated description by cubic. -->

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Sachin Iyer <siyer@detail.dev>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Detail Bug] CLI: bugs list --status ... pagination drops bugs when multiple statuses are requested

1 participant