Skip to content

[UP-4693] Relative time filters return no results despite matching data - #1975

Merged
jan-lewandoski-blazity merged 12 commits into
devfrom
blazebot/up-4693
Jul 28, 2026
Merged

[UP-4693] Relative time filters return no results despite matching data#1975
jan-lewandoski-blazity merged 12 commits into
devfrom
blazebot/up-4693

Conversation

@arthur-blazity-ai-workflow

@arthur-blazity-ai-workflow arthur-blazity-ai-workflow Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

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

  • Sorting and filtering now happen server-side. The backend owns the ordering and the activity filter, so results and counts are correct regardless of how many tasks exist or how many pages are loaded.
  • The "Active in last N days" filter now uses real activity (most recent trace), not the task's last-modified time — so it means what it says.
  • "Last active" on the card is now accurate. It reflects a task's true most-recent activity, so a task active within the selected range is no longer mislabeled "Inactive."

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

@claude

claude Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Code review

No issues found. Checked for bugs and CLAUDE.md compliance.

@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Coverage

Tests Skipped Failures Errors Time
1958 7 💤 0 ❌ 0 🔥 17m 48s ⏱️

@Encoura-Lewandoski

Copy link
Copy Markdown

Does current implementation differ from #1936 you closed? Make sure you addressed all comments from #1936.

arthur-blazity-ai-workflow Bot added a commit that referenced this pull request Jul 23, 2026
…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>
@jan-lewandoski-blazity

jan-lewandoski-blazity commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Revert previous changes and re-consider the implementation with the following context:

Tasks have two separate timestamps:

  • updated_at: when the task record was last modified (created, rules edited, archived).
  • last_active: the last time the task actually received traffic, i.e. its most recent trace. Null if it has no traces. This one isn't stored on the task; it's computed from trace data by a separate endpoint (POST /api/v1/traces/overview).

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.

@alwaysmeticulous

alwaysmeticulous Bot commented Jul 23, 2026

Copy link
Copy Markdown

✅ 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 fc0846b Merge branch 'dev' into blazebot/up-4693. This comment will update as new commits are pushed.

@jan-lewandoski-blazity jan-lewandoski-blazity left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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 jan-lewandoski-blazity left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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_active into 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.

jan-lewandoski-blazity pushed a commit that referenced this pull request Jul 24, 2026
…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>
@jan-lewandoski-blazity
jan-lewandoski-blazity force-pushed the blazebot/up-4693 branch 3 times, most recently from 9297111 to 847cfdd Compare July 24, 2026 12:05
@jan-lewandoski-blazity jan-lewandoski-blazity changed the title [UP-4693] [BE] Relative time filters return no results despite matching data [UP-4693] Relative time filters return no results despite matching data Jul 24, 2026
jan-lewandoski-blazity pushed a commit that referenced this pull request Jul 24, 2026
…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>
Comment thread genai-engine/src/repositories/tasks_repository.py Outdated


@pytest.fixture
def repo_env():

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I added conftest.py under tests/unit/repositories/ to expose the client fixture and adjusted it according to your recommendation 👍

Comment thread genai-engine/tests/unit/routes/tasks/test_task_search_endpoints.py
arthur-blazity-ai-workflow Bot and others added 6 commits July 27, 2026 09:03
…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>
Comment thread genai-engine/src/repositories/tasks_repository.py Outdated
…(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>
@jan-lewandoski-blazity
jan-lewandoski-blazity enabled auto-merge (squash) July 28, 2026 06:37
@jan-lewandoski-blazity
jan-lewandoski-blazity merged commit 53cf1d5 into dev Jul 28, 2026
25 checks passed
@jan-lewandoski-blazity
jan-lewandoski-blazity deleted the blazebot/up-4693 branch July 28, 2026 08:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

4 participants