From d930a28694ffc904f8a6b64212e2722f4a6c82d2 Mon Sep 17 00:00:00 2001 From: Dotta Date: Sat, 9 May 2026 22:54:04 -0500 Subject: [PATCH] Address final recovery review items --- packages/shared/src/validators/issue.test.ts | 14 ++++++++++++++ packages/shared/src/validators/issue.ts | 18 ++++++++---------- server/src/services/recovery/service.ts | 4 ---- 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/packages/shared/src/validators/issue.test.ts b/packages/shared/src/validators/issue.test.ts index a57396889c6..d4f5921f8c9 100644 --- a/packages/shared/src/validators/issue.test.ts +++ b/packages/shared/src/validators/issue.test.ts @@ -85,6 +85,20 @@ describe("issue validators", () => { ).toBe(false); }); + it("rejects recovery outcomes that are not supported by the source-scoped resolution endpoint", () => { + expect( + resolveIssueRecoveryActionSchema.safeParse({ + outcome: "delegated", + }).success, + ).toBe(false); + + expect( + resolveIssueRecoveryActionSchema.safeParse({ + outcome: "escalated", + }).success, + ).toBe(false); + }); + it("normalizes escaped line breaks in issue comment bodies", () => { const parsed = addIssueCommentSchema.parse({ body: "Progress update\\r\\n\\r\\nNext action.", diff --git a/packages/shared/src/validators/issue.ts b/packages/shared/src/validators/issue.ts index 87b462ed638..55c5bac0665 100644 --- a/packages/shared/src/validators/issue.ts +++ b/packages/shared/src/validators/issue.ts @@ -202,21 +202,19 @@ export const issueRecoveryActionReadModelSchema = z.object({ export type IssueRecoveryActionReadModel = z.infer; +const RESOLVE_ISSUE_RECOVERY_ACTION_OUTCOMES = [ + "restored", + "false_positive", + "blocked", + "cancelled", +] as const; + export const resolveIssueRecoveryActionSchema = z.object({ actionId: z.string().uuid().optional(), - outcome: z.enum(ISSUE_RECOVERY_ACTION_OUTCOMES), + outcome: z.enum(RESOLVE_ISSUE_RECOVERY_ACTION_OUTCOMES), sourceIssueStatus: z.enum(["done", "in_review", "blocked"]).optional().nullable(), resolutionNote: multilineTextSchema.optional().nullable(), }).strict().superRefine((value, ctx) => { - if (value.outcome === "delegated" || value.outcome === "escalated") { - ctx.addIssue({ - code: z.ZodIssueCode.custom, - message: "This recovery outcome is not supported by the source-scoped resolution endpoint", - path: ["outcome"], - }); - return; - } - if (value.outcome === "restored") { if (value.sourceIssueStatus !== "done" && value.sourceIssueStatus !== "in_review") { ctx.addIssue({ diff --git a/server/src/services/recovery/service.ts b/server/src/services/recovery/service.ts index 30b2508b7f9..6dc91d241b6 100644 --- a/server/src/services/recovery/service.ts +++ b/server/src/services/recovery/service.ts @@ -1547,8 +1547,6 @@ export function recoveryService(db: Db, deps: { enqueueWakeup: RecoveryWakeup }) function strandedRecoveryActionFingerprint(input: { issue: typeof issues.$inferSelect; recoveryCause: StrandedRecoveryCause; - latestRun: LatestIssueRun; - successfulRunHandoffEvidence?: SuccessfulRunHandoffRecoveryEvidence | null; }) { return [ "source_scoped_recovery", @@ -1606,8 +1604,6 @@ export function recoveryService(db: Db, deps: { enqueueWakeup: RecoveryWakeup }) fingerprint: strandedRecoveryActionFingerprint({ issue: input.issue, recoveryCause, - latestRun: input.latestRun, - successfulRunHandoffEvidence: input.successfulRunHandoffEvidence, }), evidence: buildStrandedRecoveryActionEvidence({ issue: input.issue,