From 6f2ffe025f462b7582f38ff5ada89ba47b9c71f7 Mon Sep 17 00:00:00 2001 From: Rafael Audibert <32079912+rafaeelaudibert@users.noreply.github.com> Date: Fri, 19 Jun 2026 17:19:17 -0300 Subject: [PATCH 1/3] fix(inbox): tiebreak priority sort by newest first MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Priority is a coarse 5-bucket rank (P0–P4), so when sorting the inbox by priority, reports in the same tier came back in an arbitrary order. Append a `-created_at` clause so the newest report wins within a tier. The signal report list API already applies comma-separated ordering clauses in order (and falls back to `id`), so this is a frontend-only change scoped to the priority sort; total_weight and created_at sorts are unchanged. Generated-By: PostHog Code Task-Id: 3a0c3340-da9d-4189-82ac-8c9e310051c3 --- packages/core/src/inbox/reportFiltering.test.ts | 6 ++++++ packages/core/src/inbox/reportFiltering.ts | 7 ++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/core/src/inbox/reportFiltering.test.ts b/packages/core/src/inbox/reportFiltering.test.ts index 59c025ba1e..05b1e3bf06 100644 --- a/packages/core/src/inbox/reportFiltering.test.ts +++ b/packages/core/src/inbox/reportFiltering.test.ts @@ -106,6 +106,12 @@ describe("buildSignalReportListOrdering", () => { expect(buildSignalReportListOrdering(field, direction)).toBe(expected); }); + it("tiebreaks priority order by newest first", () => { + expect(buildSignalReportListOrdering("priority", "asc")).toBe( + "status,priority,-created_at", + ); + }); + it("does not float the current user's reports via ordering", () => { expect(buildSignalReportListOrdering("priority", "asc")).not.toContain( "is_suggested_reviewer", diff --git a/packages/core/src/inbox/reportFiltering.ts b/packages/core/src/inbox/reportFiltering.ts index d6cb760a9b..222c2c2e5c 100644 --- a/packages/core/src/inbox/reportFiltering.ts +++ b/packages/core/src/inbox/reportFiltering.ts @@ -65,6 +65,10 @@ export function buildStatusFilterParam(statuses: SignalReportStatus[]): string { * Comma-separated `ordering` for the signal report list API: * 1. Status rank (ready first – semantic server-side rank, always applied) * 2. Toolbar-selected field (priority, total_weight, created_at, etc.) + * 3. For priority, a `-created_at` tiebreak so the newest report wins within a + * tier. Priority is a coarse 5-bucket rank (P0–P4); without it, reports in + * the same tier come back in an arbitrary order. The server applies the + * clauses in order (and falls back to `id`), so this only breaks ties. * * Reviewer scope is applied via the `suggested_reviewers` param, not ordering: * a `-is_suggested_reviewer` tiebreak would float the user's reports to the top @@ -75,7 +79,8 @@ export function buildSignalReportListOrdering( direction: "asc" | "desc", ): string { const fieldKey = direction === "desc" ? `-${field}` : field; - return `status,${fieldKey}`; + const tiebreak = field === "priority" ? ",-created_at" : ""; + return `status,${fieldKey}${tiebreak}`; } export function buildSuggestedReviewerFilterParam( From dd004e95d17427d5b59ec505834d92d0d3e6c305 Mon Sep 17 00:00:00 2001 From: Rafael Audibert <32079912+rafaeelaudibert@users.noreply.github.com> Date: Fri, 19 Jun 2026 17:23:12 -0300 Subject: [PATCH 2/3] refactor(inbox): tiebreak non-priority sorts by priority Assemble the ordering clauses with a join instead of a literal comma fragment, and give every non-priority sort a `priority` tiebreak so the most urgent report wins when the primary field can't separate two reports. The priority sort still tiebreaks by `-created_at` (newest first within a tier). Generated-By: PostHog Code Task-Id: 3a0c3340-da9d-4189-82ac-8c9e310051c3 --- packages/core/src/inbox/reportFiltering.test.ts | 15 +++++++++------ packages/core/src/inbox/reportFiltering.ts | 13 +++++++------ 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/packages/core/src/inbox/reportFiltering.test.ts b/packages/core/src/inbox/reportFiltering.test.ts index 05b1e3bf06..ab6788d04f 100644 --- a/packages/core/src/inbox/reportFiltering.test.ts +++ b/packages/core/src/inbox/reportFiltering.test.ts @@ -99,12 +99,15 @@ describe("filterReportsBySearch", () => { describe("buildSignalReportListOrdering", () => { it.each([ - ["total_weight", "desc", "status,-total_weight"], - ["created_at", "asc", "status,created_at"], - ["signal_count", "desc", "status,-signal_count"], - ] as const)("orders by status then %s (%s)", (field, direction, expected) => { - expect(buildSignalReportListOrdering(field, direction)).toBe(expected); - }); + ["total_weight", "desc", "status,-total_weight,priority"], + ["created_at", "asc", "status,created_at,priority"], + ["signal_count", "desc", "status,-signal_count,priority"], + ] as const)( + "orders by status then %s (%s), tiebreaking by priority", + (field, direction, expected) => { + expect(buildSignalReportListOrdering(field, direction)).toBe(expected); + }, + ); it("tiebreaks priority order by newest first", () => { expect(buildSignalReportListOrdering("priority", "asc")).toBe( diff --git a/packages/core/src/inbox/reportFiltering.ts b/packages/core/src/inbox/reportFiltering.ts index 222c2c2e5c..170b65db17 100644 --- a/packages/core/src/inbox/reportFiltering.ts +++ b/packages/core/src/inbox/reportFiltering.ts @@ -65,10 +65,11 @@ export function buildStatusFilterParam(statuses: SignalReportStatus[]): string { * Comma-separated `ordering` for the signal report list API: * 1. Status rank (ready first – semantic server-side rank, always applied) * 2. Toolbar-selected field (priority, total_weight, created_at, etc.) - * 3. For priority, a `-created_at` tiebreak so the newest report wins within a - * tier. Priority is a coarse 5-bucket rank (P0–P4); without it, reports in - * the same tier come back in an arbitrary order. The server applies the - * clauses in order (and falls back to `id`), so this only breaks ties. + * 3. A tiebreak so reports the primary field can't separate come back in a + * sensible order. Sorting by priority (a coarse 5-bucket P0–P4 rank) tiebreaks + * by `-created_at` so the newest report wins within a tier; every other field + * tiebreaks by `priority` so the most urgent report wins. The server applies + * the clauses in order (and falls back to `id`), so this only breaks ties. * * Reviewer scope is applied via the `suggested_reviewers` param, not ordering: * a `-is_suggested_reviewer` tiebreak would float the user's reports to the top @@ -79,8 +80,8 @@ export function buildSignalReportListOrdering( direction: "asc" | "desc", ): string { const fieldKey = direction === "desc" ? `-${field}` : field; - const tiebreak = field === "priority" ? ",-created_at" : ""; - return `status,${fieldKey}${tiebreak}`; + const tiebreak = field === "priority" ? "-created_at" : "priority"; + return ["status", fieldKey, tiebreak].join(","); } export function buildSuggestedReviewerFilterParam( From bf8860c525c8838ac89930255defb36c015a5802 Mon Sep 17 00:00:00 2001 From: Rafael Audibert <32079912+rafaeelaudibert@users.noreply.github.com> Date: Fri, 19 Jun 2026 17:39:34 -0300 Subject: [PATCH 3/3] test(inbox): parameterise priority ordering tiebreak cases Fold the priority tiebreak test into an it.each table to match the team's parameterised-test preference, and add the previously-untested priority/desc direction (status,-priority,-created_at). Generated-By: PostHog Code Task-Id: 3a0c3340-da9d-4189-82ac-8c9e310051c3 --- packages/core/src/inbox/reportFiltering.test.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/core/src/inbox/reportFiltering.test.ts b/packages/core/src/inbox/reportFiltering.test.ts index ab6788d04f..c5fcfc97a6 100644 --- a/packages/core/src/inbox/reportFiltering.test.ts +++ b/packages/core/src/inbox/reportFiltering.test.ts @@ -109,11 +109,15 @@ describe("buildSignalReportListOrdering", () => { }, ); - it("tiebreaks priority order by newest first", () => { - expect(buildSignalReportListOrdering("priority", "asc")).toBe( - "status,priority,-created_at", - ); - }); + it.each([ + ["priority", "asc", "status,priority,-created_at"], + ["priority", "desc", "status,-priority,-created_at"], + ] as const)( + "tiebreaks %s (%s) by newest first", + (field, direction, expected) => { + expect(buildSignalReportListOrdering(field, direction)).toBe(expected); + }, + ); it("does not float the current user's reports via ordering", () => { expect(buildSignalReportListOrdering("priority", "asc")).not.toContain(