Skip to content

feat(mail): return typed error envelopes across the mail domain#1250

Merged
evandance merged 2 commits into
mainfrom
feat/errs-migrate-mail
Jun 4, 2026
Merged

feat(mail): return typed error envelopes across the mail domain#1250
evandance merged 2 commits into
mainfrom
feat/errs-migrate-mail

Conversation

@evandance

@evandance evandance commented Jun 3, 2026

Copy link
Copy Markdown
Collaborator

Summary

shortcuts/mail returned many command-facing errors through legacy output.Err* / output.Errorf / final fmt.Errorf paths and legacy API-call envelopes, so consumers had to recover classification from free-form message text. This migrates the mail domain to typed errs.* envelopes, giving callers stable type, subtype, param / params, hint, retryable, and log_id metadata across validation, API, network, file-I/O, and partial-failure flows. The shared drive-media upload helpers gain additive typed counterparts so mail's attachment uploads emit typed envelopes as well.

Changes

  • Migrate command-facing errors in shortcuts/mail from legacy output constructors, common legacy wrappers, and final bare fmt.Errorf returns to typed errs.* builders
  • Convert mail API boundaries from legacy runtime.CallAPI / JSON helpers to typed calls or runtime.ClassifyAPIResponse, and update mail-specific enrichers to read and preserve errs.ProblemOf metadata
  • Add shortcuts/mail/mail_errors.go with mail-local typed helpers for validation, file I/O, invalid-response, problem-message decoration, and hint enrichment
  • Add UploadDriveMediaAllTyped / UploadDriveMediaMultipartTyped to shortcuts/common and deprecate the legacy pair (legacy implementations unchanged); mail's large-attachment and template-attachment uploads consume the typed variants so upload failures carry typed subtype / code / log_id
  • Register mail API-code metadata for stable retry/quota hints and keep command-specific guidance in local enrichment paths
  • Emit batch draft-send partial failures through runtime.OutPartialFailure so successful and failed draft sends stay structured in stdout while the command exits with a typed multi-status signal; account-level fatal classification reads typed category/subtype/code (unregistered not_found codes stay per-draft recoverable)
  • Folder/label flags now resolve both IDs and names; --message-ids accepts unpadded base64url IDs; attachment and patch-file paths are validated with SafeInputPath/ValidatePath before reading; unknown-flag errors emit typed validation envelopes with candidate suggestions in params
  • Update mail tests and fakes to assert typed errors, structured params, typed API metadata preservation, and partial-failure envelopes; add typed upload coverage in shortcuts/common
  • Enforce typed-only mail errors through golangci path exceptions and the errscontract legacy-envelope / legacy-API-call guards

Test Plan

  • gofmt -l .
  • go vet ./...
  • go test -race ./shortcuts/mail/... ./shortcuts/ ./shortcuts/common/ ./internal/errclass/ ./internal/output/ ./errs/
  • go run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.1.6 run --new-from-rev=origin/main ./shortcuts/mail/... ./shortcuts/common/... ./internal/errclass/... — 0 issues
  • go run -C lint . ..
  • go build ./...

Related Issues

N/A

@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Migrates mail shortcuts to typed error helpers (validation/failed-precondition/invalid-response), replaces legacy fmt.Errorf/output.ErrValidation with mail-specific errors, switches many runtime.CallAPI calls to CallAPITyped, adds mail error metadata, extends linter scopes, and updates tests for typed error contracts.

Changes

Mail typed error migration

Layer / File(s) Summary
All mail typed-error migration changes
*.golangci.yml, internal/errclass/*, lint/errscontract/*, shortcuts/mail/*, tests under shortcuts/mail/*
Full-domain migration to typed errs patterns: config/linter scope updates, mailCodeMeta registration, API hint for quota, new unexported mail error helpers, CallAPITyped migrations, replacing fmt.Errorf/output.ErrValidation with mail helpers, //nolint:forbidigo annotations, and extensive test rewrites/additions validating typed errors and behaviors.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • larksuite/cli#806: Related refactor of mail unknown-flag suggestion handling and tests.
  • larksuite/cli#1202: Related changes to message ID validation and mail helpers.
  • larksuite/cli#1017: Related draft-send batching/partial-failure behavior and prior work on send flows.

Suggested reviewers

  • chanthuang
  • liangshuo-1
  • infeng

"A rabbit's ode to typed errors:

Loose strings once tangled every call,
Now hints and params stand proud and tall.
Mail commands whisper clear, with cause in chain,
Typed problems hop forward—no more vague pain.
Hooray: structured errors, neat and small!"

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/errs-migrate-mail

@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 3, 2026
@github-actions

github-actions Bot commented Jun 3, 2026

Copy link
Copy Markdown

🚀 PR Preview Install Guide

🧰 CLI update

npm i -g https://pkg.pr.new/larksuite/cli/@larksuite/cli@3924114502b02eb3f627bec8e4f18848b0725d67

🧩 Skill update

npx skills add larksuite/cli#feat/errs-migrate-mail -y -g

@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: 7

Caution

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

⚠️ Outside diff range comments (1)
shortcuts/mail/mail_triage.go (1)

1093-1110: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Do not retry every DoAPI failure.

The runtime.DoAPI error branch bypasses shouldRetryTriageAPIError, so auth/config/request-construction failures are retried three times exactly like network/rate-limit failures. That adds unnecessary delay and obscures the real error; gate this branch with the same retry predicate after wrapping/decorating the error.

🤖 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_triage.go` around lines 1093 - 1110, The DoAPI error
branch is currently always retried; modify the block handling runtime.DoAPI
errors so it wraps the error with client.WrapDoAPIError and
mailDecorateProblemMessage into lastErr, then consult
shouldRetryTriageAPIError(lastErr) before deciding to retry—i.e., if
!shouldRetryTriageAPIError(lastErr) or attempt == triageAPIRetries return nil,
lastErr, otherwise proceed to the sleep/retry; keep runtime.ClassifyAPIResponse
handling unchanged. Use the same symbols shown: runtime.DoAPI,
client.WrapDoAPIError, mailDecorateProblemMessage, shouldRetryTriageAPIError,
triageAPIRetries to locate and update the logic.
🧹 Nitpick comments (1)
shortcuts/mail/draft/large_attachment_parse.go (1)

4-4: ⚡ Quick win

Narrow nolint:forbidigo scope to the specific intermediate wraps.

A file-level suppression can hide unrelated future violations in this parser. Prefer line-level //nolint:forbidigo on the intentional intermediate fmt.Errorf sites instead.

🤖 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/draft/large_attachment_parse.go` at line 4, Remove the
file-level "//nolint:forbidigo" suppression and instead place line-level
"//nolint:forbidigo" comments directly on the specific intermediate fmt.Errorf
calls that perform the intentional error wraps in this parser; locate each
fmt.Errorf usage in this file (the intermediate large-attachment parser error
wrap sites) and add the nolint right after those lines so other forbidigo
violations elsewhere in the file remain linted.
🤖 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 @.golangci.yml:
- Around line 79-82: Update the comment above the errs-no-legacy-helper rule to
reflect that it now applies to both shortcuts/drive and shortcuts/mail;
specifically edit the comment that currently reads "errs-no-legacy-helper is
drive-only" to mention drive and mail (or remove the "drive-only" qualifier) so
it matches the path-except pattern (shortcuts/drive/|shortcuts/mail/) used for
errs-no-legacy-helper.

In `@shortcuts/mail/large_attachment_test.go`:
- Around line 977-979: Update the downstream tests to match the new error
wording: replace their expectations that look for "stat" with checks for the
updated phrase "cannot read file" (or ensure they use
strings.Contains(err.Error(), "cannot read file")). Specifically adjust
TestProcessLargeAttachments_FileStatError and
TestPreprocessLargeAttachments_StatError so they assert against the same error
contract produced by statAttachmentFiles(...) (use the same contains check as
the changed test).

In `@shortcuts/mail/mail_draft_edit.go`:
- Around line 520-525: The patch validation branch drops the `--patch-file`
attribution; change the error returned from patch.Validate() to use
mailValidationParamError("--patch-file", ...) (instead of mailValidationError)
so validation failures retain the invalid_parameters metadata; specifically
update the return in the patch.Validate() error branch (the call to
patch.Validate()) to return mailValidationParamError("--patch-file", "validate
patch file: %v", err).WithCause(err) (preserving the existing err and message
style) while leaving the json.Unmarshal branch unchanged.

In `@shortcuts/mail/mail_draft_send.go`:
- Around line 277-292: The typed internal/SDK errors from CallAPITyped should be
treated as fatal like the untyped fallback case: update the fatal-check to
include errs.CategoryInternal (add it to the existing category switch alongside
errs.CategoryAuthentication/Authorization/Config/Network) so that when
errs.ProblemOf(err) yields a Problem with Category == errs.CategoryInternal the
function returns true; also ensure any subtype-based checks still apply and
update the corresponding test that expected internal errors to be recoverable.

In `@shortcuts/mail/mail_reply.go`:
- Around line 274-276: The error message passed to mailValidationError is
redundantly formatting the error and also attaching it as the cause; in the
ResolveLocalImagePaths error branch (resolved, refs, resolveErr :=
draftpkg.ResolveLocalImagePaths(...)) replace mailValidationError("%v",
resolveErr).WithCause(resolveErr) with a descriptive message (e.g. "failed to
resolve local image paths") and call .WithCause(resolveErr) so the error text
appears only as the cause; keep draftpkg.ResolveLocalImagePaths, resolveErr,
mailValidationError and WithCause references intact.
- Around line 338-339: The error message duplicates the error because the
formatted string includes "%v" while the same err is also attached via
WithCause(err); update the return in the mailReply build path to remove the "%v"
placeholder so it reads something like mailValidationError("failed to build
EML").WithCause(err), keeping the mailValidationError call and the
WithCause(err) chaining intact (look for the return statement that calls
mailValidationError(...).WithCause(err) in this function).

In `@shortcuts/mail/mail_watch.go`:
- Around line 210-212: The call inside the vfs.MkdirAll error path uses
mailFileIOError("cannot create output directory %q: %v", err, outputDir) with a
mismatched format argument order; update the mailFileIOError invocation in that
block so the format verbs and arguments align (either pass outputDir then err to
match "%q: %v", or drop the "%v" if the function attaches the cause itself) —
locate the vfs.MkdirAll error branch and fix the mailFileIOError call
accordingly.

---

Outside diff comments:
In `@shortcuts/mail/mail_triage.go`:
- Around line 1093-1110: The DoAPI error branch is currently always retried;
modify the block handling runtime.DoAPI errors so it wraps the error with
client.WrapDoAPIError and mailDecorateProblemMessage into lastErr, then consult
shouldRetryTriageAPIError(lastErr) before deciding to retry—i.e., if
!shouldRetryTriageAPIError(lastErr) or attempt == triageAPIRetries return nil,
lastErr, otherwise proceed to the sleep/retry; keep runtime.ClassifyAPIResponse
handling unchanged. Use the same symbols shown: runtime.DoAPI,
client.WrapDoAPIError, mailDecorateProblemMessage, shouldRetryTriageAPIError,
triageAPIRetries to locate and update the logic.

---

Nitpick comments:
In `@shortcuts/mail/draft/large_attachment_parse.go`:
- Line 4: Remove the file-level "//nolint:forbidigo" suppression and instead
place line-level "//nolint:forbidigo" comments directly on the specific
intermediate fmt.Errorf calls that perform the intentional error wraps in this
parser; locate each fmt.Errorf usage in this file (the intermediate
large-attachment parser error wrap sites) and add the nolint right after those
lines so other forbidigo violations elsewhere in the file remain linted.
🪄 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: f8009d50-c6dc-4810-a483-f13bf7f7e15f

📥 Commits

Reviewing files that changed from the base of the PR and between 33de28f and d176e3c.

📒 Files selected for processing (49)
  • .golangci.yml
  • internal/errclass/classify.go
  • internal/errclass/codemeta_mail.go
  • lint/errscontract/rule_no_legacy_envelope_literal.go
  • shortcuts/mail/body_file.go
  • shortcuts/mail/draft/charset.go
  • shortcuts/mail/draft/large_attachment_parse.go
  • shortcuts/mail/draft/limits.go
  • shortcuts/mail/draft/model.go
  • shortcuts/mail/draft/parse.go
  • shortcuts/mail/draft/patch.go
  • shortcuts/mail/draft/patch_calendar.go
  • shortcuts/mail/draft/serialize.go
  • shortcuts/mail/draft/service.go
  • shortcuts/mail/emlbuilder/builder.go
  • shortcuts/mail/filecheck/filecheck.go
  • shortcuts/mail/flag_suggest.go
  • shortcuts/mail/flag_suggest_test.go
  • shortcuts/mail/helpers.go
  • shortcuts/mail/helpers_test.go
  • shortcuts/mail/large_attachment.go
  • shortcuts/mail/large_attachment_test.go
  • shortcuts/mail/lint/linter.go
  • shortcuts/mail/mail_decline_receipt.go
  • shortcuts/mail/mail_draft_create.go
  • shortcuts/mail/mail_draft_edit.go
  • shortcuts/mail/mail_draft_send.go
  • shortcuts/mail/mail_draft_send_test.go
  • shortcuts/mail/mail_errors.go
  • shortcuts/mail/mail_forward.go
  • shortcuts/mail/mail_lint_html.go
  • shortcuts/mail/mail_message.go
  • shortcuts/mail/mail_messages.go
  • shortcuts/mail/mail_reply.go
  • shortcuts/mail/mail_reply_all.go
  • shortcuts/mail/mail_send.go
  • shortcuts/mail/mail_send_receipt.go
  • shortcuts/mail/mail_share_to_chat.go
  • shortcuts/mail/mail_shortcut_validation_test.go
  • shortcuts/mail/mail_signature.go
  • shortcuts/mail/mail_template_create.go
  • shortcuts/mail/mail_template_update.go
  • shortcuts/mail/mail_thread.go
  • shortcuts/mail/mail_triage.go
  • shortcuts/mail/mail_watch.go
  • shortcuts/mail/mail_watch_test.go
  • shortcuts/mail/signature/provider.go
  • shortcuts/mail/signature_compose.go
  • shortcuts/mail/template_compose.go

Comment thread .golangci.yml Outdated
Comment thread shortcuts/mail/large_attachment_test.go
Comment thread shortcuts/mail/mail_draft_edit.go
Comment thread shortcuts/mail/mail_draft_send.go
Comment thread shortcuts/mail/mail_reply.go Outdated
Comment thread shortcuts/mail/mail_reply.go
Comment thread shortcuts/mail/mail_watch.go
@evandance evandance force-pushed the feat/errs-migrate-mail branch from d176e3c to a137598 Compare June 3, 2026 10:44
@codecov

codecov Bot commented Jun 3, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 67.87975% with 203 lines in your changes missing coverage. Please review.
✅ Project coverage is 70.06%. Comparing base (abc0553) to head (3924114).

Files with missing lines Patch % Lines
shortcuts/mail/helpers.go 71.90% 31 Missing and 3 partials ⚠️
shortcuts/mail/mail_draft_edit.go 36.36% 27 Missing and 1 partial ⚠️
shortcuts/common/drive_media_upload.go 81.81% 12 Missing and 10 partials ⚠️
shortcuts/mail/mail_forward.go 0.00% 14 Missing ⚠️
shortcuts/mail/template_compose.go 45.83% 13 Missing ⚠️
shortcuts/mail/large_attachment.go 43.75% 9 Missing ⚠️
shortcuts/mail/mail_draft_create.go 0.00% 8 Missing ⚠️
shortcuts/mail/mail_send_receipt.go 0.00% 8 Missing ⚠️
shortcuts/mail/mail_watch.go 76.47% 8 Missing ⚠️
shortcuts/mail/mail_reply.go 0.00% 7 Missing ⚠️
... and 16 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1250      +/-   ##
==========================================
+ Coverage   69.84%   70.06%   +0.21%     
==========================================
  Files         666      668       +2     
  Lines       64451    64724     +273     
==========================================
+ Hits        45019    45351     +332     
+ Misses      15812    15735      -77     
- Partials     3620     3638      +18     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@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: 2

🤖 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/emlbuilder/builder.go`:
- Around line 63-66: The helper readFile opens an untrusted CLI-supplied path
without validating it; before calling fio.Open(path) in function readFile (and
keeping the existing nolint comment), call validate.SafeInputPath(path) and
handle/return any validation error (wrap it similar to the existing fmt.Errorf
message) so the package-level guard is explicit; reference
validate.SafeInputPath, the readFile function, and the path variable when making
this change.

In `@shortcuts/mail/helpers.go`:
- Around line 924-927: The current by-name helper loop (using idFn and returning
mailValidationError when id==value) incorrectly rejects exact IDs and routes
*Name resolvers through ID-returning helpers; update callers (resolveFolderName,
resolveLabelName, resolveFolderQueryName, resolveLabelQueryName) to first detect
an exact-ID input using idFn and, if it matches, call a new or existing
resolveNameValueByID (or equivalent) path that returns the name directly for
that ID, and only invoke the existing by-name helper for true name lookups;
remove or change the branch that returns mailValidationError when id==value so
exact IDs are accepted and only non-matching name lookups fall through to name
resolution.
🪄 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: efe5b45b-3dda-4c66-983f-42de09be8c3c

📥 Commits

Reviewing files that changed from the base of the PR and between d176e3c and a137598.

📒 Files selected for processing (50)
  • .golangci.yml
  • internal/errclass/classify.go
  • internal/errclass/codemeta_mail.go
  • lint/errscontract/rule_no_legacy_envelope_literal.go
  • shortcuts/mail/body_file.go
  • shortcuts/mail/draft/charset.go
  • shortcuts/mail/draft/large_attachment_parse.go
  • shortcuts/mail/draft/limits.go
  • shortcuts/mail/draft/model.go
  • shortcuts/mail/draft/parse.go
  • shortcuts/mail/draft/patch.go
  • shortcuts/mail/draft/patch_calendar.go
  • shortcuts/mail/draft/serialize.go
  • shortcuts/mail/draft/service.go
  • shortcuts/mail/emlbuilder/builder.go
  • shortcuts/mail/filecheck/filecheck.go
  • shortcuts/mail/flag_suggest.go
  • shortcuts/mail/flag_suggest_test.go
  • shortcuts/mail/helpers.go
  • shortcuts/mail/helpers_test.go
  • shortcuts/mail/large_attachment.go
  • shortcuts/mail/large_attachment_test.go
  • shortcuts/mail/lint/linter.go
  • shortcuts/mail/mail_decline_receipt.go
  • shortcuts/mail/mail_draft_create.go
  • shortcuts/mail/mail_draft_edit.go
  • shortcuts/mail/mail_draft_send.go
  • shortcuts/mail/mail_draft_send_test.go
  • shortcuts/mail/mail_errors.go
  • shortcuts/mail/mail_forward.go
  • shortcuts/mail/mail_lint_html.go
  • shortcuts/mail/mail_message.go
  • shortcuts/mail/mail_messages.go
  • shortcuts/mail/mail_reply.go
  • shortcuts/mail/mail_reply_all.go
  • shortcuts/mail/mail_send.go
  • shortcuts/mail/mail_send_receipt.go
  • shortcuts/mail/mail_share_to_chat.go
  • shortcuts/mail/mail_shortcut_validation_test.go
  • shortcuts/mail/mail_signature.go
  • shortcuts/mail/mail_template_create.go
  • shortcuts/mail/mail_template_update.go
  • shortcuts/mail/mail_thread.go
  • shortcuts/mail/mail_triage.go
  • shortcuts/mail/mail_watch.go
  • shortcuts/mail/mail_watch_test.go
  • shortcuts/mail/signature/provider.go
  • shortcuts/mail/signature_compose.go
  • shortcuts/mail/template_compose.go
  • shortcuts/register_test.go
✅ Files skipped from review due to trivial changes (10)
  • shortcuts/mail/draft/patch_calendar.go
  • shortcuts/mail/draft/serialize.go
  • shortcuts/mail/draft/model.go
  • shortcuts/mail/draft/patch.go
  • shortcuts/mail/draft/large_attachment_parse.go
  • shortcuts/mail/draft/limits.go
  • shortcuts/mail/mail_shortcut_validation_test.go
  • shortcuts/mail/draft/parse.go
  • shortcuts/mail/lint/linter.go
  • shortcuts/mail/filecheck/filecheck.go
🚧 Files skipped from review as they are similar to previous changes (34)
  • shortcuts/mail/large_attachment_test.go
  • shortcuts/mail/mail_message.go
  • shortcuts/mail/mail_signature.go
  • lint/errscontract/rule_no_legacy_envelope_literal.go
  • shortcuts/mail/helpers_test.go
  • shortcuts/mail/flag_suggest.go
  • internal/errclass/codemeta_mail.go
  • shortcuts/mail/draft/charset.go
  • shortcuts/mail/mail_lint_html.go
  • shortcuts/mail/mail_watch_test.go
  • shortcuts/mail/mail_thread.go
  • shortcuts/mail/mail_send_receipt.go
  • shortcuts/mail/mail_decline_receipt.go
  • shortcuts/mail/mail_reply.go
  • shortcuts/mail/signature/provider.go
  • shortcuts/mail/body_file.go
  • .golangci.yml
  • shortcuts/mail/mail_draft_create.go
  • shortcuts/mail/mail_send.go
  • shortcuts/mail/mail_template_create.go
  • shortcuts/mail/mail_reply_all.go
  • shortcuts/mail/mail_watch.go
  • shortcuts/mail/template_compose.go
  • shortcuts/mail/mail_triage.go
  • shortcuts/mail/signature_compose.go
  • shortcuts/mail/mail_errors.go
  • shortcuts/mail/mail_draft_send_test.go
  • shortcuts/mail/mail_draft_edit.go
  • shortcuts/mail/draft/service.go
  • shortcuts/mail/large_attachment.go
  • shortcuts/mail/mail_draft_send.go
  • shortcuts/mail/mail_share_to_chat.go
  • shortcuts/mail/flag_suggest_test.go
  • shortcuts/mail/mail_forward.go

Comment thread shortcuts/mail/emlbuilder/builder.go
Comment thread shortcuts/mail/helpers.go
@evandance evandance force-pushed the feat/errs-migrate-mail branch 2 times, most recently from ab6deb0 to 06b6197 Compare June 3, 2026 11:12

@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: 2

🧹 Nitpick comments (1)
shortcuts/mail/draft/serialize.go (1)

4-4: ⚡ Quick win

Avoid file-wide forbidigo suppression; scope it to specific lines.

Line 4 suppresses forbidigo for the whole file, which can mask unrelated violations later. Prefer local //nolint:forbidigo only on the exact fmt.Errorf sites that need migration bridging.

🤖 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/draft/serialize.go` at line 4, Remove the file-wide
"//nolint:forbidigo" at the top of serialize.go and instead add a line-scoped
"//nolint:forbidigo" comment immediately next to each usage of fmt.Errorf in
this file (e.g., inside functions that construct intermediate serializer errors
such as SerializeDraft, DraftSerializer.Serialize, or any function returning
fmt.Errorf) so the suppression only covers those specific error-bridge sites;
keep the surrounding comment noting that the mail command layer wraps these into
a typed ValidationError.
🤖 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_signature.go`:
- Line 113: The validation error uses the wrong flag name; replace the hardcoded
"--signature-id" in the mailValidationParamError call with the actual flag
exposed by this command ("--detail") so the message points users to the correct
flag; update the call at mailValidationParamError("--signature-id", "signature
not found: %s", sigID) to use "--detail" (keeping the "signature not found: %s"
format and sigID variable) in the mail signature handling function.

In `@shortcuts/mail/mail_triage.go`:
- Around line 927-929: The current check in the if uses !paramWantsSearch but
the inner predicates require a search token scenario, so the branch is
unreachable; change the condition to use paramWantsSearch (remove the negation)
so the check reads: if paramWantsSearch && (strings.TrimSpace(query) != "" ||
len(triageQueryFilterFields(filter)) > 0) { return false,
mailValidationParamError("--page-token", "...") } ensuring you keep the same
error message and references to query, filter, triageQueryFilterFields,
paramWantsSearch, and mailValidationParamError.

---

Nitpick comments:
In `@shortcuts/mail/draft/serialize.go`:
- Line 4: Remove the file-wide "//nolint:forbidigo" at the top of serialize.go
and instead add a line-scoped "//nolint:forbidigo" comment immediately next to
each usage of fmt.Errorf in this file (e.g., inside functions that construct
intermediate serializer errors such as SerializeDraft,
DraftSerializer.Serialize, or any function returning fmt.Errorf) so the
suppression only covers those specific error-bridge sites; keep the surrounding
comment noting that the mail command layer wraps these into a typed
ValidationError.
🪄 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: 657227bd-7e35-4ddd-b476-c0195f748554

📥 Commits

Reviewing files that changed from the base of the PR and between a137598 and ab6deb0.

📒 Files selected for processing (53)
  • .golangci.yml
  • internal/errclass/classify.go
  • internal/errclass/codemeta_mail.go
  • lint/errscontract/rule_no_legacy_envelope_literal.go
  • shortcuts/mail/body_file.go
  • shortcuts/mail/draft/charset.go
  • shortcuts/mail/draft/large_attachment_parse.go
  • shortcuts/mail/draft/limits.go
  • shortcuts/mail/draft/model.go
  • shortcuts/mail/draft/parse.go
  • shortcuts/mail/draft/patch.go
  • shortcuts/mail/draft/patch_calendar.go
  • shortcuts/mail/draft/serialize.go
  • shortcuts/mail/draft/service.go
  • shortcuts/mail/emlbuilder/builder.go
  • shortcuts/mail/filecheck/filecheck.go
  • shortcuts/mail/flag_suggest.go
  • shortcuts/mail/flag_suggest_test.go
  • shortcuts/mail/helpers.go
  • shortcuts/mail/helpers_test.go
  • shortcuts/mail/large_attachment.go
  • shortcuts/mail/large_attachment_test.go
  • shortcuts/mail/lint/linter.go
  • shortcuts/mail/mail_decline_receipt.go
  • shortcuts/mail/mail_draft_create.go
  • shortcuts/mail/mail_draft_edit.go
  • shortcuts/mail/mail_draft_send.go
  • shortcuts/mail/mail_draft_send_test.go
  • shortcuts/mail/mail_errors.go
  • shortcuts/mail/mail_errors_test.go
  • shortcuts/mail/mail_forward.go
  • shortcuts/mail/mail_lint_html.go
  • shortcuts/mail/mail_message.go
  • shortcuts/mail/mail_messages.go
  • shortcuts/mail/mail_reply.go
  • shortcuts/mail/mail_reply_all.go
  • shortcuts/mail/mail_send.go
  • shortcuts/mail/mail_send_receipt.go
  • shortcuts/mail/mail_share_to_chat.go
  • shortcuts/mail/mail_shortcut_validation_test.go
  • shortcuts/mail/mail_signature.go
  • shortcuts/mail/mail_template_create.go
  • shortcuts/mail/mail_template_update.go
  • shortcuts/mail/mail_thread.go
  • shortcuts/mail/mail_triage.go
  • shortcuts/mail/mail_triage_test.go
  • shortcuts/mail/mail_watch.go
  • shortcuts/mail/mail_watch_test.go
  • shortcuts/mail/signature/provider.go
  • shortcuts/mail/signature_compose.go
  • shortcuts/mail/signature_compose_test.go
  • shortcuts/mail/template_compose.go
  • shortcuts/register_test.go
✅ Files skipped from review due to trivial changes (11)
  • shortcuts/mail/lint/linter.go
  • shortcuts/mail/draft/patch.go
  • shortcuts/mail/draft/limits.go
  • shortcuts/mail/draft/patch_calendar.go
  • shortcuts/mail/large_attachment_test.go
  • shortcuts/mail/draft/large_attachment_parse.go
  • shortcuts/mail/draft/charset.go
  • shortcuts/mail/draft/parse.go
  • shortcuts/mail/large_attachment.go
  • shortcuts/mail/draft/model.go
  • shortcuts/mail/mail_shortcut_validation_test.go

Comment thread shortcuts/mail/mail_signature.go Outdated
Comment thread shortcuts/mail/mail_triage.go
@evandance evandance changed the title feat: migrate mail errors to typed contract feat(mail): return typed error envelopes across the mail domain Jun 3, 2026
@evandance evandance force-pushed the feat/errs-migrate-mail branch from 06b6197 to f5c6aa7 Compare June 3, 2026 11:17

@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

Caution

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

⚠️ Outside diff range comments (1)
shortcuts/mail/mail_draft_edit.go (1)

509-514: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Validate --patch-file before opening it.

Line 511 reads a CLI-supplied path directly. Add an explicit safe-path validation gate before Open so untrusted path input is rejected early and consistently.

Proposed fix
 func loadPatchFile(runtime *common.RuntimeContext, path string) (draftpkg.Patch, error) {
 	var patch draftpkg.Patch
+	if err := runtime.ValidatePath(path); err != nil {
+		return patch, mailValidationParamError("--patch-file", "--patch-file %q: %v", path, err).WithCause(err)
+	}
 	f, err := runtime.FileIO().Open(path)
 	if err != nil {
 		return patch, mailValidationParamError("--patch-file", "--patch-file %q: %v", path, err).WithCause(mailInputStatError(err))
 	}

As per coding guidelines **/*.go: “Validate paths before reading with validate.SafeInputPath because CLI arguments are untrusted (they come from AI agents)”.

🤖 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_draft_edit.go` around lines 509 - 514, In loadPatchFile,
validate the CLI-supplied path with validate.SafeInputPath before calling
runtime.FileIO().Open so untrusted input is rejected early; if
validate.SafeInputPath(path) returns an error, return the same
mailValidationParamError used for Open (e.g.,
mailValidationParamError("--patch-file", "--patch-file %q: %v", path,
err).WithCause(mailInputStatError(err))) so the caller sees a consistent
validation error; keep the subsequent Open only after the SafeInputPath check.
♻️ Duplicate comments (1)
shortcuts/mail/mail_watch.go (1)

210-212: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix the mailFileIOError format placeholder mismatch.

The format string has two verbs (%q: %v), but only one formatting argument is supplied after the cause, which can produce malformed user-facing text.

Proposed fix
-			if err := vfs.MkdirAll(outputDir, 0700); err != nil {
-				return mailFileIOError("cannot create output directory %q: %v", err, outputDir)
-			}
+			if err := vfs.MkdirAll(outputDir, 0700); err != nil {
+				return mailFileIOError("cannot create output directory %q", err, outputDir)
+			}
🤖 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 210 - 212, The format string
passed to mailFileIOError in the vfs.MkdirAll error branch uses two verbs ("%q:
%v") but supplies only the error plus outputDir in the wrong order, causing
incorrect formatting; fix this by calling mailFileIOError with arguments
matching the format verbs (e.g., pass outputDir first then err) or simplify the
format to a single verb and supply the corresponding value—update the call near
vfs.MkdirAll so the format string and arguments (outputDir, err) align with
mailFileIOError.
🤖 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/helpers.go`:
- Around line 2711-2714: The validator currently uses
base64.URLEncoding.DecodeString which rejects valid unpadded base64url IDs;
update the decode logic in the function around the DecodeString call (the block
referencing decoded, err, id, index and mailValidationParamError) to first try
base64.RawURLEncoding.DecodeString(id) and if that returns an error, fall back
to base64.URLEncoding.DecodeString(id); retain the existing
mailValidationParamError return when both attempts fail or decoded length is
zero so both unpadded (raw) and padded values are accepted.

---

Outside diff comments:
In `@shortcuts/mail/mail_draft_edit.go`:
- Around line 509-514: In loadPatchFile, validate the CLI-supplied path with
validate.SafeInputPath before calling runtime.FileIO().Open so untrusted input
is rejected early; if validate.SafeInputPath(path) returns an error, return the
same mailValidationParamError used for Open (e.g.,
mailValidationParamError("--patch-file", "--patch-file %q: %v", path,
err).WithCause(mailInputStatError(err))) so the caller sees a consistent
validation error; keep the subsequent Open only after the SafeInputPath check.

---

Duplicate comments:
In `@shortcuts/mail/mail_watch.go`:
- Around line 210-212: The format string passed to mailFileIOError in the
vfs.MkdirAll error branch uses two verbs ("%q: %v") but supplies only the error
plus outputDir in the wrong order, causing incorrect formatting; fix this by
calling mailFileIOError with arguments matching the format verbs (e.g., pass
outputDir first then err) or simplify the format to a single verb and supply the
corresponding value—update the call near vfs.MkdirAll so the format string and
arguments (outputDir, err) align with mailFileIOError.
🪄 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: 148f70a3-6b0b-4d70-bc80-38fd40a693b3

📥 Commits

Reviewing files that changed from the base of the PR and between ab6deb0 and f5c6aa7.

📒 Files selected for processing (52)
  • .golangci.yml
  • internal/errclass/classify.go
  • internal/errclass/codemeta_mail.go
  • lint/errscontract/rule_no_legacy_envelope_literal.go
  • shortcuts/mail/body_file.go
  • shortcuts/mail/draft/charset.go
  • shortcuts/mail/draft/large_attachment_parse.go
  • shortcuts/mail/draft/limits.go
  • shortcuts/mail/draft/model.go
  • shortcuts/mail/draft/parse.go
  • shortcuts/mail/draft/patch.go
  • shortcuts/mail/draft/patch_calendar.go
  • shortcuts/mail/draft/serialize.go
  • shortcuts/mail/draft/service.go
  • shortcuts/mail/emlbuilder/builder.go
  • shortcuts/mail/filecheck/filecheck.go
  • shortcuts/mail/flag_suggest.go
  • shortcuts/mail/flag_suggest_test.go
  • shortcuts/mail/helpers.go
  • shortcuts/mail/helpers_test.go
  • shortcuts/mail/large_attachment.go
  • shortcuts/mail/large_attachment_test.go
  • shortcuts/mail/lint/linter.go
  • shortcuts/mail/mail_decline_receipt.go
  • shortcuts/mail/mail_draft_create.go
  • shortcuts/mail/mail_draft_edit.go
  • shortcuts/mail/mail_draft_send.go
  • shortcuts/mail/mail_draft_send_test.go
  • shortcuts/mail/mail_errors.go
  • shortcuts/mail/mail_errors_test.go
  • shortcuts/mail/mail_forward.go
  • shortcuts/mail/mail_lint_html.go
  • shortcuts/mail/mail_message.go
  • shortcuts/mail/mail_reply.go
  • shortcuts/mail/mail_reply_all.go
  • shortcuts/mail/mail_send.go
  • shortcuts/mail/mail_send_receipt.go
  • shortcuts/mail/mail_share_to_chat.go
  • shortcuts/mail/mail_shortcut_validation_test.go
  • shortcuts/mail/mail_signature.go
  • shortcuts/mail/mail_template_create.go
  • shortcuts/mail/mail_template_update.go
  • shortcuts/mail/mail_thread.go
  • shortcuts/mail/mail_triage.go
  • shortcuts/mail/mail_triage_test.go
  • shortcuts/mail/mail_watch.go
  • shortcuts/mail/mail_watch_test.go
  • shortcuts/mail/signature/provider.go
  • shortcuts/mail/signature_compose.go
  • shortcuts/mail/signature_compose_test.go
  • shortcuts/mail/template_compose.go
  • shortcuts/register_test.go
✅ Files skipped from review due to trivial changes (13)
  • shortcuts/mail/draft/parse.go
  • shortcuts/mail/draft/serialize.go
  • lint/errscontract/rule_no_legacy_envelope_literal.go
  • shortcuts/mail/draft/patch.go
  • shortcuts/mail/mail_shortcut_validation_test.go
  • shortcuts/mail/draft/limits.go
  • shortcuts/mail/draft/large_attachment_parse.go
  • shortcuts/mail/signature/provider.go
  • shortcuts/mail/lint/linter.go
  • shortcuts/mail/draft/charset.go
  • shortcuts/mail/draft/model.go
  • shortcuts/mail/filecheck/filecheck.go
  • shortcuts/mail/emlbuilder/builder.go

Comment thread shortcuts/mail/helpers.go Outdated
@evandance evandance force-pushed the feat/errs-migrate-mail branch from f5c6aa7 to 381c14e Compare June 4, 2026 04:09

@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/draft/charset.go`:
- Line 4: Remove the file-scoped "//nolint:forbidigo" in
shortcuts/mail/draft/charset.go and instead apply a targeted inline nolint on
the specific fmt.Errorf call inside encodeTextCharset (the "unsupported charset
%q" error) so only that call suppresses forbidigo; keep encodeTextCharset's
behavior unchanged (do not propagate the error) because encodedLeafBody in
shortcuts/mail/draft/serialize.go swallows/resets charset, but explicitly
narrowing the nolint makes the intent clearer and avoids silencing other
forbidigo uses in the 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: dc8b8c40-1f72-44de-912f-ad162455327e

📥 Commits

Reviewing files that changed from the base of the PR and between f5c6aa7 and 381c14e.

📒 Files selected for processing (52)
  • .golangci.yml
  • internal/errclass/classify.go
  • internal/errclass/codemeta_mail.go
  • lint/errscontract/rule_no_legacy_envelope_literal.go
  • shortcuts/mail/body_file.go
  • shortcuts/mail/draft/charset.go
  • shortcuts/mail/draft/large_attachment_parse.go
  • shortcuts/mail/draft/limits.go
  • shortcuts/mail/draft/model.go
  • shortcuts/mail/draft/parse.go
  • shortcuts/mail/draft/patch.go
  • shortcuts/mail/draft/patch_calendar.go
  • shortcuts/mail/draft/serialize.go
  • shortcuts/mail/draft/service.go
  • shortcuts/mail/emlbuilder/builder.go
  • shortcuts/mail/filecheck/filecheck.go
  • shortcuts/mail/flag_suggest.go
  • shortcuts/mail/flag_suggest_test.go
  • shortcuts/mail/helpers.go
  • shortcuts/mail/helpers_test.go
  • shortcuts/mail/large_attachment.go
  • shortcuts/mail/large_attachment_test.go
  • shortcuts/mail/lint/linter.go
  • shortcuts/mail/mail_decline_receipt.go
  • shortcuts/mail/mail_draft_create.go
  • shortcuts/mail/mail_draft_edit.go
  • shortcuts/mail/mail_draft_send.go
  • shortcuts/mail/mail_draft_send_test.go
  • shortcuts/mail/mail_errors.go
  • shortcuts/mail/mail_errors_test.go
  • shortcuts/mail/mail_forward.go
  • shortcuts/mail/mail_lint_html.go
  • shortcuts/mail/mail_message.go
  • shortcuts/mail/mail_reply.go
  • shortcuts/mail/mail_reply_all.go
  • shortcuts/mail/mail_send.go
  • shortcuts/mail/mail_send_receipt.go
  • shortcuts/mail/mail_share_to_chat.go
  • shortcuts/mail/mail_shortcut_validation_test.go
  • shortcuts/mail/mail_signature.go
  • shortcuts/mail/mail_template_create.go
  • shortcuts/mail/mail_template_update.go
  • shortcuts/mail/mail_thread.go
  • shortcuts/mail/mail_triage.go
  • shortcuts/mail/mail_triage_test.go
  • shortcuts/mail/mail_watch.go
  • shortcuts/mail/mail_watch_test.go
  • shortcuts/mail/signature/provider.go
  • shortcuts/mail/signature_compose.go
  • shortcuts/mail/signature_compose_test.go
  • shortcuts/mail/template_compose.go
  • shortcuts/register_test.go
✅ Files skipped from review due to trivial changes (11)
  • shortcuts/mail/draft/limits.go
  • shortcuts/mail/draft/large_attachment_parse.go
  • shortcuts/mail/draft/parse.go
  • shortcuts/mail/draft/patch.go
  • shortcuts/mail/draft/patch_calendar.go
  • shortcuts/mail/large_attachment_test.go
  • shortcuts/mail/mail_shortcut_validation_test.go
  • shortcuts/mail/draft/serialize.go
  • shortcuts/mail/draft/model.go
  • shortcuts/mail/filecheck/filecheck.go
  • shortcuts/mail/emlbuilder/builder.go
🚧 Files skipped from review as they are similar to previous changes (31)
  • shortcuts/mail/lint/linter.go
  • lint/errscontract/rule_no_legacy_envelope_literal.go
  • shortcuts/mail/mail_thread.go
  • shortcuts/mail/mail_signature.go
  • shortcuts/mail/mail_lint_html.go
  • shortcuts/register_test.go
  • shortcuts/mail/mail_share_to_chat.go
  • internal/errclass/classify.go
  • shortcuts/mail/mail_message.go
  • shortcuts/mail/helpers_test.go
  • shortcuts/mail/mail_reply.go
  • shortcuts/mail/mail_send.go
  • shortcuts/mail/mail_watch_test.go
  • internal/errclass/codemeta_mail.go
  • shortcuts/mail/mail_reply_all.go
  • shortcuts/mail/mail_decline_receipt.go
  • shortcuts/mail/mail_send_receipt.go
  • shortcuts/mail/flag_suggest.go
  • shortcuts/mail/mail_template_create.go
  • shortcuts/mail/mail_template_update.go
  • shortcuts/mail/mail_draft_send.go
  • shortcuts/mail/signature/provider.go
  • shortcuts/mail/mail_errors_test.go
  • shortcuts/mail/signature_compose.go
  • shortcuts/mail/template_compose.go
  • shortcuts/mail/signature_compose_test.go
  • shortcuts/mail/large_attachment.go
  • shortcuts/mail/mail_triage.go
  • shortcuts/mail/mail_watch.go
  • shortcuts/mail/mail_draft_edit.go
  • shortcuts/mail/helpers.go

Comment thread shortcuts/mail/draft/charset.go Outdated
@evandance evandance force-pushed the feat/errs-migrate-mail branch 4 times, most recently from f7cb07a to 0d9a104 Compare June 4, 2026 10:19
Replace every produced error path in shortcuts/mail with typed errs.* envelopes, so consumers get stable category, subtype, param/params, hint, retryable, and log_id metadata for classification and recovery instead of free-form message text.

- Locally constructed mail errors move from output.Err* / output.Errorf / final fmt.Errorf / common legacy helpers to errs.* builders, with structured params on multi-flag validation and failed-precondition states kept non-retryable.

- API-call failures move from runtime.CallAPI / DoAPIJSON legacy boundaries to runtime.CallAPITyped or runtime.ClassifyAPIResponse, and mail-specific enrichers read errs.ProblemOf so typed code, subtype, hint, and log_id metadata are preserved.

- Batch draft-send partial failures now use runtime.OutPartialFailure so successful and failed draft sends stay in stdout while the command exits through a typed multi-status signal.

- Add mail-domain typed helpers, mail API code metadata, and guard wiring to keep shortcuts/mail from reintroducing legacy envelopes or legacy API calls.

- Keep genuine intermediate fmt.Errorf wraps in parser/builder layers annotated with nolint comments; command-facing paths wrap them into typed validation, API, network, or internal errors.
@evandance evandance force-pushed the feat/errs-migrate-mail branch from 0d9a104 to 45af782 Compare June 4, 2026 11:54
@github-actions github-actions Bot added size/XL Architecture-level or global-impact change and removed size/L Large or sensitive change across domains or core paths labels Jun 4, 2026
Comment thread shortcuts/mail/mail_draft_send.go Outdated
When an account-level failure interrupts a batch send after some drafts
already went out, the command previously produced two machine-readable
failure results: the partial-failure ledger on stdout and a second error
envelope on stderr. Consumers could not tell which one to recover from.

The batch ledger is now the only failure result for that case: it gains
aborted and abort_error fields carrying the typed cause, so callers can
see which drafts were sent, which failed, why the batch stopped, and how
to recover — all from stdout. A --stop-on-error stop keeps these fields
unset because stopping early there is the caller's own choice.

@chanthuang chanthuang left a comment

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.

LGTM. The aborted +draft-send path now emits the batch ledger as the single failure result and returns the envelope-less partial-failure signal, so the previous double-envelope concern is resolved.\n\nVerified locally: ok github.com/larksuite/cli/shortcuts/mail (cached) and ok github.com/larksuite/cli/shortcuts/mail 1.669s
ok github.com/larksuite/cli/shortcuts/common 0.595s.\n\n

@evandance evandance merged commit 5e6a3eb into main Jun 4, 2026
20 checks passed
@evandance evandance deleted the feat/errs-migrate-mail branch June 4, 2026 13:02
tuxedomm pushed a commit to zhumiaoxin/cli that referenced this pull request Jun 6, 2026
…suite#1250)

* feat(mail): return typed error envelopes across the mail domain

Replace every produced error path in shortcuts/mail with typed errs.* envelopes, so consumers get stable category, subtype, param/params, hint, retryable, and log_id metadata for classification and recovery instead of free-form message text.

- Locally constructed mail errors move from output.Err* / output.Errorf / final fmt.Errorf / common legacy helpers to errs.* builders, with structured params on multi-flag validation and failed-precondition states kept non-retryable.

- API-call failures move from runtime.CallAPI / DoAPIJSON legacy boundaries to runtime.CallAPITyped or runtime.ClassifyAPIResponse, and mail-specific enrichers read errs.ProblemOf so typed code, subtype, hint, and log_id metadata are preserved.

- Batch draft-send partial failures now use runtime.OutPartialFailure so successful and failed draft sends stay in stdout while the command exits through a typed multi-status signal.

- Add mail-domain typed helpers, mail API code metadata, and guard wiring to keep shortcuts/mail from reintroducing legacy envelopes or legacy API calls.

- Keep genuine intermediate fmt.Errorf wraps in parser/builder layers annotated with nolint comments; command-facing paths wrap them into typed validation, API, network, or internal errors.

* fix(mail): report aborted draft-send batches as a single failure result

When an account-level failure interrupts a batch send after some drafts
already went out, the command previously produced two machine-readable
failure results: the partial-failure ledger on stdout and a second error
envelope on stderr. Consumers could not tell which one to recover from.

The batch ledger is now the only failure result for that case: it gains
aborted and abort_error fields carrying the typed cause, so callers can
see which drafts were sent, which failed, why the batch stopped, and how
to recover — all from stdout. A --stop-on-error stop keeps these fields
unset because stopping early there is the caller's own choice.
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 feature size/XL Architecture-level or global-impact change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants