diff --git a/apps/mobile/src/features/chat/utils/thinkingMessages.ts b/apps/mobile/src/features/chat/utils/thinkingMessages.ts index 35bd41b351..2eee2a1de4 100644 --- a/apps/mobile/src/features/chat/utils/thinkingMessages.ts +++ b/apps/mobile/src/features/chat/utils/thinkingMessages.ts @@ -60,7 +60,6 @@ export const THINKING_MESSAGES = [ "Blooming", "Sparking", "Nesting", - "Looping", "Wiring", "Snipping", "Zoning", diff --git a/packages/core/src/inbox/engagement.test.ts b/packages/core/src/inbox/engagement.test.ts index 4256dced8f..f880ace30c 100644 --- a/packages/core/src/inbox/engagement.test.ts +++ b/packages/core/src/inbox/engagement.test.ts @@ -65,6 +65,19 @@ describe("buildBulkActionEvents", () => { expect(events.every((e) => e.action_type === "delete")).toBe(true); }); + it("passes through the remove-suggested-reviewer action type", () => { + const events = buildBulkActionEvents({ + reports: [fakeReport({ id: "a" }), fakeReport({ id: "b" })], + actionType: "remove_suggested_reviewer", + surface: "toolbar", + }); + + expect( + events.every((e) => e.action_type === "remove_suggested_reviewer"), + ).toBe(true); + expect(events.every((e) => e.dismissal_reason === undefined)).toBe(true); + }); + it("attaches dismissal reason/note only for dismiss, truncating the note", () => { const longNote = "x".repeat(600); const [dismissed] = buildBulkActionEvents({ diff --git a/packages/core/src/inbox/engagement.ts b/packages/core/src/inbox/engagement.ts index bb31915e35..dc4428ddbb 100644 --- a/packages/core/src/inbox/engagement.ts +++ b/packages/core/src/inbox/engagement.ts @@ -123,7 +123,7 @@ export function resolveActionProperties( /** Bulk-capable report actions fired from the selection toolbar / dismiss flows. */ export type InboxBulkActionType = Extract< InboxReportActionProperties["action_type"], - "dismiss" | "snooze" | "delete" | "reingest" + "dismiss" | "snooze" | "delete" | "reingest" | "remove_suggested_reviewer" >; export interface BuildBulkActionEventsInput { @@ -137,8 +137,9 @@ export interface BuildBulkActionEventsInput { /** * Build `INBOX_REPORT_ACTION` payloads for a bulk (or single-report) dismiss / - * snooze / delete / reingest. Pure so it can be unit-tested and reused across - * the toolbar, the per-row dismiss action, and detail-screen dismiss. + * snooze / delete / reingest / remove-suggested-reviewer. Pure so it can be + * unit-tested and reused across the toolbar, the per-row dismiss action, and + * detail-screen dismiss. * * `is_bulk` / `bulk_size` carry the grouping; `rank` / `list_size` are left at 0 * because these flows act on a selection, not a positional list slot. diff --git a/packages/core/src/sessions/titleGeneratorService.test.ts b/packages/core/src/sessions/titleGeneratorService.test.ts index 956fd897ac..dfa5829191 100644 --- a/packages/core/src/sessions/titleGeneratorService.test.ts +++ b/packages/core/src/sessions/titleGeneratorService.test.ts @@ -220,6 +220,42 @@ describe("generateTitleAndSummary", () => { expect(result?.title).toBe("Fix login bug"); }); + it("does not parse SUMMARY in a PR title as the summary", async () => { + prompt.mockResolvedValue({ + content: + "TITLE: Review PR #123: Fix SUMMARY: parsing\nSUMMARY: Fixing title and summary parsing.", + }); + + const result = await makeService().generateTitleAndSummary( + '', + ); + + expect(result).toEqual({ + title: "Review PR #123: Fix SUMMARY: parsing", + summary: "Fixing title and summary parsing.", + }); + }); + + it("instructs the model to include existing GitHub PR titles", async () => { + prompt.mockResolvedValue({ + content: + "TITLE: Review PR #123: Fix login redirect\nSUMMARY: Reviewing the existing pull request.", + }); + + await makeService().generateTitleAndSummary( + '', + ); + + expect(prompt).toHaveBeenCalledWith( + expect.anything(), + expect.objectContaining({ + system: expect.stringContaining( + "the generated TITLE MUST include both the PR number and the PR title verbatim", + ), + }), + ); + }); + it("returns null on error", async () => { prompt.mockRejectedValue(new Error("network error")); const result = await makeService().generateTitleAndSummary("some content"); diff --git a/packages/core/src/sessions/titleGeneratorService.ts b/packages/core/src/sessions/titleGeneratorService.ts index 537317fb02..7913c2f64b 100644 --- a/packages/core/src/sessions/titleGeneratorService.ts +++ b/packages/core/src/sessions/titleGeneratorService.ts @@ -40,6 +40,7 @@ Title rules: - Remove: the, this, my, a, an - If possible, start with action verbs (Fix, Implement, Analyze, Debug, Update, Research, Review) - Keep exact: technical terms, numbers, filenames, HTTP codes, PR numbers +- GitHub PR rule: If the content contains a with a non-empty title, the generated TITLE MUST include both the PR number and the PR title verbatim. This rule overrides the 6-word title limit. Never replace the PR title with a generic phrase. Before responding, verify that both values appear in TITLE. - Never assume tech stack - Only output "Untitled" if the input is completely null/missing, not just unclear - If the input is a URL (e.g. a GitHub issue link, PR link, or any web URL), generate a title based on what you can infer from the URL structure (repo name, issue/PR number, etc.). Never say you cannot access URLs or ask the user for more information. @@ -58,6 +59,7 @@ Title examples: - "Update user documentation for new API endpoints" → Update API documentation - "Research competitor pricing strategies for our product" → Research competitor pricing - "Review pull request #123" → Review pull request #123 +- "" → Review PR #123: Fix login redirect - "debug 500 errors in production" → Debug production 500 errors - "why is the payment flow failing" → Analyze payment flow failure - "So how about that weather huh" → Weather chat @@ -173,7 +175,7 @@ export class TitleGeneratorService { const text = result.content.trim(); const titleMatch = text.match(/^TITLE:\s*(.+?)(?:\n|$)/m); - const summaryMatch = text.match(/SUMMARY:\s*([\s\S]+)$/m); + const summaryMatch = text.match(/^SUMMARY:\s*([\s\S]+)$/m); const title = titleMatch?.[1] diff --git a/packages/ui/src/features/inbox/components/InboxBulkSelectionBar.tsx b/packages/ui/src/features/inbox/components/InboxBulkSelectionBar.tsx index 263b2b5d4b..9a6e96e28b 100644 --- a/packages/ui/src/features/inbox/components/InboxBulkSelectionBar.tsx +++ b/packages/ui/src/features/inbox/components/InboxBulkSelectionBar.tsx @@ -3,6 +3,7 @@ import { EyeSlashIcon, PauseIcon, TrashIcon, + UserMinusIcon, XIcon, } from "@phosphor-icons/react"; import { isDismissalReasonSnooze } from "@posthog/shared/dismissalReasons"; @@ -158,6 +159,24 @@ export function InboxBulkSelectionBar({ Reingest + +