Skip to content

feat(soup): add shared email thread filtering#2091

Merged
evanhutnik merged 7 commits intomainfrom
evan/shared-email-soup
Mar 23, 2026
Merged

feat(soup): add shared email thread filtering#2091
evanhutnik merged 7 commits intomainfrom
evan/shared-email-soup

Conversation

@evanhutnik
Copy link
Copy Markdown
Contributor

Summary

  • Adds SharedEmailFilter enum (exclude / include / only) to EmailFilters for controlling visibility of email threads shared with the user
  • Wires the filter through the full pipeline: EmailFiltersEmailLiteral::Shared AST variant → dynamic SQL query builder
  • Dynamic query conditionally prepends ProjectHierarchy + SharedEmailThreads CTEs to find threads shared via UserItemAccess (direct shares) or project membership (inherited access)
  • Resolves owner_id from email_links.macro_id instead of stamping the requesting user, so shared threads show the correct owner
  • Frontend: Mail view gets a new Shared tab (shared: 'only'), Inbox Signal/Noise exclude shared threads, Inbox All includes them, project views include shared threads
  • Adds 4 integration tests covering Exclude, Include, Only modes and owner_id correctness

Changes

Backend (Rust)

  • item_filters: New SharedEmailFilter enum, added shared field to EmailFilters, new EmailLiteral::Shared AST variant
  • email crate: Dynamic query builder conditionally adds recursive CTE for shared thread discovery, JOIN email_links for owner resolution in both dynamic and static preview queries
  • soup: build_email_request passes shared filter through the AST
  • New test fixture email_shared_threads.sql with second user, shared project, directly shared thread, project-shared thread, and unshared control thread

Frontend (TypeScript)

  • Generated SharedEmailFilter type from OpenAPI spec
  • soup-filter-presets.ts: Inbox signal/noise → exclude, inbox all → include, mail important/noise/drafts/sent → exclude, new mail shared tab → only
  • soup-view-tabs.tsx: Added "Shared" tab to Mail view
  • block-project/Block.tsx: Project view includes shared email threads

evanhutnik and others added 4 commits March 20, 2026 13:06
@evanhutnik evanhutnik requested review from a team as code owners March 20, 2026 21:26
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 20, 2026

Walkthrough

Adds shared-email thread filtering end-to-end. Introduces a SharedEmailFilter enum and shared field on EmailFilters, AST literal EmailLiteral::Shared, and extraction logic. Dynamic query builder and preview SQLs were updated to project owner_id (from email_links) and to conditionally include/exclude shared threads via CTEs. Multiple SQLx query artifacts and fixtures for shared-thread tests were added. Frontend: new mail "Shared" tab, updated filter presets, API schema changes, and client wiring for email view filters and move-to-folder behavior.

Suggested reviewers

  • whutchinson98

Poem

🐰 I hopped through rows and query streams,
Found threads that friends and projects share;
I nibbled bugs and stitched the seams,
Now inboxes show who placed each care —
A happy rabbit sorts with flair! ✨📬

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(soup): add shared email thread filtering' clearly and accurately summarizes the main change—introducing shared email thread filtering to the soup query/view system. It is specific, concise, and directly reflects the primary feature added.
Description check ✅ Passed The description is comprehensive and directly related to the changeset, explaining the new SharedEmailFilter enum, the full pipeline integration, backend/frontend changes, and test coverage. It provides meaningful context for understanding the feature.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch evan/shared-email-soup

Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

CodeRabbit can use Trivy to scan for security misconfigurations and secrets in Infrastructure as Code files.

Add a .trivyignore file to your project to customize which findings Trivy reports.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 20, 2026

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@CONTEXT.md`:
- Around line 6-7: Update the PR reference in CONTEXT.md to point to the correct
pull request and branch: replace "PR `#2065` (branch: `evan/email-projects`)" with
"PR `#2091` (branch: `evan/shared-email-soup`)" (or move the file to the correct
PR if this file belongs to a different change set) so the document reflects the
current change set.
- Line 3: Fix MD022 by ensuring each Markdown heading in CONTEXT.md has a blank
line above and below it; e.g., add a blank line before and after the "What was
built" heading (and do the same for the other headings flagged by markdownlint)
so every heading is separated by surrounding blank lines and satisfies the MD022
rule.
- Around line 3-5: The CONTEXT.md currently documents “email projects”
(references like project_id and move-to-folder) which is unrelated to this PR’s
feature (SharedEmailFilter); update the file so it either removes or splits the
unrelated project/folder content and instead adds concise context for
SharedEmailFilter (what it filters, expected UX, API/DB surfaces), or move the
email-project details into a separate CONTEXT_EMAIL_PROJECTS.md; ensure
references to symbols like SharedEmailFilter are present and remove or relocate
mentions of project_id and move-to-folder to avoid future confusion.

In `@js/app/packages/service-clients/service-storage/openapi.json`:
- Around line 9646-9650: The OpenAPI schema for the enum type SharedEmailFilter
is missing the documented default; update the SharedEmailFilter schema to
include "default": "exclude" (so the enum retains "exclude","include","only" and
adds default "exclude") to match the backend behavior and ensure generated
clients/spec consumers pick up the correct default value.

In
`@rust/cloud-storage/email/.sqlx/query-8519d142dcd5c9c6b60b9c61c24eca0b0c860eee7227124be31b2fcbff39e2bd.json`:
- Line 3: The cursor pagination is unstable because the cursor tuple
(effective_ts, t.id) and the ORDER BY keys differ; fix by carrying the real
thread updated_at through the inner subquery (select t.updated_at AS
thread_updated_at), compute effective_ts there, and use a consistent
ordering/tiebreaker everywhere—use the tuple (effective_ts, thread_updated_at,
id) for the cursor comparison, the inner subquery ORDER BY, and the outer ORDER
BY; after updating the source SQL (references: effective_ts, t.id, t.updated_at,
email_threads, CROSS JOIN LATERAL lmp) regenerate the .sqlx artifact with sqlx
prepare.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 2e30d08a-d9c9-4b69-863c-75e894cf40c0

📥 Commits

Reviewing files that changed from the base of the PR and between 5785789 and 224a19a.

⛔ Files ignored due to path filters (4)
  • js/app/packages/service-clients/service-storage/generated/schemas/emailFilters.ts is excluded by !**/generated/**
  • js/app/packages/service-clients/service-storage/generated/schemas/index.ts is excluded by !**/generated/**
  • js/app/packages/service-clients/service-storage/generated/schemas/sharedEmailFilter.ts is excluded by !**/generated/**
  • js/app/packages/service-clients/service-storage/generated/zod.ts is excluded by !**/generated/**
📒 Files selected for processing (35)
  • CONTEXT.md
  • js/app/packages/app/component/app-sidebar/soup-filter-presets.ts
  • js/app/packages/app/component/next-soup/soup-view/soup-view-tabs.tsx
  • js/app/packages/block-project/component/Block.tsx
  • js/app/packages/service-clients/service-storage/openapi.json
  • rust/cloud-storage/email/.sqlx/query-03313f689cbc216503f0372eca83b82f58a55d5c030892b6889eb77e76dbae2e.json
  • rust/cloud-storage/email/.sqlx/query-60fe557b143af00c5ddce77735e8b629dcce80126672a6c1d6bb9d2677976c75.json
  • rust/cloud-storage/email/.sqlx/query-6308825c9185249f0d2345b2f3e3276c8e153fe60dc2dd19545ac7de17e816dd.json
  • rust/cloud-storage/email/.sqlx/query-6fd1d9664bdbb6711f66cbb95e6703cbf3b285f97593dfd0eeecdbb474987074.json
  • rust/cloud-storage/email/.sqlx/query-714f90b277b3e53e84688fbe6b573cd622f5c320c33ab43c3c952aa9c12199b3.json
  • rust/cloud-storage/email/.sqlx/query-7a2801aee37ec03a99a8b2b69c3ab70d8d4fdf952f6643589fdf253d6ae32122.json
  • rust/cloud-storage/email/.sqlx/query-82301526394304e59946caa3c724c377fa46ad214834bc1a876d39ad29ad92ac.json
  • rust/cloud-storage/email/.sqlx/query-8457904f1747277abd3cc4e70ff28194f32c3ae2d6e67a392e679fd48aaab3f4.json
  • rust/cloud-storage/email/.sqlx/query-8519d142dcd5c9c6b60b9c61c24eca0b0c860eee7227124be31b2fcbff39e2bd.json
  • rust/cloud-storage/email/.sqlx/query-8b647d9753ba2b63ba9cc0057e0b71c87592c3e3983205d6969e0b3f1dbdb74b.json
  • rust/cloud-storage/email/.sqlx/query-a2c42d1ef0b78555844e0feddc14956117973fadd5565af9a2c48c7d2230e05f.json
  • rust/cloud-storage/email/.sqlx/query-a774adbda891d81dd017de5b0b50f41a5a1da6e4c5a81e51eb3cb267f10ef1fc.json
  • rust/cloud-storage/email/.sqlx/query-c3822d7dd145de025543eb4adaeba0bee0a970d6111ac2fc44049cbb0843e141.json
  • rust/cloud-storage/email/.sqlx/query-d8232849be1f6ec23537bb317cdf15e29ccdaf5ebee894e07dad1f8ca9b6b061.json
  • rust/cloud-storage/email/fixtures/email_shared_threads.sql
  • rust/cloud-storage/email/src/outbound/email_pg_repo/db_types.rs
  • rust/cloud-storage/email/src/outbound/email_pg_repo/dynamic/filters.rs
  • rust/cloud-storage/email/src/outbound/email_pg_repo/dynamic/query.rs
  • rust/cloud-storage/email/src/outbound/email_pg_repo/preview.rs
  • rust/cloud-storage/email/src/outbound/email_pg_repo/preview_views/all_mail.rs
  • rust/cloud-storage/email/src/outbound/email_pg_repo/preview_views/draft.rs
  • rust/cloud-storage/email/src/outbound/email_pg_repo/preview_views/important.rs
  • rust/cloud-storage/email/src/outbound/email_pg_repo/preview_views/new_inbox.rs
  • rust/cloud-storage/email/src/outbound/email_pg_repo/preview_views/other_inbox.rs
  • rust/cloud-storage/email/src/outbound/email_pg_repo/preview_views/sent.rs
  • rust/cloud-storage/email/src/outbound/email_pg_repo/preview_views/starred.rs
  • rust/cloud-storage/email/src/outbound/email_pg_repo/preview_views/user_label.rs
  • rust/cloud-storage/email/src/outbound/email_pg_repo/test/dynamic_query.rs
  • rust/cloud-storage/item_filters/src/ast/email.rs
  • rust/cloud-storage/item_filters/src/lib.rs
💤 Files with no reviewable changes (6)
  • rust/cloud-storage/email/.sqlx/query-d8232849be1f6ec23537bb317cdf15e29ccdaf5ebee894e07dad1f8ca9b6b061.json
  • rust/cloud-storage/email/.sqlx/query-6fd1d9664bdbb6711f66cbb95e6703cbf3b285f97593dfd0eeecdbb474987074.json
  • rust/cloud-storage/email/.sqlx/query-c3822d7dd145de025543eb4adaeba0bee0a970d6111ac2fc44049cbb0843e141.json
  • rust/cloud-storage/email/.sqlx/query-6308825c9185249f0d2345b2f3e3276c8e153fe60dc2dd19545ac7de17e816dd.json
  • rust/cloud-storage/email/.sqlx/query-8457904f1747277abd3cc4e70ff28194f32c3ae2d6e67a392e679fd48aaab3f4.json
  • rust/cloud-storage/email/.sqlx/query-7a2801aee37ec03a99a8b2b69c3ab70d8d4fdf952f6643589fdf253d6ae32122.json

CONTEXT.md Outdated
@@ -0,0 +1,65 @@
# Email Projects Feature — Context for Continuation

## What was built
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Fix markdown heading spacing (MD022).

Headings at Line 3, Line 6, Line 57, and Line 61 are missing required surrounding blank lines per markdownlint output.

Proposed markdown fix
 # Email Projects Feature — Context for Continuation
 
 ## What was built
+
 Full project support for email threads — the ability to assign email threads to projects/folders, filter them by project, and display them in project views. This mirrors existing functionality for Documents and Chats.
 
 ## PR
+
 https://github.com/macro-inc/macro/pull/2065 (branch: `evan/email-projects`) — all CI passing.
@@
 ## Known Limitations
+
 - **Command palette / `m` hotkey** doesn't work for email blocks because email threads aren't in the user history system, so they never appear in the quick access store. The right-click menu and bulk move modal work fine. See memory file `project_quick_access_store.md` for details.
 - **Generated types** — after changing Rust utoipa annotations, run `just gen-api <service-name>` from `js/app/`. Valid service names: `email-service`, `cloud-storage`, `search-service`, etc. Don't manually edit files in `service-clients/*/generated/`.
 
 ## Key Patterns to Follow
+
 - **Hex routers** in the `email` crate: generic over state type, use trait bounds, wired into `email_service` via merge. See `thread_labels_router.rs` or `thread_project_router.rs` as examples.

Also applies to: 6-6, 57-57, 61-61

🧰 Tools
🪛 markdownlint-cli2 (0.21.0)

[warning] 3-3: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CONTEXT.md` at line 3, Fix MD022 by ensuring each Markdown heading in
CONTEXT.md has a blank line above and below it; e.g., add a blank line before
and after the "What was built" heading (and do the same for the other headings
flagged by markdownlint) so every heading is separated by surrounding blank
lines and satisfies the MD022 rule.

Comment on lines +9646 to +9650
"SharedEmailFilter": {
"type": "string",
"description": "Controls whether shared email threads are included in results.",
"enum": ["exclude", "include", "only"]
},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Documented default is not encoded in the schema.

Line 6968 says the default is "exclude", but SharedEmailFilter has no explicit default. Add it to keep generated clients/spec consumers aligned with backend behavior.

💡 Proposed fix
       "SharedEmailFilter": {
         "type": "string",
         "description": "Controls whether shared email threads are included in results.",
-        "enum": ["exclude", "include", "only"]
+        "enum": ["exclude", "include", "only"],
+        "default": "exclude"
       },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"SharedEmailFilter": {
"type": "string",
"description": "Controls whether shared email threads are included in results.",
"enum": ["exclude", "include", "only"]
},
"SharedEmailFilter": {
"type": "string",
"description": "Controls whether shared email threads are included in results.",
"enum": ["exclude", "include", "only"],
"default": "exclude"
},
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@js/app/packages/service-clients/service-storage/openapi.json` around lines
9646 - 9650, The OpenAPI schema for the enum type SharedEmailFilter is missing
the documented default; update the SharedEmailFilter schema to include
"default": "exclude" (so the enum retains "exclude","include","only" and adds
default "exclude") to match the backend behavior and ensure generated
clients/spec consumers pick up the correct default value.

@@ -0,0 +1,122 @@
{
"db_name": "PostgreSQL",
"query": "\n WITH trash_label AS (\n SELECT id FROM email_labels WHERE link_id = $1 AND name = 'TRASH'\n ),\n important_label AS (\n SELECT id FROM email_labels WHERE link_id = $1 AND name = 'IMPORTANT'\n )\n SELECT\n t.id,\n t.provider_id,\n TRUE AS \"inbox_visible!\",\n t.is_read,\n t.effective_ts AS \"sort_ts!\",\n t.created_at AS \"created_at!\",\n t.updated_at AS \"updated_at!\",\n t.project_id,\n t.viewed_at AS \"viewed_at?\",\n lmp.subject AS \"name?\",\n lmp.snippet AS \"snippet?\",\n lmp.is_draft,\n EXISTS (\n SELECT 1\n FROM email_messages m_imp\n JOIN email_message_labels ml ON m_imp.id = ml.message_id\n WHERE m_imp.thread_id = t.id\n AND ml.label_id = (SELECT id FROM important_label)\n ) AS \"is_important!\",\n c.email_address AS \"sender_email?\",\n COALESCE(lmp.from_name, c.name) AS \"sender_name?\",\n c.sfs_photo_url as \"sender_photo_url?\",\n el.macro_id AS \"owner_id!\"\n FROM (\n -- Step 1: Efficiently find and sort ONLY the top N+1 candidate threads.\n -- This subquery is fast as it only touches `threads` and `user_history`.\n SELECT\n t.id,\n t.provider_id,\n t.link_id,\n t.is_read,\n t.project_id,\n t.latest_inbound_message_ts AS created_at,\n t.latest_inbound_message_ts AS updated_at,\n uh.updated_at AS viewed_at,\n CASE $5 -- sort_method_str\n WHEN 'viewed_at' THEN COALESCE(uh.\"updated_at\", '1970-01-01 00:00:00+00')\n WHEN 'viewed_updated' THEN COALESCE(uh.updated_at, t.latest_inbound_message_ts)\n ELSE t.latest_inbound_message_ts\n END AS effective_ts\n FROM email_threads t\n LEFT JOIN email_user_history uh ON uh.thread_id = t.id AND uh.link_id = t.link_id\n WHERE\n t.link_id = $1\n AND t.inbox_visible = TRUE\n AND t.latest_inbound_message_ts IS NOT NULL\n \n -- The cursor logic is moved inside this subquery for maximum efficiency.\n AND (($3::timestamptz IS NULL) OR (\n -- This CASE must exactly match the one that defines `effective_ts`\n CASE $5 -- sort_method_str\n WHEN 'viewed_at' THEN COALESCE(uh.\"updated_at\", '1970-01-01 00:00:00+00')\n WHEN 'viewed_updated' THEN COALESCE(uh.updated_at, t.latest_inbound_message_ts)\n ELSE t.latest_inbound_message_ts\n END, t.id\n ) < ($3::timestamptz, $4::uuid))\n ORDER BY effective_ts DESC, t.updated_at DESC -- fall back to updated_at if effective_ts is the same\n LIMIT $2\n ) AS t\n -- Step 2: For EACH of the limited threads from above, find its latest non-trashed message.\n CROSS JOIN LATERAL (\n SELECT\n m.subject,\n m.snippet,\n m.from_contact_id,\n m.from_name,\n m.is_draft\n FROM email_messages m\n WHERE m.thread_id = t.id\n AND NOT EXISTS (\n SELECT 1 FROM email_message_labels ml\n WHERE ml.message_id = m.id\n AND ml.label_id = (SELECT id FROM trash_label)\n )\n ORDER BY m.internal_date_ts DESC\n LIMIT 1\n ) AS lmp\n -- Step 3: Join to get the sender's details for the final result set.\n LEFT JOIN email_contacts c ON lmp.from_contact_id = c.id\n JOIN email_links el ON t.link_id = el.id\n -- Final ordering is preserved because the input `t` is already sorted.\n ORDER BY t.effective_ts DESC, t.updated_at DESC -- fall back to updated_at if effective_ts is the same\n ",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

python - <<'PY'
import json
from pathlib import Path

path = Path("rust/cloud-storage/email/.sqlx/query-8519d142dcd5c9c6b60b9c61c24eca0b0c860eee7227124be31b2fcbff39e2bd.json")
query = json.loads(path.read_text())["query"]

for needle in [
    "t.latest_inbound_message_ts AS updated_at",
    ") < ($3::timestamptz, $4::uuid)",
    "ORDER BY effective_ts DESC, t.updated_at DESC",
]:
    pos = query.find(needle)
    print(f"\n=== {needle} ===")
    if pos == -1:
        print("NOT FOUND")
        continue
    print(query[max(0, pos - 180): pos + 220])
PY

# Locate the source SQL/Rust query that must be patched before re-running `sqlx prepare`.
rg -n -C4 'sort_method_str|effective_ts|latest_inbound_message_ts' rust/cloud-storage -g '!**/.sqlx/**'

Repository: macro-inc/macro

Length of output: 50372


Fix cursor pagination by using one stable key across cursor comparison and all ORDER BY clauses.

On Line 3, the cursor predicate uses (effective_ts, t.id), the limited subquery sorts by (effective_ts, email_threads.updated_at), and the outer query re-sorts by (effective_ts, latest_inbound_message_ts) because updated_at is re-aliased in the subquery. When effective_ts ties occur (common in viewed_at/viewed_updated sort methods), the inconsistent tiebreaker causes rows to be skipped or repeated across page boundaries.

Fix the source SQL to ensure the cursor tuple and both ORDER BY clauses use the same stable key: carry the real thread updated_at through the subquery, add id as the final tiebreaker if needed, and use consistent ordering (e.g., (effective_ts, thread.updated_at, thread.id)). Then regenerate this .sqlx artifact.

Verification steps

Inspect rust/cloud-storage/email/src/outbound/email_pg_repo/preview_views/important.rs (or similar source SQL) and ensure:

  1. Cursor comparison: (effective_ts, id)
  2. Subquery ORDER BY: same key
  3. Outer ORDER BY: same key

If the keys differ, unify them before running sqlx prepare.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@rust/cloud-storage/email/.sqlx/query-8519d142dcd5c9c6b60b9c61c24eca0b0c860eee7227124be31b2fcbff39e2bd.json`
at line 3, The cursor pagination is unstable because the cursor tuple
(effective_ts, t.id) and the ORDER BY keys differ; fix by carrying the real
thread updated_at through the inner subquery (select t.updated_at AS
thread_updated_at), compute effective_ts there, and use a consistent
ordering/tiebreaker everywhere—use the tuple (effective_ts, thread_updated_at,
id) for the cursor comparison, the inner subquery ORDER BY, and the outer ORDER
BY; after updating the source SQL (references: effective_ts, t.id, t.updated_at,
email_threads, CROSS JOIN LATERAL lmp) regenerate the .sqlx artifact with sqlx
prepare.

evanhutnik and others added 2 commits March 20, 2026 17:50
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@js/app/packages/service-clients/service-search/openapi.json`:
- Around line 1923-1927: The schema for the enum type SharedEmailFilter is
missing the documented default; update the OpenAPI schema for SharedEmailFilter
to include "default": "exclude" alongside its "enum" so generated clients/docs
reflect the documented default behavior, and then regenerate any client/docs as
needed to pick up the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: ddc176f7-cc66-4123-8ef1-aae072f98703

📥 Commits

Reviewing files that changed from the base of the PR and between 224a19a and 6ce1400.

⛔ Files ignored due to path filters (3)
  • js/app/packages/service-clients/service-search/generated/models/emailFilters.ts is excluded by !**/generated/**
  • js/app/packages/service-clients/service-search/generated/models/index.ts is excluded by !**/generated/**
  • js/app/packages/service-clients/service-search/generated/models/sharedEmailFilter.ts is excluded by !**/generated/**
📒 Files selected for processing (1)
  • js/app/packages/service-clients/service-search/openapi.json

Comment on lines +1923 to +1927
"SharedEmailFilter": {
"type": "string",
"description": "Controls whether shared email threads are included in results.",
"enum": ["exclude", "include", "only"]
},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Encode the documented default in the schema.

SharedEmailFilter documents default behavior as "exclude" but does not declare a default value, which can create contract drift in generated clients/docs.

💡 Proposed fix
       "SharedEmailFilter": {
         "type": "string",
         "description": "Controls whether shared email threads are included in results.",
+        "default": "exclude",
         "enum": ["exclude", "include", "only"]
       },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"SharedEmailFilter": {
"type": "string",
"description": "Controls whether shared email threads are included in results.",
"enum": ["exclude", "include", "only"]
},
"SharedEmailFilter": {
"type": "string",
"description": "Controls whether shared email threads are included in results.",
"default": "exclude",
"enum": ["exclude", "include", "only"]
},
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@js/app/packages/service-clients/service-search/openapi.json` around lines
1923 - 1927, The schema for the enum type SharedEmailFilter is missing the
documented default; update the OpenAPI schema for SharedEmailFilter to include
"default": "exclude" alongside its "enum" so generated clients/docs reflect the
documented default behavior, and then regenerate any client/docs as needed to
pick up the change.

@evanhutnik evanhutnik merged commit 0072a19 into main Mar 23, 2026
40 checks passed
@evanhutnik evanhutnik deleted the evan/shared-email-soup branch March 23, 2026 18:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant