diff --git a/src/app/api/documents/[id]/reindex/route.ts b/src/app/api/documents/[id]/reindex/route.ts index f2a05b0f74..054391878f 100644 --- a/src/app/api/documents/[id]/reindex/route.ts +++ b/src/app/api/documents/[id]/reindex/route.ts @@ -243,12 +243,21 @@ export async function POST(request: Request, { params }: { params: Promise<{ id: throw new Error(`Failed to enqueue reindex job: ${jobError.message}; competing-job check failed: ${competingJobsError.message}`); } if ((competingJobs?.length ?? 0) === 0) { - const { error: rollbackError } = await supabase + let rollbackQuery = supabase .from("documents") .update(rollbackDocumentPayload) .eq("id", id) .eq("owner_id", user.id) .eq("updated_at", rollbackFence); + if (!atomicReindex) { + rollbackQuery = rollbackQuery + .eq("status", "queued") + .is("error_message", null) + .eq("page_count", 0) + .eq("chunk_count", 0) + .eq("image_count", 0); + } + const { error: rollbackError } = await rollbackQuery; if (rollbackError) { throw new Error(`Failed to enqueue reindex job: ${jobError.message}; rollback failed: ${rollbackError.message}`); } diff --git a/src/app/api/documents/bulk/reindex/route.ts b/src/app/api/documents/bulk/reindex/route.ts index 06391bfcb6..267d31d21d 100644 --- a/src/app/api/documents/bulk/reindex/route.ts +++ b/src/app/api/documents/bulk/reindex/route.ts @@ -237,12 +237,21 @@ export async function POST(request: Request) { ); } if ((competingJobs?.length ?? 0) === 0) { - const { error: rollbackError } = await supabase + let rollbackQuery = supabase .from("documents") .update(rollbackDocumentPayload) .eq("id", document.id) .eq("owner_id", user.id) .eq("updated_at", rollbackFence); + if (!atomicReindex) { + rollbackQuery = rollbackQuery + .eq("status", "queued") + .is("error_message", null) + .eq("page_count", 0) + .eq("chunk_count", 0) + .eq("image_count", 0); + } + const { error: rollbackError } = await rollbackQuery; if (rollbackError) { throw new Error(`Failed to enqueue bulk reindex job: ${jobError.message}; rollback failed: ${rollbackError.message}`); } diff --git a/tests/private-access-routes.test.ts b/tests/private-access-routes.test.ts index 0176254fbd..1fb70e3174 100644 --- a/tests/private-access-routes.test.ts +++ b/tests/private-access-routes.test.ts @@ -1644,6 +1644,11 @@ describe("private document API access", () => { // cannot revert a newer queue state written by an overlapping request. const fence = (documentUpdates[0]?.updatePayload as { updated_at?: string }).updated_at; expect(documentUpdates[1]?.filters).toContainEqual({ column: "updated_at", value: fence }); + expect(documentUpdates[1]?.filters).toContainEqual({ column: "status", value: "queued" }); + expect(documentUpdates[1]?.filters).toContainEqual({ column: "error_message", value: null }); + expect(documentUpdates[1]?.filters).toContainEqual({ column: "page_count", value: 0 }); + expect(documentUpdates[1]?.filters).toContainEqual({ column: "chunk_count", value: 0 }); + expect(documentUpdates[1]?.filters).toContainEqual({ column: "image_count", value: 0 }); }); it("skips single-document rollback when a competing active job appears after the safety check", async () => { @@ -1765,6 +1770,11 @@ describe("private document API access", () => { // reindex route — the rollback matches on the stamp this request wrote. const fence = (documentUpdates[0]?.updatePayload as { updated_at?: string }).updated_at; expect(documentUpdates[1]?.filters).toContainEqual({ column: "updated_at", value: fence }); + expect(documentUpdates[1]?.filters).toContainEqual({ column: "status", value: "queued" }); + expect(documentUpdates[1]?.filters).toContainEqual({ column: "error_message", value: null }); + expect(documentUpdates[1]?.filters).toContainEqual({ column: "page_count", value: 0 }); + expect(documentUpdates[1]?.filters).toContainEqual({ column: "chunk_count", value: 0 }); + expect(documentUpdates[1]?.filters).toContainEqual({ column: "image_count", value: 0 }); }); it("skips bulk rollback when a competing active job appears after the safety check", async () => {