feat(bugs): add --since/--until time-window filters to bugs list#275
Merged
sachiniyer merged 3 commits intomainfrom May 6, 2026
Merged
feat(bugs): add --since/--until time-window filters to bugs list#275sachiniyer merged 3 commits intomainfrom
sachiniyer merged 3 commits intomainfrom
Conversation
Triage workflows kept piping `--format json` into Python that filtered on `createdAt > now - 1d` to do what `--since 1d` should do natively. Add `--since` and `--until` flags accepting: - durations: 30s, 15m, 24h, 7d, 2w (resolved as `now - duration`) - ISO dates: 2024-01-15 (midnight UTC) - RFC3339 timestamps: 2024-01-15T12:00:00Z The bugs API has no server-side date filter, so we route through the existing `fetch_all_bugs` paginator and filter client-side on `createdAt` — the same shape as `--vulns` / `--introduced-by`. Both flags resolve against a single `now` so a window like `--since 7d --until 1d` reads as one interval. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Manual smoke-test against the live API showed `--since garbage` produced just `Error: Invalid --since value` — anyhow's default Display hides chained context, so the parse-error hint listing accepted forms (durations, ISO date, RFC3339) was lost. Flatten the chain: emit one self-contained message that names the flag and includes the parser's diagnostic, e.g.: Error: invalid --since value: could not parse 'garbage' as a duration (e.g. 1d, 24h), a date (YYYY-MM-DD), or an RFC3339 timestamp Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
99e3862 to
a808e75
Compare
Contributor
Author
This stack of pull requests is managed by Graphite. Learn more about stacking. |
sachiniyer
added a commit
that referenced
this pull request
May 6, 2026
…list (#282) ## Summary The scans list cards already render `Status` and `Scan Type`, but you couldn't filter on either. Triage prompts like "failed scans in the last 24h" had to fall through to grepping `--format json`. Add four filter flags to `scans list`: | Flag | Type | Notes | |----------------|-------------------------------------------------------|-------| | `--status` | `in-progress \| complete \| failed \| dlq` | `WorkflowStatus` | | `--scan-type` | `default \| recent-changes` | `ScanType` | | `--since` | duration / ISO date / RFC3339 | shares `parse_time_spec` with `bugs list` | | `--until` | same as `--since` | resolves against the same `now` so windows read as one interval | `WorkflowStatus` and `ScanType` get `clap::ValueEnum` impls so they work as flag values. ## Implementation The scans API has no server-side filtering today (only `repo_id`/`limit`/`offset`), so any active filter forces an all-fetch path — same shape as the bugs `--vulns` / `--introduced-by` plumbing: paginate server-side to gather every scan, filter the combined set, then re-paginate client-side for output. Bare `scans list` (no filter) still hits the cheap single-page server fetch and is byte-equivalent to before. ## Stacking **This PR is stacked on #275** (`siyer/bugs-list-since-until`) so it can reuse `parse_time_spec`. If #275 merges first, PR6 rebases cleanly; if PR6 lands first, the eventual merge conflict is just "both branches add the same util." ## Testing **Automated, run locally and passing:** - `cargo test` — full suite green. 9 new tests in `src/commands/scans.rs::tests`: - `filter_no_filters_returns_all` - `filter_by_status_failed`, `filter_by_status_complete` - `filter_by_scan_type_default`, `filter_by_scan_type_recent_changes` - `filter_combines_status_and_scan_type_as_and` - `filter_since_inclusive_lower_bound`, `filter_until_inclusive_upper_bound` - `filter_window_with_status` (status + since combined) - `cargo clippy -- -D warnings` — clean - `cargo fmt --check` — clean - `cargo xtask check` — clean (HELP.md regenerated for the new flags) **Manual end-to-end against live API (`usedetail/detail`):** - `scans list … --status complete --limit 5` → `total=124`, every `workflowStatus == "complete"` ✅ - `scans list … --status failed --limit 5` → `total=13`, first item `workflowStatus == "failed"` ✅ - `scans list … --scan-type recent-changes --limit 5` → `total=75`, every `scanType == "recentChanges"` ✅ - `scans list … --status complete --scan-type default --since 7d --limit 5` → `total=1`, both filters satisfied ✅ - `scans list … --since garbage` → `Error: invalid --since value: could not parse 'garbage' as a duration (e.g. 1d, 24h), a date (YYYY-MM-DD), or an RFC3339 timestamp` ✅ - Bare `scans list usedetail/detail --format json --limit 3` (regression) → `total=137, page=1` — single-page server fetch unchanged 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Summary
from datetime import datetime, timedelta, timezone; cutoff = now - timedelta(days=1)againstbugs list --format jsonto do what--since 1dshould do natively.--sinceand--untilflags accepting three forms:30s,15m,24h,7d,2w— resolved asnow - duration2024-01-15— midnight UTC2024-01-15T12:00:00Z(offsets normalize to UTC)fetch_all_bugspaginator and filter client-side oncreatedAt— same shape as--vulns/--introduced-by.now, so--since 7d --until 1dreads as one half-open window.Testing
Automated, run locally and passing:
cargo test— full suite green. New unit tests insrc/utils/datetime.rs:parse_time_spec(13 cases): seconds / minutes / hours / days / weeks; case-insensitive units; zero duration; ISO date; RFC3339; RFC3339 with non-UTC offset normalizes to UTC; negative duration rejected; unknown unit rejected; empty string rejected; unit-only string rejected; whitespace trimming.filter_by_time_range(5 cases) insrc/commands/bugs.rs: no bounds returns all; inclusive lower bound; inclusive upper bound; both bounds clamp to a single bug; inverted window returns empty (we don't error).bugs_list_since_until_parses).cargo clippy -- -D warnings— cleancargo fmt --check— cleancargo xtask check— clean (HELP.md regenerated in c92505c)Manual end-to-end against live API (repo: usedetail/detail, ~305 pending bugs):
--since 1d→ 1 bug (matches the most-recent ingest)--since 7d→ 6 bugs--since 30d→ 17 bugs--since 30d --until 7d→ 11 bugs (== 17 − 6, confirming inclusive-bound semantics on a closed window)--since 2024-01-01→ 305 (all bugs, since none predate that date) — paginated to default 50/page, total_pages=7--since garbage→Error: invalid --since value: could not parse 'garbage' as a duration (e.g. 1d, 24h), a date (YYYY-MM-DD), or an RFC3339 timestamp(last commit, 99e3862, fixed an earlier UX bug where the parse hint was hidden behind anyhow's context chain)bugs list(no--since/--until) → unchanged: hitsclient.list_bugsdirectly, default 50/page🤖 Generated with Claude Code