Commit 5019719
Add task detail page + preview-deploy fixes (#103)
* Pin alembic search_path=public for preview branches
Freshly-created Supabase preview branches reached through the
Supavisor session pooler hand alembic a backend whose default
``search_path`` is empty, so the first DDL alembic emits — ``CREATE
TABLE alembic_version_{oddish,backend}`` — dies with
``InvalidSchemaNameError: no schema has been selected to create in``,
even though ``public`` exists and the role can read/write rows there.
Pass ``server_settings={"search_path": "public"}`` to asyncpg in
both alembic env.py files so every connection sets search_path at
the protocol level on startup, before any DDL runs. This is the
default search_path postgres ships with, so it's a no-op on
working configurations.
https://claude.ai/code/session_01KDGmgDLbXCX89RgwmPfoE8
* Add task detail page with version switcher and per-agent trial cards
Adds /tasks/[task_id], a task-centric view linked from the task browser
showing task-wide and per-version cost rollups, a version switcher, and
a per-agent breakdown of trial chips that opens the existing task/trial
drawers. Adds a /tasks/{task_id}/detail backend endpoint that bundles
the task with trials, all version summaries, and cost totals so the
page renders in one round trip.
https://claude.ai/code/session_01KDGmgDLbXCX89RgwmPfoE8
* Update Vercel preview env whenever Modal redeploys, not just on first branch
Gating ``Link Vercel project`` / ``Point Vercel preview to Modal
preview`` / ``Redeploy Vercel preview with updated env`` on
``branch_was_created == 'true'`` was too narrow. When a fresh-branch
run failed mid-flight (e.g. alembic flake on first push), the Vercel
env was never pointed at the PR's Modal URL, and every subsequent
push for the rest of the PR's lifetime had ``branch_was_created=false``
so the preview silently served prod for the entire PR.
Switch the gate to ``steps.deploy_modal.outputs.modal_api_url != ''``
so the Vercel env is refreshed any time Modal was redeployed in this
run. ``vercel env add --force`` is idempotent, so re-pointing at the
same URL is a no-op when nothing changed.
https://claude.ai/code/session_01KDGmgDLbXCX89RgwmPfoE8
* Aggregate task detail across every version, not just current
The previous detail endpoint reused get_task_status_core, which filters
trials to ``task.current_version_id`` (matches /tasks list semantics).
That meant every v1/v2/v3 trial was dropped before reaching the
per-version aggregator and the task-wide ``totals`` rollup, so the
detail page showed "0 trials · \$0" for every non-current version and
the total-spent KPI only counted the current version's trials — even
when each version had real trials behind it.
Load the task directly with all non-superseded trials and feed the
full set into ``task_status.trials``. The canonical header counts
(total/completed/failed/reward_*) stay scoped to the current version
so the header KPI block keeps matching the /tasks list — but the
trials list now spans every version, which is what the in-memory
version switcher needs and what the per-version chips + ``totals``
roll-up read from.
https://claude.ai/code/session_01KDGmgDLbXCX89RgwmPfoE8
* Surface verdict, pass rate, per-agent cost/duration; version dropdown
- Replace the chip-row version switcher with a dropdown that scales
past a handful of versions and lifts the version message into each
option.
- Render the task verdict (with confidence, primary issue / reasoning,
failure detail) above the KPI bar when run_analysis is set.
- Add pass-rate (passed / scored) next to avg score in the KPI bar.
- Add avg cost per priced trial and avg wall-clock duration to each
per-agent card.
- Trim over-explanatory comments left from earlier commits.
https://claude.ai/code/session_01KDGmgDLbXCX89RgwmPfoE8
* Dedup formatCostUsd, trial aggregation, verdict block; fix version dropdown
- Extract formatCostUsd, formatDurationSec, trialDurationSec into
lib/format.ts; rewire task-detail-client + experiment-detail-view.
- Extract TrialAggregate / accumulateTrial / summarizeTrials into
lib/trial-aggregation.ts; rewire task-detail-client and refactor
experiment-detail-view's buildExperimentSummary to compose them.
- Extract a TaskVerdictBadge component with ``card`` (drawer panel)
and ``inline`` (task detail page) variants; replace the duplicated
blocks in task-files-panel and task-detail-client.
- Switch the version picker from shadcn Select to DropdownMenu so
rich items (label + sub-line) render correctly.
- Drop the conceptually muddled "All versions" entry from the picker;
cross-version cost lives in its own KPI tile.
- Switch version selection from router.replace (which triggers a Next
soft-navigation and re-runs the page) to local state +
history.replaceState for URL persistence — selection is now a single
React render.
Net: -311 lines across the three call-sites, +240 into the new shared
modules, with three sites now on a single source of truth.
https://claude.ai/code/session_01KDGmgDLbXCX89RgwmPfoE8
* Add Run judge button to task verdict block
The verdict pipeline is opt-in (TaskSubmission.run_analysis defaults
to False), so most tasks render the verdict badge with no content.
Wire an on-demand "Run judge" button that POSTs to the existing
/tasks/{task_id}/verdict/retry endpoint, switches the badge to a
queuing state, and revalidates SWR so the pending → complete
transition reflects automatically.
https://claude.ai/code/session_01KDGmgDLbXCX89RgwmPfoE8
* Surface upstream status + body when verdict/retry proxy hits non-JSON
The /api/tasks/[task_id]/verdict/retry proxy blindly JSON.parse'd the
upstream body, so any non-JSON response (e.g. a Modal infra error
page) bubbled up to the UI as "Unexpected token 'm' ... is not valid
JSON", obscuring the actual upstream status. Catch the parse failure
and return the truncated body with its real status instead.
https://claude.ai/code/session_01KDGmgDLbXCX89RgwmPfoE8
* Point Run judge at analysis/retry; revert stray uv.lock churn
The verdict endpoint is gated on every trial analysis having
finished, so calling /verdict/retry on a task that was submitted with
run_analysis=False (the default) always 400s with "All trial analyses
must finish before running a task verdict". Switch the Run judge
button to /tasks/{id}/analysis/retry — that endpoint queues the
per-trial analyses and flips task.run_analysis=True, and the verdict
auto-enqueues from the cleanup worker once the analyses complete
(see workers/queue/cleanup.py:379-394).
Also harden the /analysis/retry proxy the same way as /verdict/retry
so a non-JSON upstream body surfaces with its actual status, and
revert the accidental backend/uv.lock churn that snuck into b1b7995
from a syntax-check ``uv sync``.
https://claude.ai/code/session_01KDGmgDLbXCX89RgwmPfoE8
* fix(verdict): show analyzing state while trial analyses are in flight
The neutral "Verdict pending" state was rendered both when nothing had
been run yet and while trial analyses were actively running, hiding the
fact that work was in progress and letting users click Run judge into a
guaranteed 400.
https://claude.ai/code/session_01KDGmgDLbXCX89RgwmPfoE8
* Address PR review: drop N+1 in detail endpoint and add tests
- Plumb the already-fetched TaskModel into list_task_versions_core so
get_task_detail_core stops re-running get_task_for_org_core (and its
extra SELECT) just to satisfy the version listing helper.
- Extract the rollup/aggregation block from get_task_detail_core into
_aggregate_task_detail_rollups so the cost-totals + per-version
bucketing is unit-testable without standing up the query stack.
- Add tests/test_task_detail_endpoint.py covering: the N+1 fix (with
regression guard for the non-task path), cross-org 404 via
get_task_detail_core, the happy aggregation path, and the orphan
task_version_id edge case.
https://claude.ai/code/session_01KDGmgDLbXCX89RgwmPfoE8
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 9504273 commit 5019719
20 files changed
Lines changed: 2011 additions & 207 deletions
File tree
- .github/workflows
- backend
- alembic
- api/routers
- frontend/src
- app
- (app)/tasks
- [task_id]
- api/tasks/[task_id]
- analysis/retry
- detail
- verdict/retry
- components
- lib
- oddish
- alembic
- src/oddish
- core
- server
- tests
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
234 | 234 | | |
235 | 235 | | |
236 | 236 | | |
237 | | - | |
238 | | - | |
239 | | - | |
240 | | - | |
241 | | - | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
242 | 241 | | |
243 | | - | |
| 242 | + | |
244 | 243 | | |
245 | 244 | | |
246 | 245 | | |
247 | 246 | | |
248 | | - | |
| 247 | + | |
249 | 248 | | |
250 | 249 | | |
251 | 250 | | |
| |||
256 | 255 | | |
257 | 256 | | |
258 | 257 | | |
259 | | - | |
| 258 | + | |
260 | 259 | | |
261 | 260 | | |
262 | 261 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
101 | 101 | | |
102 | 102 | | |
103 | 103 | | |
104 | | - | |
105 | | - | |
106 | | - | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
107 | 112 | | |
108 | 113 | | |
109 | 114 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
| 19 | + | |
19 | 20 | | |
20 | 21 | | |
21 | 22 | | |
| |||
52 | 53 | | |
53 | 54 | | |
54 | 55 | | |
| 56 | + | |
55 | 57 | | |
56 | 58 | | |
57 | 59 | | |
| |||
622 | 624 | | |
623 | 625 | | |
624 | 626 | | |
| 627 | + | |
| 628 | + | |
| 629 | + | |
| 630 | + | |
| 631 | + | |
| 632 | + | |
| 633 | + | |
| 634 | + | |
| 635 | + | |
| 636 | + | |
| 637 | + | |
| 638 | + | |
| 639 | + | |
| 640 | + | |
625 | 641 | | |
626 | 642 | | |
627 | 643 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
0 commit comments