Skip to content

perf(tasks): summary-first /tasks/browse selection with bounded previews - #1191

Merged
kyle-compute merged 7 commits into
stagingfrom
perf/tasks-browse-cutover
Aug 12, 2026
Merged

perf(tasks): summary-first /tasks/browse selection with bounded previews#1191
kyle-compute merged 7 commits into
stagingfrom
perf/tasks-browse-cutover

Conversation

@kyle-compute

@kyle-compute kyle-compute commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator

Why this PR exists

Split structure and merge order

  1. perf(tasks): maintain task browse summaries on trial writes #1185 — summary maintenance: model, helpers, write-through refresh hooks.
  2. perf(tasks): repopulate historical task browse summary rows #1190 — historical population: migration task_browse_summary_002.
  3. perf(tasks): summary-first /tasks/browse selection with bounded previews #1191 (this PR) — backend read cutover. 452 application-code lines.
  4. perf(frontend): render bounded task-card trial preview #1192 — frontend card cap.

Exact screen, request, and frequency

  • Screen: the authenticated main task-card grid at /tasks.

Screenshot for area that is targeted

image
  • Controlling browser request: GET /api/tasks/browse.
  • Controlling backend request: GET /tasks/browse.
  • Frequency: every initial visit to /tasks and every browse refetch caused by pagination, search, filtering, sorting, or revalidation.
  • S3 involvement: none on the measured browse navigation.

Production evidence

  • Production p75 for /api/tasks/browse was approximately 8.86 seconds.
  • Production /tasks LCP was approximately 9.15 seconds. The close match shows the browse endpoint controls when the cards appear.
  • The main task-selection SQL alone took approximately 3.09 seconds.
  • The /tasks navigation made no object-storage request. Supabase S3 is not the cause of this page's skeleton wait.

What this PR changes

  • Changes GET /tasks/browse to summary-first selection: the default path orders current task versions by the summary's last_run_at and applies the 24-task limit before any trial aggregation.
  • Returns exact counts plus at most 24 trial previews per card, fetched through an indexed lateral query (idx_trials_task_browse_preview).
  • Counters, status buckets, and cost totals come from the summary row; total_trials stays exact and latest_trials_truncated marks a shortened preview.
  • New response fields on TaskBrowseItem: pass_count, partial_count, fail_count, harness_count, skipped_count, pending_count, latest_trials_truncated.
  • Keeps advanced aggregate filters, comparisons, and non-default aggregate sorts on their existing on-demand aggregation path.
  • Adds the billed-trial regression coverage the first landing lacked: the fixture seeds billed_user_id rows and pins the corrected billed_cost_usd rollup. This is the exact gap that let perf(tasks): bound task browser page work #1152's KeyError reach production unseen.

Local query evidence

Using a local Postgres fixture with 600 tasks and approximately 31,000 trials:

  • Before: page selection read 21,085 trial rows and touched 1,552 buffers.
  • After: page selection touched 161 buffers, took approximately 0.66 ms, and read zero trial rows for selection.
  • The card-preview lateral query took approximately 0.49 ms and returned no more than 24 rows per card.

Known behavior change between this PR and #1192

What this PR does not change

Historical-data decision

Validation completed

  • backend tests/test_browse_search.py and tests/test_browse_filters.py: 14 passed. The three failures (test_search_grammar_and_or_exclude, test_browse_filters, test_browse_comparison_extras) reproduce identically on clean staging and are not introduced by this branch.
  • New test: bounded preview with exact totals — 31 trials on one card yields total_trials 31, cost 30.0, billed_cost_usd 15.0 across 15 billed trials, exactly 24 previews, latest_trials_truncated true, identical results with and without org scoping.
  • New test: switching a task's default version reflects on the card immediately (summary row created in the same transaction by the perf(tasks): maintain task browse summaries on trial writes #1185 hook).
  • New test: the default page query joins task_version_browse_summaries and contains no GROUP BY over trials.
  • oddish tests/test_browse_tag_filters.py: 22 passed, 1 skipped.

Rollback

  • Restore the old query: revert this PR and the previous on-demand aggregation path is back.
  • The summary table, hooks, and populated rows stay deployed and harmless.

Note

Medium Risk
Hot-path read cutover for the main task browser with no fallback when summary rows are missing, so it depends on prior population being complete. Also expands the browse API response shape used by the frontend.

Overview
Cuts over the default GET /tasks/browse path to summary-first selection: page ordering and exact card counters now come from task_version_browse_summaries for the selected current_version_id, instead of aggregating organization trial history before applying the limit.

Visible cards then load at most 24 current-version trial previews via a lateral query. Exact totals stay on the summary row; latest_trials_truncated marks when the preview is shorter than total_trials. New status-bucket fields (pass_count, partial_count, etc.) are also returned from the summary.

Advanced aggregate filters, comparisons, and non-default aggregate sorts keep their existing on-demand trial aggregation path. Costs on the default path resolve from the persisted summary cost_breakdown (including billed rollups).

Reviewed by Cursor Bugbot for commit ab05e20. Bugbot is set up for automated code reviews on this repo. Configure here.

One summary row per task version (exact card counters, status buckets,
model-grouped cost inputs, last_run_at), refreshed inside the same
transaction as every trial-population mutation: create/import,
start/reset, completion, cancellation, retry/supersede, scoped deletion,
experiment deletion, and default-version selection. Refreshes serialize
per version with sorted transaction-scoped advisory locks because
concurrent trial inserts hold KEY SHARE FK locks on task_versions.

No reader change and no historical scan: the browse endpoint still
aggregates on demand, and rows exist only for versions written to after
deploy. The read cutover and historical population land separately.

Split 1/4 of the #1152 re-land.
Data-only migration: reseed a summary row for every task version, zero
every counter, then replay the scoped aggregate and cost-breakdown
updates from task_browse_summary_001. Needed because the summary table
survived the #1156 revert while write-through maintenance did not, so
existing rows are frozen at the 2026-08-11 backfill, versions created
since have no row at all, and versions whose scoped trials vanished
need resetting.

The default browse reader (next split) has no fallback scan when a
summary row is missing or stale, so it needs this population to return
correct ordering and exact counts for historical tasks.

Split 2/4 of the #1152 re-land.
Default GET /tasks/browse ordering and exact card counters now come from
task_version_browse_summaries: the page query orders current task
versions by the summary's last_run_at and applies the 24-task limit
before any trial aggregation, instead of grouping the organization's
whole trial history first. Selected cards fetch at most 24 recent
current-version trials through an indexed lateral query; total_trials
stays exact and latest_trials_truncated marks a shortened preview.

Advanced aggregate filters, comparisons, and non-default aggregate
sorts keep the on-demand aggregation path. The cost rollup uses the
corrected billed_cost_usd key (the billed_usd KeyError 500'd /tasks and
caused the #1156 revert); billed-trial coverage now pins it.

Split 3/4 of the #1152 re-land; requires the summary maintenance hooks
and historical population from the previous splits.

Local EXPLAIN (600 tasks / 31k trials): page selection 1,552 -> 161
buffers, 21,085 trial rows -> 0 read at selection time.
@vercel

vercel Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
oddish-app Ready Ready Preview Aug 12, 2026 1:30am

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 287a14c. Configure here.

Comment thread oddish/src/oddish/core/endpoints/tasks_query.py
@cursor
cursor Bot requested review from anushbmx and charlesyhuang August 12, 2026 00:40

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Risk: medium. Left a non-blocking comment — not approving because Cursor Bugbot reported an unresolved medium finding on truncated browse trial previews and the Bugbot check finished as skipped. Assigned reviewers for human follow-up.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Router and Approver

@github-actions

github-actions Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Oddish preview

Commit: ab05e205dcd2ab28e10ae1bb0e2b938cf6245a8e

Surface Link Target
Frontend https://pr-1191.oddish.app Vercel preview for ab05e20
Backend oddish-pr-1191 oddish-pr-1191
Database project reybmlrtsdxybsthtjdx project reybmlrtsdxybsthtjdx

Vercel deployment URL: https://oddish-nd92qid0f.oddish.app

Plan:

  • Frontend deploy: true
  • Backend deploy: true
  • Migrations: false

This comment is updated by the PR Preview workflow.

@kyle-compute
kyle-compute changed the base branch from perf/tasks-browse-backfill to staging August 12, 2026 01:17
@kyle-compute
kyle-compute merged commit 52b8cb1 into staging Aug 12, 2026
18 checks passed
@kyle-compute
kyle-compute deleted the perf/tasks-browse-cutover branch August 12, 2026 01:31
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