Skip to content

feat(bugs): add --since/--until time-window filters to bugs list#275

Merged
sachiniyer merged 3 commits intomainfrom
siyer/bugs-list-since-until
May 6, 2026
Merged

feat(bugs): add --since/--until time-window filters to bugs list#275
sachiniyer merged 3 commits intomainfrom
siyer/bugs-list-since-until

Conversation

@sachiniyer
Copy link
Copy Markdown
Contributor

@sachiniyer sachiniyer commented May 5, 2026

Summary

  • Triage transcripts kept hand-rolling from datetime import datetime, timedelta, timezone; cutoff = now - timedelta(days=1) against bugs list --format json to do what --since 1d should do natively.
  • Add --since and --until flags accepting three forms:
    • Duration: 30s, 15m, 24h, 7d, 2w — resolved as now - duration
    • ISO date: 2024-01-15 — midnight UTC
    • RFC3339: 2024-01-15T12:00:00Z (offsets normalize to UTC)
  • 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 — same shape as --vulns/--introduced-by.
  • Both flags resolve against a single now, so --since 7d --until 1d reads as one half-open window.

Testing

Automated, run locally and passing:

  • cargo test — full suite green. New unit tests in src/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) in src/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).
    • 1 clap-parse test (bugs_list_since_until_parses).
  • cargo clippy -- -D warnings — clean
  • cargo fmt --check — clean
  • cargo 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 garbageError: 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)
  • Bare bugs list (no --since/--until) → unchanged: hits client.list_bugs directly, default 50/page

🤖 Generated with Claude Code

@sachiniyer sachiniyer temporarily deployed to integration-tests May 5, 2026 18:18 — with GitHub Actions Inactive
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

No issues found across 3 files

sachiniyer and others added 3 commits May 6, 2026 15:38
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>
@sachiniyer sachiniyer force-pushed the siyer/bugs-list-since-until branch from 99e3862 to a808e75 Compare May 6, 2026 22:39
@sachiniyer sachiniyer temporarily deployed to integration-tests May 6, 2026 22:39 — with GitHub Actions Inactive
Copy link
Copy Markdown
Contributor Author

@sachiniyer sachiniyer merged commit 453cfec into main May 6, 2026
13 checks passed
@sachiniyer sachiniyer deleted the siyer/bugs-list-since-until branch May 6, 2026 23:02
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>
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.

1 participant