Skip to content

Commit 01d85af

Browse files
committed
Fix stale failure handler overwriting resolved approvals and migration pending count
- ProjectionPipeline: add early return when existingRow is already resolved in the stale failure code path, preventing decision from being overwritten with null on already-resolved approvals. - Migration step 4: add AND status = 'pending' condition so stale failure backfill does not overwrite rows already resolved by earlier steps. - Migration step 5: replace activity-based pending_approval_count derivation with a direct count from projection_pending_approvals WHERE status = 'pending', consistent with the runtime's refreshThreadShellSummary. Applied via @cursor push command
1 parent b6bcd14 commit 01d85af

2 files changed

Lines changed: 12 additions & 38 deletions

File tree

apps/server/src/orchestration/Layers/ProjectionPipeline.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1268,6 +1268,9 @@ const makeOrchestrationProjectionPipeline = Effect.fn("makeOrchestrationProjecti
12681268
if (Option.isNone(existingRow)) {
12691269
return;
12701270
}
1271+
if (existingRow.value.status === "resolved") {
1272+
return;
1273+
}
12711274
yield* projectionPendingApprovalRepository.upsert({
12721275
requestId,
12731276
threadId: existingRow.value.threadId,

apps/server/src/persistence/Migrations/024_BackfillProjectionThreadShellSummary.ts

Lines changed: 9 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -175,11 +175,12 @@ export default Effect.gen(function* () {
175175
FROM latest_stale_failures
176176
WHERE latest_stale_failures.request_id = projection_pending_approvals.request_id
177177
)
178-
WHERE EXISTS (
179-
SELECT 1
180-
FROM latest_stale_failures
181-
WHERE latest_stale_failures.request_id = projection_pending_approvals.request_id
182-
)
178+
WHERE status = 'pending'
179+
AND EXISTS (
180+
SELECT 1
181+
FROM latest_stale_failures
182+
WHERE latest_stale_failures.request_id = projection_pending_approvals.request_id
183+
)
183184
`;
184185

185186
yield* sql`
@@ -192,40 +193,10 @@ export default Effect.gen(function* () {
192193
AND message.role = 'user'
193194
),
194195
pending_approval_count = COALESCE((
195-
WITH latest_approval_states AS (
196-
SELECT
197-
latest.request_id,
198-
latest.kind,
199-
latest.detail
200-
FROM (
201-
SELECT
202-
json_extract(activity.payload_json, '$.requestId') AS request_id,
203-
activity.kind,
204-
lower(COALESCE(json_extract(activity.payload_json, '$.detail'), '')) AS detail,
205-
ROW_NUMBER() OVER (
206-
PARTITION BY json_extract(activity.payload_json, '$.requestId')
207-
ORDER BY activity.created_at DESC, activity.activity_id DESC
208-
) AS row_number
209-
FROM projection_thread_activities AS activity
210-
WHERE activity.thread_id = projection_threads.thread_id
211-
AND json_extract(activity.payload_json, '$.requestId') IS NOT NULL
212-
AND activity.kind IN (
213-
'approval.requested',
214-
'approval.resolved',
215-
'provider.approval.respond.failed'
216-
)
217-
) AS latest
218-
WHERE latest.row_number = 1
219-
)
220196
SELECT COUNT(*)
221-
FROM latest_approval_states
222-
WHERE latest_approval_states.kind = 'approval.requested'
223-
OR (
224-
latest_approval_states.kind = 'provider.approval.respond.failed'
225-
AND latest_approval_states.detail NOT LIKE '%stale pending approval request%'
226-
AND latest_approval_states.detail NOT LIKE '%unknown pending approval request%'
227-
AND latest_approval_states.detail NOT LIKE '%unknown pending permission request%'
228-
)
197+
FROM projection_pending_approvals
198+
WHERE projection_pending_approvals.thread_id = projection_threads.thread_id
199+
AND projection_pending_approvals.status = 'pending'
229200
), 0),
230201
pending_user_input_count = COALESCE((
231202
WITH latest_user_input_states AS (

0 commit comments

Comments
 (0)