[UP-4693] Relative time filters return no results despite matching data - #1975
Conversation
Code reviewNo issues found. Checked for bugs and CLAUDE.md compliance. |
…on convention (UP-4693) Addresses PR #1936 review comments carried over into PR #1975. - Archiving/unarchiving a task now bumps updated_at (tasks.updated_at has no onupdate= default, so set it explicitly), so a task archived/unarchived within the window is returned by "Recently Updated" relative time filters even when its created_at is outside the window. - Add regression test asserting updated_at advances and the task is returned via sort_field=UPDATED after both archive and unarchive. - Document in genai-engine/CLAUDE.md that API wire-format schema types/enums (e.g. TaskSortField) must live in src/schemas/ and be imported by repositories/routes, with TaskSortField in schemas/enums.py as the canonical example. Enum placement (item 1) verified already correct. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Revert previous changes and re-consider the implementation with the following context: Tasks have two separate timestamps:
The "Active in last 7/14 days" dropdown on the tasks dashboard reads updated_at, not last_active. That's the mismatch. In the video attached to UP-4693, the task shows "Active 16 hours ago" but doesn't appear under "Active in last 7 days", because its updated_at is old even though it had a recent trace. The filter also runs frontend-side right now, over whatever tasks are already loaded. To fix it we need a backend change: /api/v2/tasks/search should accept a last_active filter, and the frontend should pass it. One heads-up on scope: last_active isn't a column on the tasks table, so the search query has to join and aggregate the trace-metadata table (max trace end-time per task) to filter on it. Not a one-line param add. Default with no param stays as-is: show everything, including inactive tasks and all ranges. Also, change the sorting logic so that it's server-side. By default (when no sort param is sent) it should work as previously. |
|
✅ Meticulous spotted visual differences in 23 of 853 screens tested, but all differences have already been approved: view differences detected. Meticulous evaluated ~7 hours of user flows against your PR. Last updated for commit |
jan-lewandoski-blazity
left a comment
There was a problem hiding this comment.
Fix: task-list empty state shows onboarding when a filter hides everything
The "Active in last N days" filter now runs server-side (last_active_start_time), so tasks is the already-filtered list. When the filter matches nothing, the onboarding empty state ("No tasks found / Get started by creating your first agent task") wrongly fires and hides the filter toolbar, trapping the user with no way to change the range.
jan-lewandoski-blazity
left a comment
There was a problem hiding this comment.
Fix: task cards show "Inactive" for tasks the filter counts as active
In the tasks list (React + TanStack Query frontend), the "Active in last N days" filter runs server-side against each task's real last activity, and offers 7 / 14 / 30-day windows. But the task card metrics (Traces, Tokens, Success rate, and "Last active") all come from one call to the trace-overview endpoint whose lookback window is hardcoded to 7 days.
The bug: with the "Active in last 30 days" filter, a task active ~21 days ago correctly appears in the list, but its card reads "Last active: Inactive" — because the overview's hardcoded 7-day window doesn't see the 21-day-old activity. The filter window (up to 30 days, plus "All time") and the card window (fixed 7 days) disagree.
Do NOT just widen the hardcoded window — that would also change what the metric tiles (Traces/Tokens/Success) summarize, and it still breaks for "All time". Keep the tiles on their 7-day window.
Fix: add a second, separate call to the same trace-overview endpoint using an unbounded lookback (start = epoch/Unix 0, end = now), and use only its last_active for the card's "Last active" field. A trace can't predate its task, so an unbounded window equals "since the task was created," giving the true most-recent activity regardless of the metrics window.
Concretely:
- Extend the overview hook to accept an option (e.g.
sinceCreated) that swaps the 7-day start for epoch, with a distinct query key so both calls cache independently. Default behavior unchanged. - In the tasks-list component, keep the existing 7-day overview call for the tiles, add the unbounded call, and pass its
last_activeinto each card as an override prop. - In the card, prefer the override for "Last active", falling back to the 7-day value. Leave the metric tiles (and their "last 7 days" labels) untouched.
No backend change. After: a task active 21 days ago shows "21 days ago" on its card, a never-active task still shows "Inactive", and the tiles still reflect the last 7 days. Run the frontend's type-check/lint before finishing.
…on convention (UP-4693) Addresses PR #1936 review comments carried over into PR #1975. - Archiving/unarchiving a task now bumps updated_at (tasks.updated_at has no onupdate= default, so set it explicitly), so a task archived/unarchived within the window is returned by "Recently Updated" relative time filters even when its created_at is outside the window. - Add regression test asserting updated_at advances and the task is returned via sort_field=UPDATED after both archive and unarchive. - Document in genai-engine/CLAUDE.md that API wire-format schema types/enums (e.g. TaskSortField) must live in src/schemas/ and be imported by repositories/routes, with TaskSortField in schemas/enums.py as the canonical example. Enum placement (item 1) verified already correct. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
9297111 to
847cfdd
Compare
…on convention (UP-4693) Addresses PR #1936 review comments carried over into PR #1975. - Archiving/unarchiving a task now bumps updated_at (tasks.updated_at has no onupdate= default, so set it explicitly), so a task archived/unarchived within the window is returned by "Recently Updated" relative time filters even when its created_at is outside the window. - Add regression test asserting updated_at advances and the task is returned via sort_field=UPDATED after both archive and unarchive. - Document in genai-engine/CLAUDE.md that API wire-format schema types/enums (e.g. TaskSortField) must live in src/schemas/ and be imported by repositories/routes, with TaskSortField in schemas/enums.py as the canonical example. Enum placement (item 1) verified already correct. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3ef8ecd to
1739929
Compare
|
|
||
|
|
||
| @pytest.fixture | ||
| def repo_env(): |
There was a problem hiding this comment.
is there a reason we can't use the GenaiEngineTestClientBase like other test files to create/delete tasks and other items? I think maybe DatabaseTraceMetadata you might have to insert directly but tasks you should be able to use the client
There was a problem hiding this comment.
I added conftest.py under tests/unit/repositories/ to expose the client fixture and adjusted it according to your recommendation 👍
…693) The All Tasks listing filtered/ordered only on tasks.created_at, so a task created before the window but updated within it was excluded (it also sat deep in the created_at ordering and was never loaded into the paginated client-side view). Introduce a TaskSortField enum as the single source of truth mapping the selected mode to the timestamp column, and resolve that column for BOTH the time-range predicate (start_time/end_time) and the ORDER BY in TaskRepository.query_tasks. 'updated' -> updated_at (Recently updated), 'created' -> created_at (Recently created, also the safe default when unset). Expose sort_field/start_time/end_time as query params on POST /api/v2/tasks/search (SearchTasksRequest is pinned in external arthur_common). Add repository + endpoint regression tests and update the API changelog / OpenAPI baseline. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…on convention (UP-4693) Addresses PR #1936 review comments carried over into PR #1975. - Archiving/unarchiving a task now bumps updated_at (tasks.updated_at has no onupdate= default, so set it explicitly), so a task archived/unarchived within the window is returned by "Recently Updated" relative time filters even when its created_at is outside the window. - Add regression test asserting updated_at advances and the task is returned via sort_field=UPDATED after both archive and unarchive. - Document in genai-engine/CLAUDE.md that API wire-format schema types/enums (e.g. TaskSortField) must live in src/schemas/ and be imported by repositories/routes, with TaskSortField in schemas/enums.py as the canonical example. Enum placement (item 1) verified already correct. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ting (UP-4693) Reworks the tasks-search relative-time filter per PR review. The dashboard's "Active in last N days" now filters on last_active (MAX(trace_metadata.end_time) per task) instead of tasks.updated_at, fixing tasks that show "Active X ago" but were missing from the window because their record timestamp was old. Backend (/api/v2/tasks/search): - New TaskSortField enum (name/created_at/updated_at/last_active) in schemas/enums. - query_tasks accepts last_active_start_time/last_active_end_time and sort_field. last_active is computed via a GROUP BY subquery over trace_metadata (org-scoped when a tenant scope is provided): INNER join when filtering (excludes trace-less tasks), LEFT join with NULLS LAST when only sorting. No params => unchanged created_at ordering and full task set. - Route exposes the inputs as optional query params; sort direction reuses the existing pagination `sort` param. Frontend: - useTasksList forwards sort_field/sort/last_active_start_time; AllTasks drives the sort dropdown and "Active in last N days" through the request (server-side), dropping the client-side sort and updated_at filter. Docs/spec: regenerated staging.openapi.json and added an api_changelog entry. Tests: repository + endpoint coverage for last_active filtering, ordering by each sort field (both directions), default-order parity, and org scoping. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ing empty state (UP-4693) Backend (types only, no behavior change): split the trace-metadata aggregate into a distinct query var and its .subquery() result so the Subquery is no longer assigned to a RowReturningQuery-typed variable, and use the subquery for all .c column access and join/outerjoin calls. Annotate the sort column as Any so it can hold Task columns or the subquery's last_active column. Clears all 9 mypy errors. Frontend: the 'Active in last N days' filter runs server-side, so an empty result from a non-default range no longer trips the onboarding empty state. Derive the applied-filter flag from the current search/range state instead of the returned tasks length, and only show onboarding when no filter and the default range are active; otherwise keep the toolbar/sort dropdown and show a neutral empty message. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1739929 to
54e8e1c
Compare
…(UP-4693) Addresses PR review nit: the ordering block's comment restated behavior already evident from the code. Keep only the note justifying the `Any` annotation (different column kinds across branches, which mypy rejects). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
UP-4693
Fix task list sorting and "Active in last N days" filtering
Problem
Sorting and the "Active in last N days" filter on the All Tasks page were buggy. Both ran client-side, over only the tasks already loaded in the browser, so results were unreliable once the list grew beyond one page — matching tasks could silently be missing or out of order.
The filter also didn't reflect real activity. It matched on when a task record was last modified, not when it was last active, so tasks with no traffic could show up under an "Active" filter, and a genuinely active task could be filtered out.
On top of that, a task card could show "Last active: Inactive" even when the task had recent activity, because the card only ever looked at a fixed 7-day window that didn't match the selected filter range.
What changed
Result
Sorting behaves consistently, the activity filter returns the right tasks, and the "Last active" label on each card matches reality.
Preview:
Screen.Recording.2026-07-24.at.13.15.33.mov