Skip to content

Connect mail watch to event consume#1542

Open
bubbmon233 wants to merge 3 commits into
larksuite:mainfrom
bubbmon233:feat/4ae867d
Open

Connect mail watch to event consume#1542
bubbmon233 wants to merge 3 commits into
larksuite:mainfrom
bubbmon233:feat/4ae867d

Conversation

@bubbmon233

@bubbmon233 bubbmon233 commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator

Generated by the harness-coding skill.

  • Branch: feat/4ae867d
  • Target: main

Sprints

ID Title Status Commit
S1 Connect mail watch to the unified event framework passed 97f0ed0

Source specs


This MR was created autonomously. Quality gates were enforced by the repo's own pre-commit hooks.

Summary by CodeRabbit

  • New Features
    • Added support for mail.user_mailbox.event.message_received_v1, including msg_format modes and folder/label filtering.
    • Migrated mail +watch to the unified event consumption framework, with data/json output modes (and forced full format when using --output-dir).
    • Added automatic mailbox event subscription with cleanup.
  • Documentation
    • Updated mail watch skill/docs to use unified event examples and clarify filtering/output-schema usage.
  • Bug Fixes
    • Improved stdin-EOF shutdown behavior to apply consistently to bounded and unbounded runs.
  • Tests
    • Added/expanded unit tests for normalization, matching/processing, and stdin/timeout handling.

Register the mail message received EventKey and route the legacy watch shortcut through the unified consume path so subscription identity and cleanup are handled by the event framework.

sprint: S1
@github-actions github-actions Bot added domain/mail PR touches the mail domain size/L Large or sensitive change across domains or core paths labels Jun 23, 2026
@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a new mail message-received event implementation, registers it, and migrates mail +watch onto the unified event consumer flow with updated stdin-close shutdown handling and documentation.

Changes

Mail message received event

Layer / File(s) Summary
Parameter normalization
events/mail/params.go
Defines mailbox and label/folder normalization for the mail event, including mailbox defaults, msg_format validation, primary mailbox lookup, JSON-array parsing, system alias handling, name-to-ID resolution, and error shaping for profile lookup failures.
Message matching and processing
events/mail/message_received.go
Implements mailbox matching, raw event body extraction, message fetching, fetch-format selection, minimal message projection, and folder/label filtering for received mail events.
Subscription hook and key registration
events/mail/preconsume.go, events/mail/register.go, events/register.go
Adds the subscribe/unsubscribe pre-consume hook, exports the mail event type and key definition, and registers the mail keys in the global event registry.
Mail event tests
events/mail/message_received_test.go
Adds tests for subscription identity, parameter normalization, envelope and flat payload matching, message processing formats and filters, and mailbox-path subscribe/unsubscribe behavior.

Event consume shutdown and mail watch migration

Layer / File(s) Summary
Stdin-close shutdown behavior
cmd/event/consume.go, cmd/event/consume_stdin_test.go, internal/event/consume/consume.go, internal/event/consume/consume_test.go, internal/event/consume/bounded_test.go, internal/event/consume/listening_text_test.go
Adds StdinClosed to consume options, routes stdin EOF through a separate shutdown path, updates listening and stop-hint text, and adjusts the related tests for stdin-close behavior and exit reasons.
mail +watch unified consume flow
shortcuts/mail/mail_watch.go, shortcuts/mail/mail_watch_test.go
Replaces the in-file WebSocket flow with consume.Run, adds the runtime API and envelope adapters, parses timeout and consume options, updates user-facing shortcut text, forces full message format for output directories, and adds shortcut tests.
Skill docs and references
skills/lark-mail/SKILL.md, skills/lark-mail/references/lark-mail-watch.md, skills/lark-event/SKILL.md
Updates the mail skill and watch reference text to describe the unified event framework, the recommended event consume entrypoint, output-schema inspection, output-directory usage, filter behavior, and the raw event envelope wording.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • larksuite/cli#1185: Adds the same mail.user_mailbox.event.message_received_v1 event path and related mail event wiring.
  • larksuite/cli#1285: Changes the same stdin-EOF shutdown behavior in cmd/event/consume.go and internal/event/consume/*.
  • larksuite/cli#1510: Also extends the global event registration list in events/register.go with a new domain key set.

Suggested reviewers

  • liuxinyanglxy
  • chanthuang
  • infeng

I hopped by moonlit mailbox rows,
To watch the message river flows.
With envelopes, events, and tidy hops,
The watch now runs till stdin stops.
🐰✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description misses the required Summary, Changes, Test Plan, and Related Issues sections from the template. Add the template sections with a 1-3 sentence summary, a bullet list of changes, a test plan, and a Related Issues entry (or None).
Docstring Coverage ⚠️ Warning Docstring coverage is 9.52% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly states the main change: connecting mail watch to event consume.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 OpenGrep (1.23.0)
internal/event/consume/consume_test.go

┌──────────────┐
│ Opengrep CLI │
└──────────────┘

�[32m✔�[39m �[1mOpengrep OSS�[0m
�[32m✔�[39m Basic security coverage for first-party code vulnerabilities.

[00.37][ERROR]: unable to find a config; path .coderabbit-opengrep-fallback.yml does not exist

internal/event/consume/listening_text_test.go

┌──────────────┐
│ Opengrep CLI │
└──────────────┘

�[32m✔�[39m �[1mOpengrep OSS�[0m
�[32m✔�[39m Basic security coverage for first-party code vulnerabilities.

[00.97][ERROR]: unable to find a config; path .coderabbit-opengrep-fallback.yml does not exist

internal/event/consume/consume.go

┌──────────────┐
│ Opengrep CLI │
└──────────────┘

�[32m✔�[39m �[1mOpengrep OSS�[0m
�[32m✔�[39m Basic security coverage for first-party code vulnerabilities.

[00.76][ERROR]: unable to find a config; path .coderabbit-opengrep-fallback.yml does not exist

  • 4 others
🔧 markdownlint-cli2 (0.22.1)
skills/lark-event/SKILL.md

markdownlint-cli2 v0.22.1 (markdownlint v0.40.0)
Error: Unable to use configuration file '/coderabbit-0.markdownlint-cli2.jsonc'; ENOENT: no such file or directory, open '/coderabbit-0.markdownlint-cli2.jsonc'
at throwForConfigurationFile (file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2.mjs:48:9)
at readOptionsOrConfig (file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2.mjs:169:5)
at async main (file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2.mjs:927:21)
at async file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2-bin.mjs:14:22 {
[cause]: Error: ENOENT: no such file or directory, open '/coderabbit-0.markdownlint-cli2.jsonc'
at async open (node:internal/fs/promises:640:25)
at async Object.readFile (node:internal/fs/promises:1287:14)
at async readOptionsOrConfig (file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2.mjs:141:17)
at async main (file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2.mjs:927:21)
at async file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2-bin.mjs:14:22 {
errno: -2,
code: 'ENOENT',
syscall: 'open',
path: '/coderabbit-0.markdownlint-cli2.jsonc'
}
}


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions

github-actions Bot commented Jun 23, 2026

Copy link
Copy Markdown

🚀 PR Preview Install Guide

🧰 CLI update

npm i -g https://pkg.pr.new/larksuite/cli/@larksuite/cli@7b31585e27cdafa0d3af1eb87545cba32ac76357

🧩 Skill update

npx skills add bubbmon233/cli#feat/4ae867d -y -g

@github-actions

github-actions Bot commented Jun 23, 2026

Copy link
Copy Markdown

PR Quality Summary

CI did not complete successfully. Use the failed check links below to decide whether this PR needs a code change or a rerun.

Failed checks

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@shortcuts/mail/mail_watch.go`:
- Around line 52-72: The mailWatchConsumeRuntime type and its CallAPI method are
duplicated across five files. Create a new shared file in the events/mail
package (e.g., runtime.go) that defines this type once, then remove the
duplicate mailWatchConsumeRuntime definition from shortcuts/mail/mail_watch.go
and import the shared type instead. Apply the same refactoring to the other four
files mentioned (events/mail/params.go, events/mail/preconsume.go,
events/mail/message_received.go, and events/mail/message_received_test.go) to
ensure all files use the single shared definition of the CallAPI method.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3ff9cbb1-f3c4-497a-b600-7bacad945b4b

📥 Commits

Reviewing files that changed from the base of the PR and between 5efaf65 and 97f0ed0.

📒 Files selected for processing (9)
  • events/mail/message_received.go
  • events/mail/message_received_test.go
  • events/mail/params.go
  • events/mail/preconsume.go
  • events/mail/register.go
  • events/register.go
  • shortcuts/mail/mail_watch.go
  • skills/lark-mail/SKILL.md
  • skills/lark-mail/references/lark-mail-watch.md

Comment on lines +52 to +72
type mailWatchConsumeRuntime struct {
runtime *common.RuntimeContext
}

func (r *mailWatchConsumeRuntime) CallAPI(_ context.Context, method, path string, body interface{}) (json.RawMessage, error) {
apiPath := path
query := make(larkcore.QueryParams)
if u, err := url.Parse(path); err == nil && u.RawQuery != "" {
apiPath = u.Path
for key, values := range u.Query() {
if len(values) > 0 {
query.Set(key, values[0])
}
}
}
data, err := r.runtime.DoAPIJSONTyped(method, apiPath, query, body)
if err != nil {
return nil, err
}
return json.Marshal(map[string]interface{}{"data": data})
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

Extract duplicated mailWatchConsumeRuntime to a shared location.

The mailWatchConsumeRuntime type and its CallAPI method are duplicated across at least 5 files in this PR:

  • shortcuts/mail/mail_watch.go (this file, lines 52-72)
  • events/mail/params.go:56-72
  • events/mail/preconsume.go:56-72
  • events/mail/message_received.go:56-72
  • events/mail/message_received_test.go:56-72

Define this adapter once in a shared location (e.g., events/mail/runtime.go or internal/event/apiruntime.go) and import it in all consuming files to eliminate duplication and ensure consistency.

♻️ Suggested refactor

Create a new file events/mail/runtime.go:

package mail

import (
	"context"
	"encoding/json"
	"net/url"

	larkcore "github.com/larksuite/oapi-sdk-go/v3/core"
	"github.com/larksuite/cli/shortcuts/common"
)

type ConsumeRuntime struct {
	runtime *common.RuntimeContext
}

func (r *ConsumeRuntime) CallAPI(_ context.Context, method, path string, body interface{}) (json.RawMessage, error) {
	apiPath := path
	query := make(larkcore.QueryParams)
	if u, err := url.Parse(path); err == nil && u.RawQuery != "" {
		apiPath = u.Path
		for key, values := range u.Query() {
			if len(values) > 0 {
				query.Set(key, values[0])
			}
		}
	}
	data, err := r.runtime.DoAPIJSONTyped(method, apiPath, query, body)
	if err != nil {
		return nil, err
	}
	return json.Marshal(map[string]interface{}{"data": data})
}

Then in this file, replace lines 52-72 with:

-type mailWatchConsumeRuntime struct {
-	runtime *common.RuntimeContext
-}
-
-func (r *mailWatchConsumeRuntime) CallAPI(_ context.Context, method, path string, body interface{}) (json.RawMessage, error) {
-	apiPath := path
-	query := make(larkcore.QueryParams)
-	if u, err := url.Parse(path); err == nil && u.RawQuery != "" {
-		apiPath = u.Path
-		for key, values := range u.Query() {
-			if len(values) > 0 {
-				query.Set(key, values[0])
-			}
-		}
-	}
-	data, err := r.runtime.DoAPIJSONTyped(method, apiPath, query, body)
-	if err != nil {
-		return nil, err
-	}
-	return json.Marshal(map[string]interface{}{"data": data})
-}

And update the consume.Run call sites (lines 271, 274):

-		Runtime:         &mailWatchConsumeRuntime{runtime: runtime},
+		Runtime:         &mailpkg.ConsumeRuntime{Runtime: runtime},
...
-		RemoteAPIClient: &mailWatchConsumeRuntime{runtime: runtime},
+		RemoteAPIClient: &mailpkg.ConsumeRuntime{Runtime: runtime},

Apply the same refactor to all other files that duplicate this code.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@shortcuts/mail/mail_watch.go` around lines 52 - 72, The
mailWatchConsumeRuntime type and its CallAPI method are duplicated across five
files. Create a new shared file in the events/mail package (e.g., runtime.go)
that defines this type once, then remove the duplicate mailWatchConsumeRuntime
definition from shortcuts/mail/mail_watch.go and import the shared type instead.
Apply the same refactoring to the other four files mentioned
(events/mail/params.go, events/mail/preconsume.go,
events/mail/message_received.go, and events/mail/message_received_test.go) to
ensure all files use the single shared definition of the CallAPI method.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@shortcuts/mail/mail_watch_test.go`:
- Around line 276-288: The TestMailWatchRejectsInvalidTimeout function currently
validates the error type and Param field, but it is missing assertions for the
complete typed-error contract. Add two additional assertions: first, use
errs.ProblemOf(err) to check that the Subtype field equals
errs.SubtypeInvalidArgument; second, use errors.Is to verify that the wrapped
cause (the original parse error from parseMailWatchTimeout) is preserved.
Reference the existing test patterns in
TestMailWatchOutputDirRejectsUnsafePathTyped and
TestMailWatchOutputDirMkdirFailureTyped to follow the established assertion
structure in this file.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ad20503c-17ef-4202-98b9-4c252094088e

📥 Commits

Reviewing files that changed from the base of the PR and between 97f0ed0 and c317663.

📒 Files selected for processing (2)
  • shortcuts/mail/mail_watch.go
  • shortcuts/mail/mail_watch_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • shortcuts/mail/mail_watch.go

Comment on lines +276 to +288
func TestMailWatchRejectsInvalidTimeout(t *testing.T) {
_, err := parseMailWatchTimeout("soon")
if err == nil {
t.Fatal("expected invalid timeout error")
}
var validationErr *errs.ValidationError
if !errors.As(err, &validationErr) {
t.Fatalf("expected validation error, got %T: %v", err, err)
}
if validationErr.Param != "--timeout" {
t.Fatalf("param = %q, want --timeout", validationErr.Param)
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, locate and inspect the test file
fd -t f "mail_watch_test.go" --exec wc -l {} +

Repository: larksuite/cli

Length of output: 238


🏁 Script executed:

# Get outline of the test file to understand structure
ast-grep outline shortcuts/mail/mail_watch_test.go

Repository: larksuite/cli

Length of output: 4111


🏁 Script executed:

# Read the specific test function and surrounding context
sed -n '270,295p' shortcuts/mail/mail_watch_test.go

Repository: larksuite/cli

Length of output: 913


🏁 Script executed:

# Find the parseMailWatchTimeout function definition
rg "func parseMailWatchTimeout" -A 20 shortcuts/mail/

Repository: larksuite/cli

Length of output: 1606


🏁 Script executed:

# Check what errs.ValidationError looks like and how ProblemOf works
rg "type ValidationError" -A 5 --type go

Repository: larksuite/cli

Length of output: 151


🏁 Script executed:

# Look for existing error-path test patterns to understand standards
rg "errs.ProblemOf" shortcuts/mail/ -B 3 -A 3 --type go

Repository: larksuite/cli

Length of output: 12641


🏁 Script executed:

rg "func mailValidationParamError" -A 10 shortcuts/mail/

Repository: larksuite/cli

Length of output: 923


🏁 Script executed:

# Check the test that does assert ValidationError category/subtype
sed -n '227,254p' shortcuts/mail/mail_watch_test.go

Repository: larksuite/cli

Length of output: 937


🏁 Script executed:

rg "func mailValidationError" -A 5 shortcuts/mail/mail_errors.go

Repository: larksuite/cli

Length of output: 460


🏁 Script executed:

sed -n '290,327p' shortcuts/mail/mail_watch_test.go

Repository: larksuite/cli

Length of output: 1317


Strengthen the invalid-timeout test to assert full typed-error contract.

This test validates type and Param, but should also assert the errs.ProblemOf(err) metadata—specifically that Subtype == errs.SubtypeInvalidArgument—and that the wrapped cause is preserved (e.g., via errors.Is(err, <original parse error>)). See TestMailWatchOutputDirRejectsUnsafePathTyped (lines 227–254) and TestMailWatchOutputDirMkdirFailureTyped (lines 290+) for the established pattern in this file.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@shortcuts/mail/mail_watch_test.go` around lines 276 - 288, The
TestMailWatchRejectsInvalidTimeout function currently validates the error type
and Param field, but it is missing assertions for the complete typed-error
contract. Add two additional assertions: first, use errs.ProblemOf(err) to check
that the Subtype field equals errs.SubtypeInvalidArgument; second, use errors.Is
to verify that the wrapped cause (the original parse error from
parseMailWatchTimeout) is preserved. Reference the existing test patterns in
TestMailWatchOutputDirRejectsUnsafePathTyped and
TestMailWatchOutputDirMkdirFailureTyped to follow the established assertion
structure in this file.

Sources: Coding guidelines, Learnings

Defer stdin EOF cancellation until after consume setup so PreConsume can complete and last-subscriber cleanup can run. Timeout and max-events remain fallback bounds, while stdin EOF exits as a signal for non-TTY callers.

sprint: S1
@bubbmon233 bubbmon233 requested a review from liangshuo-1 as a code owner June 24, 2026 15:32

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
internal/event/consume/consume.go (1)

162-164: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Use loopCtx for exit reason reporting.

StdinClosed now cancels only loopCtx, but the deferred exit log still calls exitReason(ctx, ...). When stdin EOF stops an unbounded or not-yet-bounded run, ctx.Err() can remain nil, so stderr may not report reason: signal as promised by the CLI/docs.

🐛 Proposed fix
 	lastForKey := false
 	var emitted atomic.Int64
 	startTime := time.Now()
+	exitCtx := ctx
 
 	// On panic, run cleanup unconditionally — leaking server state is worse than
 	// unsubscribing a still-live co-consumer (recoverable).
 	defer func() {
@@
 		}
 		if !opts.Quiet && r == nil {
-			reason := exitReason(ctx, emitted.Load(), opts)
+			reason := exitReason(exitCtx, emitted.Load(), opts)
 			fmt.Fprintf(errOut, "[event] exited — received %d event(s) in %s (reason: %s)\n",
 				emitted.Load(), truncateDuration(time.Since(startTime)), reason)
 		}
@@
 	if opts.StdinClosed != nil {
 		loopCtx, loopCancel = context.WithCancel(ctx)
+		exitCtx = loopCtx
 		defer loopCancel()
 		go func() {

Also applies to: 180-194

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/event/consume/consume.go` around lines 162 - 164, The deferred exit
log is still deriving the reason from the parent ctx instead of the
loop-specific cancellation context, so update the exit-reason reporting in
consume/consume.go to use loopCtx in the exitReason call. Make sure the deferred
stderr message and any related exit logging paths that currently reference
exitReason(ctx, ...) are switched to loopCtx so stdin EOF in unbounded or
not-yet-bounded runs reports the expected signal reason consistently.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@internal/event/consume/consume.go`:
- Around line 162-164: The deferred exit log is still deriving the reason from
the parent ctx instead of the loop-specific cancellation context, so update the
exit-reason reporting in consume/consume.go to use loopCtx in the exitReason
call. Make sure the deferred stderr message and any related exit logging paths
that currently reference exitReason(ctx, ...) are switched to loopCtx so stdin
EOF in unbounded or not-yet-bounded runs reports the expected signal reason
consistently.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ff36e332-465a-4bac-adce-f6c883d6b773

📥 Commits

Reviewing files that changed from the base of the PR and between c317663 and 7b31585.

📒 Files selected for processing (8)
  • cmd/event/consume.go
  • cmd/event/consume_stdin_test.go
  • internal/event/consume/bounded_test.go
  • internal/event/consume/consume.go
  • internal/event/consume/consume_test.go
  • internal/event/consume/listening_text_test.go
  • shortcuts/mail/mail_watch.go
  • skills/lark-event/SKILL.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • shortcuts/mail/mail_watch.go

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

domain/mail PR touches the mail domain size/L Large or sensitive change across domains or core paths

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant