Skip to content

feat(notifications): evaluation-framed reminder emails for override workspaces - #1366

Merged
priosshrsth merged 3 commits into
mainfrom
anit/out-3948-change-header-of-reminder-emails-for-c1
Jul 3, 2026
Merged

feat(notifications): evaluation-framed reminder emails for override workspaces#1366
priosshrsth merged 3 commits into
mainfrom
anit/out-3948-change-header-of-reminder-emails-for-c1

Conversation

@priosshrsth

@priosshrsth priosshrsth commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator

Override workspaces (C1) now get an evaluation-framed reminder email: an htmlBody reading "mystery shop evaluation for <title>" with review wording, instead of the generic task body. Non-override workspaces are unchanged.

#Test Cases

Screenshot 2026-07-02 at 11 03 02 Screenshot 2026-07-02 at 11 00 20 Screenshot 2026-07-02 at 10 58 23

🤖 Generated with Claude Code

…orkspaces

Opted-in workspaces now receive an htmlBody variant that frames reminders as a
"mystery shop evaluation for <bold title>" and uses review wording, instead of
the generic task body.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@linear-code

linear-code Bot commented Jul 2, 2026

Copy link
Copy Markdown

OUT-3948

@vercel

vercel Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
tasks-app Ready Ready Preview, Comment Jul 2, 2026 6:24am

Request Review

@greptile-apps

greptile-apps Bot commented Jul 2, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds evaluation-framed reminder emails for override (C1) workspaces: when a workspace ID is in the reminderSubjectOverrideWorkspaces set, the email body switches to mystery-shop evaluation language with a custom header and CTA, while non-override workspaces receive the unchanged generic flow.

  • escapeHtml is applied to evaluationTitle before interpolation, closing the HTML-injection vector flagged in the previous review.
  • The intentional subject/htmlBody divergence for "Action Required: " prefixed titles is now explicitly documented with an inline comment and covered by a dedicated test.
  • Snapshot file updated to assert htmlBody: undefined for all non-override variants, locking the baseline.

Confidence Score: 5/5

Safe to merge — the change is well-scoped to override workspaces, both previous review concerns are fully addressed, and the new code paths are covered by unit and snapshot tests.

Both flagged issues from the earlier review are resolved: user-supplied title content is now HTML-escaped before injection into the email body, and the deliberate subject/htmlBody wording difference for "Action Required:" prefixed titles is documented and test-asserted. No new logic gaps were found. The override path is cleanly isolated from the generic flow, so a regression there would not affect non-override workspaces.

No files require special attention.

Important Files Changed

Filename Overview
src/app/api/notification/notification.helpers.ts Adds escapeHtml helper, optional evaluationTitle parameter, and per-variant htmlBody fields; HTML-special chars are properly escaped before interpolation into the template strings.
src/jobs/notifications/send-reminder-email.ts Refactors override path to build a separate email object: override workspaces get custom header/title/htmlBody; non-override keeps the original header/title/body flow. Clean separation with good inline comments.
src/app/api/notification/notification.helpers.test.ts Adds three new tests: undefined htmlBody when no evaluationTitle, evaluation wording when opted in, and XSS prevention via HTML escaping. Good coverage of the new branch.
src/jobs/notifications/send-reminder-email.test.ts Adds two tests: override workspace sends correct header/title/htmlBody, and "Action Required:" prefix stays in subject but is stripped from htmlBody — the intentional divergence is now explicitly asserted.
src/app/api/notification/snapshots/notification.helpers.test.ts.snap Snapshot updated to include htmlBody: undefined for all non-override variants, confirming baseline behavior is unchanged.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant Caller
    participant sendReminderEmail
    participant getReminderEmailDetails
    participant CopilotAPI

    Caller->>sendReminderEmail: task, workspace, reminderType, ...
    sendReminderEmail->>sendReminderEmail: "needsOverride = reminderSubjectOverrideWorkspaces.has(workspace.id)"
    sendReminderEmail->>sendReminderEmail: "displayTitle = task.title.replace(reminderSubjectSearch, ...)"
    alt "needsOverride == true"
        sendReminderEmail->>sendReminderEmail: "evaluationTitle = displayTitle.replace(Action Required, empty)"
        sendReminderEmail->>getReminderEmailDetails: workspace, task, isCompanyRecipient, evaluationTitle
        getReminderEmailDetails->>getReminderEmailDetails: escapeHtml(evaluationTitle)
        getReminderEmailDetails-->>sendReminderEmail: details with htmlBody set for all variants
        sendReminderEmail->>sendReminderEmail: "email = subject + custom header/title/htmlBody"
    else "needsOverride == false"
        sendReminderEmail->>getReminderEmailDetails: workspace, task, isCompanyRecipient, undefined
        getReminderEmailDetails-->>sendReminderEmail: details with htmlBody undefined
        sendReminderEmail->>sendReminderEmail: "email = subject + details header/title/body"
    end
    sendReminderEmail->>CopilotAPI: createNotification with deliveryTargets email
    CopilotAPI-->>sendReminderEmail: notification.id
    sendReminderEmail-->>Caller: notification.id
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant Caller
    participant sendReminderEmail
    participant getReminderEmailDetails
    participant CopilotAPI

    Caller->>sendReminderEmail: task, workspace, reminderType, ...
    sendReminderEmail->>sendReminderEmail: "needsOverride = reminderSubjectOverrideWorkspaces.has(workspace.id)"
    sendReminderEmail->>sendReminderEmail: "displayTitle = task.title.replace(reminderSubjectSearch, ...)"
    alt "needsOverride == true"
        sendReminderEmail->>sendReminderEmail: "evaluationTitle = displayTitle.replace(Action Required, empty)"
        sendReminderEmail->>getReminderEmailDetails: workspace, task, isCompanyRecipient, evaluationTitle
        getReminderEmailDetails->>getReminderEmailDetails: escapeHtml(evaluationTitle)
        getReminderEmailDetails-->>sendReminderEmail: details with htmlBody set for all variants
        sendReminderEmail->>sendReminderEmail: "email = subject + custom header/title/htmlBody"
    else "needsOverride == false"
        sendReminderEmail->>getReminderEmailDetails: workspace, task, isCompanyRecipient, undefined
        getReminderEmailDetails-->>sendReminderEmail: details with htmlBody undefined
        sendReminderEmail->>sendReminderEmail: "email = subject + details header/title/body"
    end
    sendReminderEmail->>CopilotAPI: createNotification with deliveryTargets email
    CopilotAPI-->>sendReminderEmail: notification.id
    sendReminderEmail-->>Caller: notification.id
Loading

Reviews (2): Last reviewed commit: "fix(notifications): escape evaluation ti..." | Re-trigger Greptile

Comment thread src/app/api/notification/notification.helpers.ts Outdated
Comment thread src/jobs/notifications/send-reminder-email.ts
- HTML-escape the task title before interpolating into the reminder htmlBody (XSS).
- Compute subject/body titles explicitly in the caller: the subject keeps the
  "Action Required: " prefix (mirrors the assignment email) while the evaluation
  htmlBody drops it, instead of stripping it hidden inside the helper.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Deployment failed with the following error:

Deploying Serverless Functions to multiple regions is restricted to the Pro and Enterprise plans.

Learn More: https://vercel.link/multiple-function-regions

@priosshrsth

Copy link
Copy Markdown
Collaborator Author

@greptileai I have addressed your comments. Please re review the pr.

Comment on lines +45 to +50
? {
subject,
header: 'Review your mystery shop evaluation',
title: 'Review Evaluation',
htmlBody: details.htmlBody,
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets store this object somewhere in config or constants instead of hardcoding.

@priosshrsth
priosshrsth merged commit e72e7f0 into main Jul 3, 2026
3 checks passed
priosshrsth added a commit that referenced this pull request Jul 3, 2026
* feat(notifications): evaluation-framed reminder emails for override workspaces (#1366)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants