From f72f5e6edecb48c6a9d078b863ddcdc865884d42 Mon Sep 17 00:00:00 2001 From: Dotta Date: Sun, 16 Aug 2026 20:04:51 +0000 Subject: [PATCH] fix(feedback): isolate delivery route setup Keep unrelated issue routes independent of the delivery-state read model and publish the operator retry endpoint in OpenAPI. Co-Authored-By: Paperclip --- .../issue-agent-mutation-ownership-routes.test.ts | 3 +++ server/src/routes/issues.ts | 11 ++++++++--- server/src/routes/openapi.ts | 7 +++++++ 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/server/src/__tests__/issue-agent-mutation-ownership-routes.test.ts b/server/src/__tests__/issue-agent-mutation-ownership-routes.test.ts index 950c155e65e..711fbd1d055 100644 --- a/server/src/__tests__/issue-agent-mutation-ownership-routes.test.ts +++ b/server/src/__tests__/issue-agent-mutation-ownership-routes.test.ts @@ -205,6 +205,9 @@ function registerRouteMocks() { listIssueVotesForUser: vi.fn(async () => []), saveIssueVote: vi.fn(async () => ({ vote: null, consentEnabledNow: false, sharingEnabled: false })), }), + feedbackDeliveryCommentStateService: () => ({ + listForIssue: vi.fn(async () => new Map()), + }), goalService: () => ({}), heartbeatService: () => mockHeartbeatService, instanceSettingsService: () => ({ diff --git a/server/src/routes/issues.ts b/server/src/routes/issues.ts index f839ad19ba9..ca12fd884e1 100644 --- a/server/src/routes/issues.ts +++ b/server/src/routes/issues.ts @@ -2767,7 +2767,12 @@ export function issueRoutes( const goalsSvc = goalService(db); const issueApprovalsSvc = issueApprovalService(db); const recoveryActionsSvc = issueRecoveryActionService(db); - const feedbackDeliveryStatesSvc = feedbackDeliveryCommentStateService(db); + // Build this query service only for the two feedback-delivery routes that + // use it. Most issue routes do not read delivery state, so route setup must + // not couple them to this optional read model. + const listFeedbackDeliveryForIssue = ( + input: Parameters["listForIssue"]>[0], + ) => feedbackDeliveryCommentStateService(db).listForIssue(input); const executionWorkspacesSvc = executionWorkspaceServiceDirect(db); const workProductsSvc = workProductService(db); const documentsSvc = documentService(db); @@ -10779,7 +10784,7 @@ export function issueRoutes( // banner to act on, and the run reference stays gated behind the same // board/user authority that guards run detail. const isOperatorViewer = req.actor.type !== "agent"; - const feedbackDeliveryByComment = await feedbackDeliveryStatesSvc.listForIssue({ + const feedbackDeliveryByComment = await listFeedbackDeliveryForIssue({ companyId: issue.companyId, issueId: issue.id, viewerCanRetry: isOperatorViewer, @@ -10812,7 +10817,7 @@ export function issueRoutes( const actor = getActorInfo(req); const readStates = () => - feedbackDeliveryStatesSvc.listForIssue({ + listFeedbackDeliveryForIssue({ companyId: issue.companyId, issueId: issue.id, viewerCanRetry: true, diff --git a/server/src/routes/openapi.ts b/server/src/routes/openapi.ts index c855ea3eb6a..9f70416e9bd 100644 --- a/server/src/routes/openapi.ts +++ b/server/src/routes/openapi.ts @@ -6838,6 +6838,13 @@ registerCurrentRoute({ summary: "Retry a scheduled issue run now", }); +registerCurrentRoute({ + method: "post", + path: "/api/issues/{id}/feedback-delivery/retry", + tags: ["issues"], + summary: "Retry exhausted human feedback delivery", +}); + registerCurrentRoute({ method: "post", path: "/api/issues/{id}/monitor/check-now",