Skip to content

fix(experiments): sort experiment list by result and creator#17

Open
lordspline wants to merge 1 commit into
masterfrom
capy/experiments-list-result-creator-sort
Open

fix(experiments): sort experiment list by result and creator#17
lordspline wants to merge 1 commit into
masterfrom
capy/experiments-list-result-creator-sort

Conversation

@lordspline

@lordspline lordspline commented Jun 17, 2026

Copy link
Copy Markdown

Problem

Sorting the Experiments list by the Result or Created by column broke list loading, as reported in PostHog/posthog#63621:

  • Result sent order=conclusion, which was not on the experiment order allowlist → 400 Invalid order field: 'conclusion'.
  • Created by sent order=created_by, whose ordering annotation built a Coalesce(first_name, email) over a CharField and an EmailField without an explicit output_fieldFieldError: Expression contains mixed types: CharField, EmailField500 ("A server error occurred").

The created_by failure only surfaced when the queryset was actually evaluated, so the existing allowlist test (which never evaluated the queryset) passed while the endpoint 500'd. On small datasets the 500 was further masked in the UI because LemonTable's client-side sorter re-sorts the already-loaded page — i.e. it looked sorted while the global, server-side request had failed.

Changes

All changes are in the experiment service ordering logic (no viewset/serializer or schema change, so no generated-type regeneration is required):

  • created_by ordering: add output_field=CharField() to the Coalesce, fixing the mixed-type FieldError.
  • conclusion ("Result") ordering: allowlist conclusion / -conclusion and order by a fixed Case ranking (won < lost < inconclusive < stopped_early < invalid, with no conclusion last). This mirrors the frontend Result sorter so server-side ordering is correct globally across pages, rather than only re-sorting the current page client-side.

Other valid sortable columns (name, created/updated/start/end dates, duration, status) are unchanged.

No visual UI change — the table renders identically; it simply stops erroring and now returns a globally-correct order.

How did you test this code?

I'm an agent (Captain Capy). I ran the following locally and verified behavior end to end:

Automated tests (hogli test):

  • products/experiments/backend/test/test_experiment_service.py — new/updated coverage: conclusion asc/desc by ranking, conclusion ordering global across pagination slices, created_by asc/desc (forcing queryset evaluation to catch the mixed-type regression), null creator and null conclusion handling, blank-first_name → email fallback, and the existing test_order_by_valid_fields_works strengthened to evaluate the queryset.
  • frontend/src/scenes/experiments/experimentsLogic.test.ts — new coverage: order forwarded to the API and reset to page 1 for conclusion, -conclusion, created_by, -created_by, and an unaffected -created_at; order synced to / restored from URL search params; order omitted from params when unset. 46/46 suite tests pass.
  • ruff lint/format and ty check pass (via pre-commit) on the changed Python; oxlint/oxfmt on the changed TS.

Manual browser verification against the local stack with seeded experiments (varied conclusions and two creators):

  • Before (fix reverted): clicking Result showed Load experiments failed: Invalid order field: 'conclusion'; the API returned 400 for order=conclusion and 500 for order=created_by.
  • After: clicking Result and Created by (both directions) reorders the table with no error toast; the API returns 200 for order=conclusion, -conclusion, created_by, -created_by, with the ranking/creator ordering verified directly.

Automatic notifications

  • Publish to changelog?
  • Alert Sales and Marketing teams?

Docs update

No docs change required.

🤖 Agent context

Autonomy: Human-driven (agent-assisted) — directed by Miles Morales.

Authored by Captain Capy. Investigated the LemonTable sorting contract (columns provide client-side sorters and a server-side onSort that pushes order=<columnKey>), the experiments logic/URL state, and the service-layer order allowlist. Confirmed both failures by evaluating the queryset directly (400 for conclusion, FieldError/500 for created_by).

Chose to fix server-side ordering (rather than dropping the columns to client-only sort) so ordering stays correct across the 100/page server pagination — explicitly avoiding the "sort only the current page but present it as global" trap, which is what masked the created_by 500 in the UI. Kept the change in the service layer to avoid an OpenAPI/types regeneration; the order parameter description already enumerates fields non-exhaustively. Verified before/after in a real browser session in addition to the automated tests above.


View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

Sorting the Experiments list by the Result column sent order=conclusion, which was not allowlisted (400 Invalid order field), and sorting by Created by sent order=created_by, whose Coalesce annotation mixed CharField and EmailField without an explicit output_field (500 FieldError).

Allow conclusion ordering via a fixed Case ranking that mirrors the frontend Result sorter (won < lost < inconclusive < stopped_early < invalid, no conclusion last) so server-side ordering stays consistent across paginated pages, and give the created_by Coalesce an explicit output_field. Strengthen the order regression test to evaluate the queryset so annotation errors surface, and add asc/desc, null-creator/conclusion, pagination, and URL-state coverage.
@lordspline lordspline added the capy Generated by capy.ai label Jun 17, 2026 — with Capy AI
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

capy Generated by capy.ai

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant