From f1334aac80e5e9b752a7d016ab598601ab7fef23 Mon Sep 17 00:00:00 2001 From: liangxu Date: Wed, 8 Jul 2026 22:32:15 +0800 Subject: [PATCH] fix(tenant): finalize definitive/manual-retry publish tasks as failed Add normalizeDesktopTaskCompletionStatusForTask so an "unknown" publish completion is downgraded to "failed" when the payload is a manual retry or the error matches a definitive publish failure, instead of leaving it stuck as unknown. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../tenant/app/desktop_task_service.go | 20 ++++++++++- .../tenant/app/desktop_task_service_test.go | 34 +++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/server/internal/tenant/app/desktop_task_service.go b/server/internal/tenant/app/desktop_task_service.go index 5982aec..425ba6d 100644 --- a/server/internal/tenant/app/desktop_task_service.go +++ b/server/internal/tenant/app/desktop_task_service.go @@ -1217,7 +1217,7 @@ func (s *DesktopTaskService) Complete(ctx context.Context, client *repository.De previousTask, _ := taskRepo.GetByDesktopID(ctx, desktopID, client.WorkspaceID) previousAttemptID := activeAttemptID(previousTask) if previousTask != nil { - finalStatus = normalizeDesktopTaskCompletionStatusForKind(previousTask.Kind, finalStatus) + finalStatus = normalizeDesktopTaskCompletionStatusForTask(previousTask.Kind, finalStatus, previousTask.Payload, errorJSON) } task, err := taskRepo.Complete(ctx, desktopID, HashDesktopClientToken(strings.TrimSpace(req.LeaseToken)), finalStatus, resultJSON, errorJSON) @@ -1937,6 +1937,24 @@ func normalizeDesktopTaskCompletionStatusForKind(kind string, status string) str return strings.TrimSpace(status) } +func normalizeDesktopTaskCompletionStatusForTask(kind string, status string, payloadJSON []byte, errorJSON []byte) string { + normalizedStatus := normalizeDesktopTaskCompletionStatusForKind(kind, status) + if strings.TrimSpace(kind) != "publish" || normalizedStatus != "unknown" { + return normalizedStatus + } + if boolValueFromMap(unmarshalJSONObject(payloadJSON), "manual_retry") { + return "failed" + } + if publishUnknownTaskHasDefinitiveFailure(&repository.DesktopTask{ + Kind: "publish", + Status: "unknown", + Error: errorJSON, + }) { + return "failed" + } + return normalizedStatus +} + func normalizeDesktopTaskViewStatus(kind string, status string) string { switch strings.TrimSpace(kind) { case "monitor": diff --git a/server/internal/tenant/app/desktop_task_service_test.go b/server/internal/tenant/app/desktop_task_service_test.go index 3353f40..70e5906 100644 --- a/server/internal/tenant/app/desktop_task_service_test.go +++ b/server/internal/tenant/app/desktop_task_service_test.go @@ -204,6 +204,40 @@ func TestNormalizeDesktopTaskCompletionStatusForKindFailsMonitorUnknown(t *testi } } +func TestNormalizeDesktopTaskCompletionStatusForTaskFailsDefinitivePublishUnknown(t *testing.T) { + t.Parallel() + + got := normalizeDesktopTaskCompletionStatusForTask( + "publish", + "unknown", + nil, + []byte(`{"publish_submit_uncertain":true,"code":"toutiaohao_publish_failed","message":"标题长度应该在2-30字之间"}`), + ) + if got != "failed" { + t.Fatalf("publish title validation completion status = %q, want failed", got) + } + + got = normalizeDesktopTaskCompletionStatusForTask( + "publish", + "unknown", + nil, + []byte(`{"publish_submit_uncertain":true,"message":"network interrupted after submit"}`), + ) + if got != "unknown" { + t.Fatalf("ambiguous publish completion status = %q, want unknown", got) + } + + got = normalizeDesktopTaskCompletionStatusForTask( + "publish", + "unknown", + []byte(`{"manual_retry":true}`), + []byte(`{"publish_submit_uncertain":true,"message":"network interrupted after submit"}`), + ) + if got != "failed" { + t.Fatalf("manual retry publish completion status = %q, want failed", got) + } +} + func TestExpiredMonitorDesktopTasksCandidateSQLSelectsOnlyExpiredInProgressMonitorTasks(t *testing.T) { t.Parallel()