Skip to content

Commit ee9cbe1

Browse files
priosshrsthclaude
andcommitted
fix(notifications): strip configured phrase from reminder subject (OUT-3919)
For subject-override workspaces the reminder email subject is derived from the task title. Strip a configurable substring from it via the SUBJECT_REPLACE / SUBJECT_REPLACE_TARGET env vars instead of hardcoding workspace-specific phrasing. Falls back to the raw title when unset, and guards against a nullish title. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 20bf74e commit ee9cbe1

3 files changed

Lines changed: 33 additions & 3 deletions

File tree

src/config/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,12 @@ export const showQueries = (() => {
5050

5151
export const assemblyApiDomain = z.string().url().parse(process.env.NEXT_PUBLIC_ASSEMBLY_API_DOMAIN)
5252

53+
// Substring stripped from the task title when building the reminder email subject for
54+
// subject-override workspaces, and the value it's replaced with. Configured via env so the
55+
// workspace-specific phrasing isn't hardcoded (OUT-3919).
56+
export const reminderSubjectSearch = process.env.REMINDER_SUBJECT_SEARCH || ''
57+
export const reminderSubjectReplacement = process.env.REMINDER_SUBJECT_REPLACEMENT || ''
58+
5359
// Workspaces whose single reminder emails use the task title as the subject, prefixed with the
5460
// escalating cadence tag (OUT-3861). Comma-separated workspace ids, e.g. C1: us-west-2_lg5zB-Utp.
5561
export const reminderSubjectOverrideWorkspaces = new Set(

src/jobs/notifications/send-reminder-email.test.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@ import { CopilotAPI } from '@/utils/CopilotAPI'
33
import { TaskReminderType } from '@prisma/client'
44
import { sendReminderEmail } from './send-reminder-email'
55

6-
jest.mock('@/config', () => ({ reminderSubjectOverrideWorkspaces: new Set(['ws_override']) }))
6+
jest.mock('@/config', () => ({
7+
reminderSubjectOverrideWorkspaces: new Set(['ws_override']),
8+
reminderSubjectSearch: ': Capital One',
9+
reminderSubjectReplacement: ':',
10+
}))
711

812
const workspace: WorkspaceResponse = {
913
id: 'ws_1',
@@ -138,6 +142,23 @@ describe('sendReminderEmail', () => {
138142
expect(payload.deliveryTargets.email.title).toBe('View task')
139143
})
140144

145+
it('strips the configured search phrase from the subject for override workspaces', async () => {
146+
const createNotification = jest.fn().mockResolvedValue({ id: 'notif_6', createdAt: '2026-05-25T00:00:00Z' })
147+
148+
await sendReminderEmail({
149+
task: { ...task, title: 'Quarterly review: Capital One' },
150+
recipientClientId: 'client_1',
151+
recipientCompanyId: 'company_1',
152+
reminderType: TaskReminderType.DUE_DATE_OVERDUE_3D,
153+
isCompanyRecipient: false,
154+
workspace: { ...workspace, id: 'ws_override' },
155+
copilot: buildCopilotMock(createNotification),
156+
})
157+
158+
const payload = createNotification.mock.calls[0][0]
159+
expect(payload.deliveryTargets.email.subject).toBe('[Overdue] Quarterly review:')
160+
})
161+
141162
it('keeps the generic subject for workspaces not in the override set', async () => {
142163
const createNotification = jest.fn().mockResolvedValue({ id: 'notif_5', createdAt: '2026-05-25T00:00:00Z' })
143164

src/jobs/notifications/send-reminder-email.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import 'server-only'
22

33
import { getReminderEmailDetails, REMINDER_ESCALATION_TAG } from '@/app/api/notification/notification.helpers'
4-
import { reminderSubjectOverrideWorkspaces } from '@/config'
4+
import { reminderSubjectOverrideWorkspaces, reminderSubjectSearch, reminderSubjectReplacement } from '@/config'
55
import { NotificationRequestBody, WorkspaceResponse } from '@/types/common'
66
import { CopilotAPI } from '@/utils/CopilotAPI'
77
import { Task, TaskReminderType } from '@prisma/client'
@@ -29,10 +29,13 @@ export const sendReminderEmail = async ({
2929
}: SendReminderEmailArgs): Promise<string> => {
3030
const details = getReminderEmailDetails(workspace, task, isCompanyRecipient)[reminderType]
3131

32+
const replacedSubjectTitle = reminderSubjectSearch
33+
? task.title.replace(reminderSubjectSearch, reminderSubjectReplacement)
34+
: task.title
3235
// For opted-in workspaces, mirror the customized assignment email by using the task title as the
3336
// subject, prefixed with the escalating cadence tag (OUT-3861).
3437
const subject = reminderSubjectOverrideWorkspaces.has(workspace.id)
35-
? `${REMINDER_ESCALATION_TAG[reminderType]} ${task.title}`
38+
? `${REMINDER_ESCALATION_TAG[reminderType]} ${replacedSubjectTitle}`
3639
: details.subject
3740

3841
const payload: NotificationRequestBody = {

0 commit comments

Comments
 (0)