Skip to content

feat(task): emit typed error envelopes across the task domain#1231

Merged
evandance merged 1 commit into
mainfrom
feat/errs-migrate-task
Jun 5, 2026
Merged

feat(task): emit typed error envelopes across the task domain#1231
evandance merged 1 commit into
mainfrom
feat/errs-migrate-task

Conversation

@evandance

@evandance evandance commented Jun 2, 2026

Copy link
Copy Markdown
Collaborator

Summary

Migrate the task domain's error output from the legacy exit-code envelope to typed errs.* errors. Every task command failure now carries a stable category, subtype, and recovery hint on a structured stderr envelope, so callers (AI agents, scripts) can branch on the error class instead of parsing free-text messages.

Changes

  • Replace legacy output.Err* / output.Exit* / &output.ExitError{} producers in shortcuts/task/ with typed errs.* builders; add local task helpers for file-input and transport errors.
  • Convert the task API error classifier so its category/subtype mirror internal/errclass/codemeta_task.go while preserving per-code recovery hints; the upload-attachment stream path now passes runtime classify context too.
  • Route client-side validation and local-parse failures to direct typed builders instead of fabricating API error codes.
  • Surface batch partial failures honestly with ok:false + non-zero exit while preserving per-item successes/failures; pretty output keeps the tasklist-create human summary.
  • Enforce the typed-only contract for shortcuts/task/ via golangci-lint and errscontract guards.

Exit codes are derived from the error category: input validation → 2, permission denied → 3, other API errors → 1, local internal failures → 5, partial batch failures → 1.

Test Plan

  • make unit-test
  • go vet ./...
  • gofmt -l .
  • go mod tidy in root and lint/
  • go test -C lint ./...
  • go run -C lint . ..
  • go run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.1.6 run --new-from-rev=origin/main

Related Issues

Part of the ongoing effort to migrate business domains to the typed errs.* error contract (task domain).

Summary by CodeRabbit

  • Refactor

    • Standardized typed errors across task shortcuts for clearer validation, upload, network and API-response messages; validations now indicate offending flags and include recovery hints. API calls and response parsing are centralized for consistent error behavior. Batch operations now surface per-item failures and return partial-failure status while preserving successes.
  • Tests

    • Expanded coverage for typed errors, malformed responses, partial-failure flows, lint contract guards, and API-error hinting/exit-code mappings.
  • Chores

    • Lint/config updated to support the new error-handling patterns.

@evandance evandance added enhancement New feature or request size/M Single-domain feat or fix with limited business impact labels Jun 2, 2026
@coderabbitai

coderabbitai Bot commented Jun 2, 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

Migrate shortcuts/task from legacy output/WrapTaskError to typed github.com/larksuite/cli/errs across builders, command handlers, upload flows, tasklist flows, shared helpers, tests, and lint config.

Changes

Task Shortcut Error Handling Migration

Layer / File(s) Summary
Lint config update
.golangci.yml
Forbidigo path-except updated to include shortcuts/task/ for errs rules.
Typed error foundation
shortcuts/task/task_errors.go, shortcuts/task/task_util.go, shortcuts/task/task_util_test.go, assorted helpers/tests
Add wrapTaskNetworkErr, taskInputStatError, taskAPIHints; rework HandleTaskApiResult and parseRelativeTime to use typed errs; add callTaskAPITyped and tests verifying API-code mapping, hints, malformed responses, and error shaping.
Validation helpers & builders
shortcuts/task/shortcuts.go, shortcuts/task/task_update.go, shortcuts/task/task_query_helpers.go, tests
Body builders and timestamp/query parsers return errs.NewValidationError/errs.NewInternalError with .WithParam() where applicable; tests assert typed errors and exit-code mapping.
Task command migrations
shortcuts/task/* (assign, followers, comment, complete, get-my/related, reopen, search, set_ancestor, subscribe_event, update)
Commands now call callTaskAPITyped and return typed validation/internal/API errors rather than legacy wrappers and manual JSON-unmarshal error wrapping; imports updated and tests added/modified.
Attachment upload
shortcuts/task/task_upload_attachment.go, shortcuts/task/task_upload_attachment_test.go
File prechecks produce typed validation/internal errors; multipart and file I/O errors use typed internal errors; network errors wrapped via wrapTaskNetworkErr; response parse failures yield typed internal errors; tests updated.
Tasklist flows & partial failures
shortcuts/task/task_tasklist_search.go, shortcuts/task/tasklist_members.go, shortcuts/task/tasklist_create.go, shortcuts/task/tasklist_add_task.go, tests
JSON parse failures -> errs.NewInternalError; flag validation -> errs.NewValidationError; per-item failure metadata sourced from errs.ProblemOf(err); partial failures return runtime.OutPartialFailure.
Tests and assertions
shortcuts/task/*_test.go
Many tests updated/added to assert concrete errs types/subtypes, use errs.ProblemOf and output.ExitCodeOf for exit-code checks, and cover malformed responses and partial-failure behavior.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • larksuite/cli#736: related upload-attachment implementation and baseline tests touching the same shortcut.
  • larksuite/cli#740: overlapping typed-errs refactor affecting similar task-shortcut files.
  • larksuite/cli#984: related lint-rule and errs-typed-only rollout changes.

Suggested labels

feature

Suggested reviewers

  • tengchengwei
  • liangshuo-1

"🐰 I hopped through code and said hello,
Errors dressed up neat in typed garb,
Validation hops where rivers flow,
Hints stitched on each API barb.
Cheers from a rabbit in the yard!"

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 45.71% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(task): emit typed error envelopes across the task domain' accurately reflects the main objective of the pull request—migrating task domain error handling to typed errs.* errors. It is concise, specific, and clearly summarizes the primary change without using vague terms or noise.
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.
Description check ✅ Passed PR description includes all required template sections with comprehensive detail on motivation, changes, testing approach, and related context.

✏️ 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 feat/errs-migrate-task

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 and usage tips.

@github-actions github-actions Bot added domain/task PR touches the task domain size/L Large or sensitive change across domains or core paths and removed size/M Single-domain feat or fix with limited business impact labels Jun 2, 2026
@github-actions

github-actions Bot commented Jun 2, 2026

Copy link
Copy Markdown

🚀 PR Preview Install Guide

🧰 CLI update

npm i -g https://pkg.pr.new/larksuite/cli/@larksuite/cli@173289eb00eb7618943efd0c614f93a7d48b992f

🧩 Skill update

npx skills add larksuite/cli#feat/errs-migrate-task -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: 5

🧹 Nitpick comments (2)
shortcuts/task/tasklist_add_task_test.go (1)

92-111: ⚡ Quick win

Parse the JSON and assert the per-item failure fields directly.

Substring checks can still pass if failed_tasks[].code or failed_tasks[].hint regress, or if the subtype string appears elsewhere in the payload.

Suggested diff
 import (
+	"encoding/json"
 	"strings"
 	"testing"
@@
 	out := stdout.String()
+	var payload struct {
+		FailedTasks []struct {
+			Guid string `json:"guid"`
+			Type string `json:"type"`
+			Code string `json:"code"`
+			Hint string `json:"hint"`
+		} `json:"failed_tasks"`
+	}
+	if err := json.Unmarshal([]byte(out), &payload); err != nil {
+		t.Fatalf("expected JSON output, got %v; body=%s", err, out)
+	}
@@
-	if !strings.Contains(out, string(errs.SubtypePermissionDenied)) {
-		t.Errorf("expected typed subtype %q in failed_tasks, got: %s", errs.SubtypePermissionDenied, out)
-	}
-	if !strings.Contains(out, string(errs.SubtypeNotFound)) {
-		t.Errorf("expected typed subtype %q in failed_tasks, got: %s", errs.SubtypeNotFound, out)
-	}
+	if len(payload.FailedTasks) != 2 {
+		t.Fatalf("expected 2 failed tasks, got %#v", payload.FailedTasks)
+	}
+	if payload.FailedTasks[0].Type != string(errs.SubtypePermissionDenied) && payload.FailedTasks[1].Type != string(errs.SubtypePermissionDenied) {
+		t.Errorf("expected a permission-denied failure, got %#v", payload.FailedTasks)
+	}
+	if payload.FailedTasks[0].Type != string(errs.SubtypeNotFound) && payload.FailedTasks[1].Type != string(errs.SubtypeNotFound) {
+		t.Errorf("expected a not-found failure, got %#v", payload.FailedTasks)
+	}
+	for _, f := range payload.FailedTasks {
+		if f.Code == "" || f.Hint == "" {
+			t.Errorf("expected typed code and hint for %#v", f)
+		}
+	}
🤖 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/task/tasklist_add_task_test.go` around lines 92 - 111, Parse the
stdout JSON in the task list test and assert the per-item failure fields
directly instead of using substring checks. Update the assertions around the
existing tasklist_add_task_test behavior to inspect the failed_tasks entries by
field, verifying the expected code and hint values for each failed task while
still checking the successful task entry and ensuring the legacy subtype/type
shape does not appear.
shortcuts/task/tasklist_create.go (1)

135-137: ⚡ Quick win

Keep the JSON decode cause in the per-task typed error.

This path drops parseErr, so batch failures lose the invalid-response reason while the other tasklist parse paths keep it.

Suggested diff
-					if tErr == nil {
-						if json.Unmarshal(tResp.RawBody, &tResult) != nil {
-							tErr = errs.NewInternalError(errs.SubtypeInvalidResponse, "failed to parse task response")
-						}
-					}
+					if tErr == nil {
+						if parseErr := json.Unmarshal(tResp.RawBody, &tResult); parseErr != nil {
+							tErr = errs.NewInternalError(errs.SubtypeInvalidResponse, "failed to parse task response: %v", parseErr)
+						}
+					}
🤖 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/task/tasklist_create.go` around lines 135 - 137, The error handling
in the JSON unmarshalling part of the per-task logic drops the original parse
error, losing valuable debugging information. In the code block inside
tasklist_create.go where json.Unmarshal is called on tResp.RawBody, capture the
error from Unmarshal into a variable (e.g., parseErr) and pass this error as the
cause when creating the new errs.NewInternalError. This keeps the original JSON
decode cause wrapped inside the typed error for better traceability.
🤖 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/task/task_assign.go`:
- Around line 37-39: The Validate function currently only rejects completely
empty --add/--remove but allows comma/whitespace-only values; before accepting
flags, parse each of runtime.Str("add") and runtime.Str("remove") using the same
trimming/splitting/filtering logic as buildMembersBody (or call
buildMembersBody) and if the resulting member slice is empty while the original
flag was non-empty, return errs.NewValidationError(errs.SubtypeInvalidArgument,
"must specify either --add or --remove") (or a similar non-empty-members
message) to reject comma/whitespace-only inputs.

In `@shortcuts/task/task_followers.go`:
- Around line 37-39: The Validate function should reject comma/whitespace-only
follower lists: after checking runtime.Str("add") / runtime.Str("remove")
non-empty, run the same parsing/cleanup used by buildFollowersBody (split on
commas, trim whitespace, filter out empty entries) for each of the provided
flags and if the resultant slice is empty treat it as invalid and return a
validation error; update Validate (the Validate: func(ctx context.Context,
runtime *common.RuntimeContext) error block) to call that parsing logic (or call
buildFollowersBody if available) for "add" and "remove" and return
errs.NewValidationError(errs.SubtypeInvalidArgument, "must specify at least one
non-empty follower in --add/--remove") when a flag is present but yields no
non-blank followers.

In `@shortcuts/task/task_upload_attachment.go`:
- Around line 96-99: The Stat error path uses a misleading literal "file not
found" message; update the call site where fio.Stat(filePath) error is handled
to use a neutral, accurate prefix (e.g., "stat failed" or "unable to stat file")
when calling taskInputStatError(err, ...), and ensure the original err is
preserved so the real filesystem error (permission denied, etc.) is included in
the returned/enveloped message; change only the message string passed to
taskInputStatError at the fio.Stat error branch.
- Around line 92-95: The check that treats a nil runtime.FileIO() as an invalid
argument should be changed to an internal failure: in task_upload_attachment.go,
locate the runtime.FileIO() call and the branch that returns
errs.NewValidationError(errs.SubtypeInvalidArgument, ...), and instead return
the internal error subtype (e.g., errs.SubtypeInternal) or an appropriate
internal error constructor so that a missing FileIO provider (variable fio ==
nil) is classified as an internal/runtime wiring failure rather than a user
validation error.

In `@shortcuts/task/task_util.go`:
- Around line 160-165: The branch that handles !hasCode must not delegate to
common.HandleApiResult (which reintroduces legacy output errors); instead,
remove the call to common.HandleApiResult and return a task-domain typed
response/error for the malformed-response case using the local types: inspect
result and action, build and return the typed envelope (e.g., a nil data + a
typed "invalid/malformed response" error value or an ApiResult with an explicit
error Code) so the task domain contract remains enforced; update the !hasCode
branch in task_util.go (replace the common.HandleApiResult call) to construct
and return that typed error/envelope.

---

Nitpick comments:
In `@shortcuts/task/tasklist_add_task_test.go`:
- Around line 92-111: Parse the stdout JSON in the task list test and assert the
per-item failure fields directly instead of using substring checks. Update the
assertions around the existing tasklist_add_task_test behavior to inspect the
failed_tasks entries by field, verifying the expected code and hint values for
each failed task while still checking the successful task entry and ensuring the
legacy subtype/type shape does not appear.

In `@shortcuts/task/tasklist_create.go`:
- Around line 135-137: The error handling in the JSON unmarshalling part of the
per-task logic drops the original parse error, losing valuable debugging
information. In the code block inside tasklist_create.go where json.Unmarshal is
called on tResp.RawBody, capture the error from Unmarshal into a variable (e.g.,
parseErr) and pass this error as the cause when creating the new
errs.NewInternalError. This keeps the original JSON decode cause wrapped inside
the typed error for better traceability.
🪄 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: 5f007437-6664-4a1a-add4-20b7f4cdabfe

📥 Commits

Reviewing files that changed from the base of the PR and between 04932c2 and 360567e.

📒 Files selected for processing (27)
  • .golangci.yml
  • shortcuts/task/shortcuts.go
  • shortcuts/task/task_assign.go
  • shortcuts/task/task_body_test.go
  • shortcuts/task/task_comment.go
  • shortcuts/task/task_complete.go
  • shortcuts/task/task_errors.go
  • shortcuts/task/task_followers.go
  • shortcuts/task/task_get_my_tasks.go
  • shortcuts/task/task_get_related_tasks.go
  • shortcuts/task/task_query_helpers.go
  • shortcuts/task/task_query_helpers_test.go
  • shortcuts/task/task_reminder.go
  • shortcuts/task/task_reopen.go
  • shortcuts/task/task_search.go
  • shortcuts/task/task_set_ancestor.go
  • shortcuts/task/task_subscribe_event.go
  • shortcuts/task/task_tasklist_search.go
  • shortcuts/task/task_update.go
  • shortcuts/task/task_upload_attachment.go
  • shortcuts/task/task_upload_attachment_test.go
  • shortcuts/task/task_util.go
  • shortcuts/task/task_util_test.go
  • shortcuts/task/tasklist_add_task.go
  • shortcuts/task/tasklist_add_task_test.go
  • shortcuts/task/tasklist_create.go
  • shortcuts/task/tasklist_members.go

Comment thread shortcuts/task/task_assign.go
Comment thread shortcuts/task/task_followers.go
Comment thread shortcuts/task/task_upload_attachment.go
Comment thread shortcuts/task/task_upload_attachment.go
Comment thread shortcuts/task/task_util.go Outdated
@codecov

codecov Bot commented Jun 2, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 75.27473% with 45 lines in your changes missing coverage. Please review.
✅ Project coverage is 70.97%. Comparing base (6367aaa) to head (173289e).

Files with missing lines Patch % Lines
shortcuts/task/task_upload_attachment.go 43.75% 9 Missing ⚠️
shortcuts/task/task_reminder.go 25.00% 6 Missing ⚠️
shortcuts/task/task_query_helpers.go 42.85% 4 Missing ⚠️
shortcuts/task/tasklist_create.go 87.50% 3 Missing and 1 partial ⚠️
shortcuts/task/shortcuts.go 50.00% 3 Missing ⚠️
shortcuts/task/task_followers.go 25.00% 3 Missing ⚠️
shortcuts/task/task_get_my_tasks.go 75.00% 3 Missing ⚠️
shortcuts/task/task_update.go 50.00% 3 Missing ⚠️
shortcuts/task/task_comment.go 0.00% 2 Missing ⚠️
shortcuts/task/task_complete.go 60.00% 1 Missing and 1 partial ⚠️
... and 4 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1231      +/-   ##
==========================================
+ Coverage   70.46%   70.97%   +0.51%     
==========================================
  Files         679      680       +1     
  Lines       65611    65318     -293     
==========================================
+ Hits        46231    46362     +131     
+ Misses      15726    15316     -410     
+ Partials     3654     3640      -14     

☔ 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.

@evandance evandance force-pushed the feat/errs-migrate-task branch from 360567e to 2e0369f Compare June 3, 2026 03:16

@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

Caution

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

⚠️ Outside diff range comments (3)
shortcuts/task/shortcuts.go (1)

183-187: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Dry-run still hides these validation failures behind an exit-0 payload.

buildTaskCreateBody now produces typed validation errors, but DryRun converts them into {"error": ...} instead of letting the command fail through the validation path. That makes --data, --due, and missing --summary behave differently in dry-run versus normal execution, and it loses the exit-2 typed envelope this migration is aiming for. Move these checks into Validate (or otherwise surface the typed error before DryRun runs).

Based on learnings: in larksuite/cli E2E dry-run tests, validation failures from the Validate callback must exit with code 2 and be asserted via result.Stdout + result.Stderr, while DryRun-callback errors stay exit 0.

🤖 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/task/shortcuts.go` around lines 183 - 187, The DryRun
implementation in DryRun: func(ctx context.Context, runtime
*common.RuntimeContext) currently calls buildTaskCreateBody and swallows typed
validation errors by returning common.NewDryRunAPI().Set("error", err.Error());
instead, move the input validation logic that buildTaskCreateBody now performs
into the command's Validate callback (or ensure Validate calls the same
validation helpers) so validation failures are produced before DryRun runs and
surface as typed exit-2 validation errors; update DryRun to assume
buildTaskCreateBody never returns validation errors (remove the Set("error",
...) conversion) and only handle non-validation runtime errors there, keeping
function names DryRun, buildTaskCreateBody and Validate as the hooks to modify.
shortcuts/task/task_util.go (1)

35-37: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Keep overflowed relative offsets on the validation path.

A huge value like +999999999999999999h matches the regex but makes strconv.Atoi fail here, and returning that raw error downgrades user input into an untyped internal failure. Map this branch to the same typed validation error shape as the format mismatch path.

Suggested fix
 	amount, err := strconv.Atoi(amountStr)
 	if err != nil {
-		return time.Time{}, err
+		return time.Time{}, errs.NewValidationError(
+			errs.SubtypeInvalidArgument,
+			"invalid relative time format: %s",
+			s,
+		)
 	}
🤖 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/task/task_util.go` around lines 35 - 37, The atoi call at "amount,
err := strconv.Atoi(amountStr)" can fail for huge offsets and currently returns
the raw strconv error; change this branch to return the same typed validation
error used for the format-mismatch path (the regex/format validation branch) so
overflowed relative offsets are treated as a user validation error rather than
an internal error. Locate the format-mismatch error construction in this file
and mirror its error type/message/shape when handling the strconv.Atoi error
(instead of returning err) so callers receive a consistent validation error for
bad relative-offset input.
shortcuts/task/tasklist_create.go (1)

97-103: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don't swallow worker panics as silent success.

This recover() only logs to stderr and exits the goroutine. It never records a failed item, so the new len(failedTasks) > 0 gate at Line 173 can still report overall success even though one requested task creation blew up. Please convert the recovered panic into a failed entry (or propagate a typed internal error after wg.Wait) instead of only printing it.

🤖 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/task/tasklist_create.go` around lines 97 - 103, The defer inside
the anonymous goroutine currently calls recover() and only prints to stderr,
which swallows the failure; change that defer (inside the goroutine defined at
go func(idx int, tDef map[string]interface{})) to capture the recovered value
and record it as a failedTasks entry (or send a typed error on the existing
error/failure channel) before calling wg.Done so the outer logic that checks
len(failedTasks) sees the failure; ensure you synchronize access to failedTasks
(use the same mutex used elsewhere or create a failure channel and append after
wg.Wait), include the panic value and contextual info (idx and tDef identifier)
in the recorded error, and keep the existing stderr logging as supplemental.
♻️ Duplicate comments (1)
shortcuts/task/task_util.go (1)

107-112: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Keep malformed task responses typed instead of falling back to the legacy helper.

At this point err is already nil, so !hasCode is a malformed-response case, not a transport case. Delegating to common.HandleApiResult reintroduces the legacy output path into a migrated package and breaks the typed-envelope contract the PR is trying to enforce.

Suggested fix
 	if !hasCode {
-		// No code field (e.g. network/transport error): delegate to the shared
-		// helper. common.HandleApiResult still emits legacy errors; migrating the
-		// shared helper layer is out of scope for the task domain.
-		data, err := common.HandleApiResult(result, err, action)
-		return data, err
+		return nil, errs.NewInternalError(
+			errs.SubtypeInvalidResponse,
+			"%s: missing code in task API response",
+			action,
+		)
 	}
🤖 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/task/task_util.go` around lines 107 - 112, The branch that checks
!hasCode currently delegates to common.HandleApiResult, which reintroduces
legacy output handling; instead, detect the malformed-response case (err is nil
and !hasCode) and return a typed malformed-response error from this package (do
not call common.HandleApiResult). Update the code inside the !hasCode block to
construct and return the package's specific error type (e.g.,
ErrMalformedTaskResponse or a new typed error) along with a nil data value,
including contextual info from result and action to aid debugging, so callers
keep the typed-envelope contract.
🤖 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/task/task_util.go`:
- Around line 115-116: The code currently discards the error from
util.ToFloat64(codeVal) causing non-numeric "code" values to be treated as 0;
change the logic in the function that reads codeVal (the block that assigns
code, err := util.ToFloat64(codeVal)) to check the conversion error and treat a
failed conversion as an invalid response by returning SubtypeInvalidResponse (or
the function's equivalent invalid response path) before converting to larkCode
and branching; specifically, stop ignoring the error from util.ToFloat64, use
the error to detect bad payloads, and only set larkCode := int(code) if
conversion succeeded so malformed values like {"code":"oops"} are rejected.

In `@shortcuts/task/tasklist_create.go`:
- Around line 173-175: The partial-failure branch in tasklist_create.go returns
via runtime.OutPartialFailure when len(failedTasks) > 0, which skips the
subsequent runtime.OutFormat(..., writer) human-readable output; change the
control flow so you call runtime.OutFormat(outData, writer) (the same formatter
used for the success path) before invoking runtime.OutPartialFailure(outData,
nil) so the formatted text is emitted and then the partial-failure exit signal
is returned; reference the failedTasks check, outData, writer, runtime.OutFormat
and runtime.OutPartialFailure (and the ctx.emit behavior in
shortcuts/common/runner.go) to locate and update the code.

---

Outside diff comments:
In `@shortcuts/task/shortcuts.go`:
- Around line 183-187: The DryRun implementation in DryRun: func(ctx
context.Context, runtime *common.RuntimeContext) currently calls
buildTaskCreateBody and swallows typed validation errors by returning
common.NewDryRunAPI().Set("error", err.Error()); instead, move the input
validation logic that buildTaskCreateBody now performs into the command's
Validate callback (or ensure Validate calls the same validation helpers) so
validation failures are produced before DryRun runs and surface as typed exit-2
validation errors; update DryRun to assume buildTaskCreateBody never returns
validation errors (remove the Set("error", ...) conversion) and only handle
non-validation runtime errors there, keeping function names DryRun,
buildTaskCreateBody and Validate as the hooks to modify.

In `@shortcuts/task/task_util.go`:
- Around line 35-37: The atoi call at "amount, err := strconv.Atoi(amountStr)"
can fail for huge offsets and currently returns the raw strconv error; change
this branch to return the same typed validation error used for the
format-mismatch path (the regex/format validation branch) so overflowed relative
offsets are treated as a user validation error rather than an internal error.
Locate the format-mismatch error construction in this file and mirror its error
type/message/shape when handling the strconv.Atoi error (instead of returning
err) so callers receive a consistent validation error for bad relative-offset
input.

In `@shortcuts/task/tasklist_create.go`:
- Around line 97-103: The defer inside the anonymous goroutine currently calls
recover() and only prints to stderr, which swallows the failure; change that
defer (inside the goroutine defined at go func(idx int, tDef
map[string]interface{})) to capture the recovered value and record it as a
failedTasks entry (or send a typed error on the existing error/failure channel)
before calling wg.Done so the outer logic that checks len(failedTasks) sees the
failure; ensure you synchronize access to failedTasks (use the same mutex used
elsewhere or create a failure channel and append after wg.Wait), include the
panic value and contextual info (idx and tDef identifier) in the recorded error,
and keep the existing stderr logging as supplemental.

---

Duplicate comments:
In `@shortcuts/task/task_util.go`:
- Around line 107-112: The branch that checks !hasCode currently delegates to
common.HandleApiResult, which reintroduces legacy output handling; instead,
detect the malformed-response case (err is nil and !hasCode) and return a typed
malformed-response error from this package (do not call common.HandleApiResult).
Update the code inside the !hasCode block to construct and return the package's
specific error type (e.g., ErrMalformedTaskResponse or a new typed error) along
with a nil data value, including contextual info from result and action to aid
debugging, so callers keep the typed-envelope contract.
🪄 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: 76cf4480-f81b-4d64-b4c5-e1a00fd149d9

📥 Commits

Reviewing files that changed from the base of the PR and between 360567e and 2e0369f.

📒 Files selected for processing (27)
  • .golangci.yml
  • shortcuts/task/shortcuts.go
  • shortcuts/task/task_assign.go
  • shortcuts/task/task_body_test.go
  • shortcuts/task/task_comment.go
  • shortcuts/task/task_complete.go
  • shortcuts/task/task_errors.go
  • shortcuts/task/task_followers.go
  • shortcuts/task/task_get_my_tasks.go
  • shortcuts/task/task_get_related_tasks.go
  • shortcuts/task/task_query_helpers.go
  • shortcuts/task/task_query_helpers_test.go
  • shortcuts/task/task_reminder.go
  • shortcuts/task/task_reopen.go
  • shortcuts/task/task_search.go
  • shortcuts/task/task_set_ancestor.go
  • shortcuts/task/task_subscribe_event.go
  • shortcuts/task/task_tasklist_search.go
  • shortcuts/task/task_update.go
  • shortcuts/task/task_upload_attachment.go
  • shortcuts/task/task_upload_attachment_test.go
  • shortcuts/task/task_util.go
  • shortcuts/task/task_util_test.go
  • shortcuts/task/tasklist_add_task.go
  • shortcuts/task/tasklist_add_task_test.go
  • shortcuts/task/tasklist_create.go
  • shortcuts/task/tasklist_members.go
🚧 Files skipped from review as they are similar to previous changes (20)
  • shortcuts/task/task_get_related_tasks.go
  • .golangci.yml
  • shortcuts/task/task_reopen.go
  • shortcuts/task/task_set_ancestor.go
  • shortcuts/task/task_complete.go
  • shortcuts/task/task_assign.go
  • shortcuts/task/task_upload_attachment_test.go
  • shortcuts/task/task_subscribe_event.go
  • shortcuts/task/task_query_helpers.go
  • shortcuts/task/task_get_my_tasks.go
  • shortcuts/task/task_tasklist_search.go
  • shortcuts/task/task_query_helpers_test.go
  • shortcuts/task/task_search.go
  • shortcuts/task/task_upload_attachment.go
  • shortcuts/task/task_errors.go
  • shortcuts/task/tasklist_members.go
  • shortcuts/task/task_update.go
  • shortcuts/task/task_reminder.go
  • shortcuts/task/task_followers.go
  • shortcuts/task/task_body_test.go

Comment thread shortcuts/task/task_util.go Outdated
Comment thread shortcuts/task/tasklist_create.go Outdated
@evandance evandance force-pushed the feat/errs-migrate-task branch from 2e0369f to 1463d40 Compare June 3, 2026 03:49

@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/task/task_reminder.go (1)

68-160: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Parse --set before the initial task GET.

A malformed --set value still reaches runtime.DoAPI() before the local strconv.Atoi validation runs, so bad client input can currently surface auth/network/API errors instead of the typed validation error this migration is supposed to guarantee. Move the --set parsing above the first GET and reuse the parsed minutes later in the add-reminders branch.

Suggested direction
 Execute: func(ctx context.Context, runtime *common.RuntimeContext) error {
 	taskId := url.PathEscape(runtime.Str("task-id"))
 	queryParams := make(larkcore.QueryParams)
 	queryParams.Set("user_id_type", "open_id")

+	var minutes int
+	if setStr := runtime.Str("set"); setStr != "" {
+		var parseErr error
+		switch {
+		case strings.HasSuffix(setStr, "m"):
+			minutes, parseErr = strconv.Atoi(strings.TrimSuffix(setStr, "m"))
+		case strings.HasSuffix(setStr, "h"):
+			var h int
+			h, parseErr = strconv.Atoi(strings.TrimSuffix(setStr, "h"))
+			if parseErr == nil {
+				minutes = h * 60
+			}
+		case strings.HasSuffix(setStr, "d"):
+			var d int
+			d, parseErr = strconv.Atoi(strings.TrimSuffix(setStr, "d"))
+			if parseErr == nil {
+				minutes = d * 24 * 60
+			}
+		default:
+			minutes, parseErr = strconv.Atoi(setStr)
+		}
+		if parseErr != nil {
+			return errs.NewValidationError(errs.SubtypeInvalidArgument, "invalid --set value %q: %v", setStr, parseErr)
+		}
+	}
+
 	// First, get the task to find existing reminders
 	getResp, err := runtime.DoAPI(&larkcore.ApiReq{
 		HttpMethod:  http.MethodGet,
 		ApiPath:     "/open-apis/task/v2/tasks/" + taskId,
 		QueryParams: queryParams,
 	})
@@
-		} else if setStr := runtime.Str("set"); setStr != "" {
-			// Parse relative time string (e.g. 15m, 1h, 1d, or plain 30)
-			var minutes int
-			var parseErr error
-			...
-			if parseErr != nil {
-				return errs.NewValidationError(errs.SubtypeInvalidArgument, "%v", parseErr)
-			}
+		} else if runtime.Str("set") != "" {
 			// If any reminders exist, remove them first
🤖 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/task/task_reminder.go` around lines 68 - 160, Move the
parsing/validation of runtime.Str("set") to the top of Execute so malformed
values are handled before any API calls: compute and validate the minutes
integer (currently done with variables minutes and parseErr and strconv.Atoi
logic) right after reading setStr and before the initial runtime.DoAPI GET,
returning the validation error if parseErr != nil; then remove the duplicate
parsing in the add-reminders branch and reuse the previously computed minutes
when building the add-reminders request payload. Ensure the existing flow for
remove and no-set cases remains unchanged and reference the Execute function,
setStr/runtime.Str("set"), minutes, parseErr, and the add-reminders branch where
minutes is used.
♻️ Duplicate comments (1)
shortcuts/task/task_util.go (1)

113-117: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Reject fractional API code values before truncating.

util.ToFloat64 accepts numeric payloads like 0.5, and int(code) then truncates that to 0, which makes a malformed response look like success. This path should treat any non-integer numeric code as errs.SubtypeInvalidResponse before branching on larkCode.

Suggested fix
+// add `math` to the imports above
 	code, ok := util.ToFloat64(codeVal)
 	if !ok {
 		return nil, errs.NewInternalError(errs.SubtypeInvalidResponse, "%s: malformed response (non-numeric code %v)", action, codeVal)
 	}
+	if code != math.Trunc(code) {
+		return nil, errs.NewInternalError(errs.SubtypeInvalidResponse, "%s: malformed response (non-integer code %v)", action, codeVal)
+	}
 	larkCode := int(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/task/task_util.go` around lines 113 - 117, The code currently uses
util.ToFloat64 to parse codeVal and then casts to int, which silently truncates
fractional values; update the validation in the parsing block (around
util.ToFloat64, codeVal, larkCode) to reject any numeric value that is not an
exact integer: after obtaining code (float64) and ok, check that code ==
math.Trunc(code) (or use math.Modf) and if it is fractional return
errs.NewInternalError(errs.SubtypeInvalidResponse, "... malformed response
(non-integer code %v)", codeVal); only then safely convert to int and proceed.
🤖 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/task/tasklist_create_test.go`:
- Around line 101-123: The test currently registers an HTTP POST stub before
invoking runMountedTaskShortcut so a client-side invalid --data JSON error can
occur after the POST; change the test so the invalid JSON is validated before
any remote call: in TestCreateTasklist_InvalidDataJSON remove or defer
reg.Register(...) and instead call runMountedTaskShortcut with args and assert
it returns the typed *errs.ValidationError; alternatively keep the stub but
assert reg received zero POSTs after runMountedTaskShortcut to ensure no request
was sent; reference CreateTasklist, runMountedTaskShortcut, reg.Register, and
TestCreateTasklist_InvalidDataJSON when making the change.

---

Outside diff comments:
In `@shortcuts/task/task_reminder.go`:
- Around line 68-160: Move the parsing/validation of runtime.Str("set") to the
top of Execute so malformed values are handled before any API calls: compute and
validate the minutes integer (currently done with variables minutes and parseErr
and strconv.Atoi logic) right after reading setStr and before the initial
runtime.DoAPI GET, returning the validation error if parseErr != nil; then
remove the duplicate parsing in the add-reminders branch and reuse the
previously computed minutes when building the add-reminders request payload.
Ensure the existing flow for remove and no-set cases remains unchanged and
reference the Execute function, setStr/runtime.Str("set"), minutes, parseErr,
and the add-reminders branch where minutes is used.

---

Duplicate comments:
In `@shortcuts/task/task_util.go`:
- Around line 113-117: The code currently uses util.ToFloat64 to parse codeVal
and then casts to int, which silently truncates fractional values; update the
validation in the parsing block (around util.ToFloat64, codeVal, larkCode) to
reject any numeric value that is not an exact integer: after obtaining code
(float64) and ok, check that code == math.Trunc(code) (or use math.Modf) and if
it is fractional return errs.NewInternalError(errs.SubtypeInvalidResponse, "...
malformed response (non-integer code %v)", codeVal); only then safely convert to
int and proceed.
🪄 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: eb37c3b3-a13a-402e-bc2c-7e34a15b023f

📥 Commits

Reviewing files that changed from the base of the PR and between 2e0369f and 1463d40.

📒 Files selected for processing (35)
  • .golangci.yml
  • shortcuts/task/shortcuts.go
  • shortcuts/task/task_assign.go
  • shortcuts/task/task_assign_test.go
  • shortcuts/task/task_body_test.go
  • shortcuts/task/task_comment.go
  • shortcuts/task/task_complete.go
  • shortcuts/task/task_errors.go
  • shortcuts/task/task_errors_test.go
  • shortcuts/task/task_followers.go
  • shortcuts/task/task_followers_test.go
  • shortcuts/task/task_get_my_tasks.go
  • shortcuts/task/task_get_my_tasks_test.go
  • shortcuts/task/task_get_related_tasks.go
  • shortcuts/task/task_query_helpers.go
  • shortcuts/task/task_query_helpers_test.go
  • shortcuts/task/task_reminder.go
  • shortcuts/task/task_reminder_test.go
  • shortcuts/task/task_reopen.go
  • shortcuts/task/task_search.go
  • shortcuts/task/task_search_test.go
  • shortcuts/task/task_set_ancestor.go
  • shortcuts/task/task_subscribe_event.go
  • shortcuts/task/task_tasklist_search.go
  • shortcuts/task/task_update.go
  • shortcuts/task/task_upload_attachment.go
  • shortcuts/task/task_upload_attachment_test.go
  • shortcuts/task/task_util.go
  • shortcuts/task/task_util_test.go
  • shortcuts/task/tasklist_add_task.go
  • shortcuts/task/tasklist_add_task_test.go
  • shortcuts/task/tasklist_create.go
  • shortcuts/task/tasklist_create_test.go
  • shortcuts/task/tasklist_members.go
  • shortcuts/task/tasklist_members_test.go
🚧 Files skipped from review as they are similar to previous changes (21)
  • shortcuts/task/task_get_related_tasks.go
  • shortcuts/task/task_set_ancestor.go
  • shortcuts/task/task_comment.go
  • shortcuts/task/task_complete.go
  • shortcuts/task/task_assign.go
  • shortcuts/task/task_query_helpers_test.go
  • shortcuts/task/task_body_test.go
  • shortcuts/task/task_subscribe_event.go
  • shortcuts/task/task_followers.go
  • shortcuts/task/tasklist_add_task.go
  • shortcuts/task/task_update.go
  • shortcuts/task/task_tasklist_search.go
  • shortcuts/task/task_query_helpers.go
  • shortcuts/task/task_errors.go
  • shortcuts/task/task_get_my_tasks.go
  • shortcuts/task/tasklist_create.go
  • shortcuts/task/tasklist_add_task_test.go
  • shortcuts/task/tasklist_members.go
  • shortcuts/task/shortcuts.go
  • .golangci.yml
  • shortcuts/task/task_search.go

Comment thread shortcuts/task/tasklist_create_test.go
@evandance evandance force-pushed the feat/errs-migrate-task branch from 1463d40 to c42cfe9 Compare June 3, 2026 05:41

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

♻️ Duplicate comments (1)
shortcuts/task/tasklist_create.go (1)

143-145: ⚠️ Potential issue | 🟠 Major | ⚖️ Poor tradeoff

Partial failures still bypass the text formatter.

Returning here skips the runtime.OutFormat(..., writer) block at lines 147-174, so the human-readable summary (including the failed tasks list) is never rendered in text mode when any sub-task fails. Consider emitting the formatted output first, then returning the partial-failure signal:

if len(failedTasks) > 0 {
    // Emit formatted output first
    runtime.OutFormat(outData, nil, func(w io.Writer) {
        fmt.Fprintf(w, "✅ Tasklist created successfully!\n")
        fmt.Fprintf(w, "Tasklist Name: %s\n", tasklistName)
        fmt.Fprintf(w, "Tasklist ID: %s\n", tasklistGuid)
        if tasklistUrl != "" {
            fmt.Fprintf(w, "Tasklist URL: %s\n", tasklistUrl)
        }
        if len(tasks) > 0 {
            fmt.Fprintln(w, strings.Repeat("-", 20))
            fmt.Fprintf(w, "Tasks created: %d/%d\n", len(createdTasks), len(tasks))
            for _, t := range createdTasks {
                guid, _ := t["guid"].(string)
                urlVal, _ := t["url"].(string)
                fmt.Fprintf(w, "  - ID: %s", guid)
                if urlVal != "" {
                    fmt.Fprintf(w, ", URL: %s", urlVal)
                }
                fmt.Fprintln(w)
            }
            if len(failedTasks) > 0 {
                fmt.Fprintf(w, "\nFailed tasks:\n")
                for _, f := range failedTasks {
                    fmt.Fprintf(w, "  - %s\n", f)
                }
            }
        }
    })
    // Then signal partial failure
    return runtime.OutPartialFailure(outData, nil)
}
🤖 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/task/tasklist_create.go` around lines 143 - 145, The
partial-failure early return (when len(failedTasks) > 0) bypasses the
human-readable text formatter; change the flow in the block that checks
failedTasks so you call runtime.OutFormat(outData, nil, func(w io.Writer) { ...
}) to emit the formatted summary (include tasklistName, tasklistGuid,
tasklistUrl, count and details from createdTasks, and list failedTasks) before
returning; after emitting formatted output, return
runtime.OutPartialFailure(outData, nil) so the text output is shown and the
partial-failure signal is still returned.
🤖 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.

Duplicate comments:
In `@shortcuts/task/tasklist_create.go`:
- Around line 143-145: The partial-failure early return (when len(failedTasks) >
0) bypasses the human-readable text formatter; change the flow in the block that
checks failedTasks so you call runtime.OutFormat(outData, nil, func(w io.Writer)
{ ... }) to emit the formatted summary (include tasklistName, tasklistGuid,
tasklistUrl, count and details from createdTasks, and list failedTasks) before
returning; after emitting formatted output, return
runtime.OutPartialFailure(outData, nil) so the text output is shown and the
partial-failure signal is still returned.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a0374f6b-b76d-49f8-89e1-2e6d8e0203cb

📥 Commits

Reviewing files that changed from the base of the PR and between 1463d40 and c42cfe9.

📒 Files selected for processing (37)
  • .golangci.yml
  • shortcuts/task/shortcuts.go
  • shortcuts/task/task_assign.go
  • shortcuts/task/task_assign_test.go
  • shortcuts/task/task_body_test.go
  • shortcuts/task/task_comment.go
  • shortcuts/task/task_complete.go
  • shortcuts/task/task_errors.go
  • shortcuts/task/task_errors_test.go
  • shortcuts/task/task_followers.go
  • shortcuts/task/task_followers_test.go
  • shortcuts/task/task_get_my_tasks.go
  • shortcuts/task/task_get_my_tasks_test.go
  • shortcuts/task/task_get_related_tasks.go
  • shortcuts/task/task_query_helpers.go
  • shortcuts/task/task_query_helpers_test.go
  • shortcuts/task/task_reminder.go
  • shortcuts/task/task_reminder_test.go
  • shortcuts/task/task_reopen.go
  • shortcuts/task/task_search.go
  • shortcuts/task/task_search_test.go
  • shortcuts/task/task_set_ancestor.go
  • shortcuts/task/task_subscribe_event.go
  • shortcuts/task/task_subscribe_event_test.go
  • shortcuts/task/task_tasklist_search.go
  • shortcuts/task/task_tasklist_search_test.go
  • shortcuts/task/task_update.go
  • shortcuts/task/task_upload_attachment.go
  • shortcuts/task/task_upload_attachment_test.go
  • shortcuts/task/task_util.go
  • shortcuts/task/task_util_test.go
  • shortcuts/task/tasklist_add_task.go
  • shortcuts/task/tasklist_add_task_test.go
  • shortcuts/task/tasklist_create.go
  • shortcuts/task/tasklist_create_test.go
  • shortcuts/task/tasklist_members.go
  • shortcuts/task/tasklist_members_test.go
🚧 Files skipped from review as they are similar to previous changes (21)
  • shortcuts/task/task_complete.go
  • shortcuts/task/task_update.go
  • shortcuts/task/task_errors.go
  • shortcuts/task/task_upload_attachment.go
  • shortcuts/task/tasklist_add_task_test.go
  • shortcuts/task/task_errors_test.go
  • shortcuts/task/task_assign.go
  • shortcuts/task/shortcuts.go
  • shortcuts/task/task_query_helpers.go
  • .golangci.yml
  • shortcuts/task/task_upload_attachment_test.go
  • shortcuts/task/task_tasklist_search.go
  • shortcuts/task/task_reopen.go
  • shortcuts/task/task_util_test.go
  • shortcuts/task/tasklist_add_task.go
  • shortcuts/task/task_search_test.go
  • shortcuts/task/tasklist_create_test.go
  • shortcuts/task/task_util.go
  • shortcuts/task/task_body_test.go
  • shortcuts/task/task_query_helpers_test.go
  • shortcuts/task/task_reminder.go

@evandance evandance force-pushed the feat/errs-migrate-task branch 6 times, most recently from 122ca57 to 5ee283f Compare June 4, 2026 13:22
tengchengwei
tengchengwei previously approved these changes Jun 5, 2026
@evandance evandance force-pushed the feat/errs-migrate-task branch 2 times, most recently from a28b974 to c5cd8b9 Compare June 5, 2026 05:17
Task commands now return structured, typed errors instead of the legacy
exit-code envelope: every failure carries a stable category, subtype, and
recovery hint, so callers can branch on the error class instead of parsing
messages. Exit codes derive from the error category — input validation exits 2,
a permission denial exits 3, other API errors exit 1.

Batch operations (adding tasks to a tasklist, creating a tasklist with tasks)
now report partial failure honestly: the per-item successes and failures stay
on stdout and the command exits non-zero instead of masking failures as a
success.
@evandance evandance force-pushed the feat/errs-migrate-task branch from c5cd8b9 to 173289e Compare June 5, 2026 12:12
@evandance evandance merged commit 8c3cba1 into main Jun 5, 2026
20 checks passed
@evandance evandance deleted the feat/errs-migrate-task branch June 5, 2026 14:30
SunPeiYang996 pushed a commit that referenced this pull request Jun 8, 2026
Task commands now return structured, typed errors instead of the legacy
exit-code envelope: every failure carries a stable category, subtype, and
recovery hint, so callers can branch on the error class instead of parsing
messages. Exit codes derive from the error category — input validation exits 2,
a permission denial exits 3, other API errors exit 1.

Batch operations (adding tasks to a tasklist, creating a tasklist with tasks)
now report partial failure honestly: the per-item successes and failures stay
on stdout and the command exits non-zero instead of masking failures as a
success.
@coderabbitai coderabbitai Bot mentioned this pull request Jun 18, 2026
6 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

domain/task PR touches the task domain enhancement New feature or request size/L Large or sensitive change across domains or core paths

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants