@@ -86,7 +86,9 @@ async function cleanupRunChildren(
8686 const runIds = await db
8787 . select ( { id : copilotRuns . id } )
8888 . from ( copilotRuns )
89- . where ( and ( inArray ( copilotRuns . workspaceId , workspaceIds ) , lt ( copilotRuns . updatedAt , retentionDate ) ) )
89+ . where (
90+ and ( inArray ( copilotRuns . workspaceId , workspaceIds ) , lt ( copilotRuns . updatedAt , retentionDate ) )
91+ )
9092 . limit ( BATCH_SIZE * MAX_BATCHES_PER_TABLE )
9193
9294 if ( runIds . length === 0 ) {
@@ -172,25 +174,54 @@ async function resolvePayload(payload: CleanupJobPayload): Promise<{
172174}
173175
174176export async function runCleanupTasks ( payload : CleanupJobPayload ) : Promise < void > {
175- const startTime = Date . now ( )
177+ const startTime = Date . now ( )
176178
177- const resolved = await resolvePayload ( payload )
178- if ( ! resolved ) return
179+ const resolved = await resolvePayload ( payload )
180+ if ( ! resolved ) return
179181
180- const { workspaceIds, retentionHours, tierLabel } = resolved
182+ const { workspaceIds, retentionHours, tierLabel } = resolved
181183
182- if ( workspaceIds . length === 0 ) {
183- logger . info ( `[${ tierLabel } ] No workspaces to process` )
184- return
185- }
184+ if ( workspaceIds . length === 0 ) {
185+ logger . info ( `[${ tierLabel } ] No workspaces to process` )
186+ return
187+ }
186188
187- const retentionDate = new Date ( Date . now ( ) - retentionHours * 60 * 60 * 1000 )
188- logger . info (
189- `[${ tierLabel } ] Processing ${ workspaceIds . length } workspaces, cutoff: ${ retentionDate . toISOString ( ) } `
189+ const retentionDate = new Date ( Date . now ( ) - retentionHours * 60 * 60 * 1000 )
190+ logger . info (
191+ `[${ tierLabel } ] Processing ${ workspaceIds . length } workspaces, cutoff: ${ retentionDate . toISOString ( ) } `
192+ )
193+
194+ // Collect chat IDs before deleting so we can clean up the copilot backend after
195+ const doomedChats = await db
196+ . select ( { id : copilotChats . id } )
197+ . from ( copilotChats )
198+ . where (
199+ and (
200+ inArray ( copilotChats . workspaceId , workspaceIds ) ,
201+ lt ( copilotChats . updatedAt , retentionDate )
202+ )
190203 )
204+ . limit ( BATCH_SIZE * MAX_BATCHES_PER_TABLE )
205+
206+ const doomedChatIds = doomedChats . map ( ( c ) => c . id )
207+
208+ // Prepare chat cleanup (collect file keys + copilot backend call) BEFORE DB deletion
209+ const chatCleanup = await prepareChatCleanup ( doomedChatIds , tierLabel )
210+
211+ // Delete run children first (checkpoints, tool calls) since they reference runs
212+ const runChildResults = await cleanupRunChildren ( workspaceIds , retentionDate , tierLabel )
213+ for ( const r of runChildResults ) {
214+ if ( r . deleted > 0 ) logger . info ( `[${ r . table } ] ${ r . deleted } deleted` )
215+ }
191216
192- // Collect chat IDs before deleting so we can clean up the copilot backend after
193- const doomedChats = await db
217+ // Delete feedback — no direct workspaceId, find via copilotChats
218+ const feedbackResult : TableCleanupResult = {
219+ table : `${ tierLabel } /copilotFeedback` ,
220+ deleted : 0 ,
221+ failed : 0 ,
222+ }
223+ try {
224+ const chatIds = await db
194225 . select ( { id : copilotChats . id } )
195226 . from ( copilotChats )
196227 . where (
@@ -201,90 +232,66 @@ export async function runCleanupTasks(payload: CleanupJobPayload): Promise<void>
201232 )
202233 . limit ( BATCH_SIZE * MAX_BATCHES_PER_TABLE )
203234
204- const doomedChatIds = doomedChats . map ( ( c ) => c . id )
205-
206- // Prepare chat cleanup (collect file keys + copilot backend call) BEFORE DB deletion
207- const chatCleanup = await prepareChatCleanup ( doomedChatIds , tierLabel )
208-
209- // Delete run children first (checkpoints, tool calls) since they reference runs
210- const runChildResults = await cleanupRunChildren ( workspaceIds , retentionDate , tierLabel )
211- for ( const r of runChildResults ) {
212- if ( r . deleted > 0 ) logger . info ( `[${ r . table } ] ${ r . deleted } deleted` )
213- }
214-
215- // Delete feedback — no direct workspaceId, find via copilotChats
216- const feedbackResult : TableCleanupResult = {
217- table : `${ tierLabel } /copilotFeedback` ,
218- deleted : 0 ,
219- failed : 0 ,
220- }
221- try {
222- const chatIds = await db
223- . select ( { id : copilotChats . id } )
224- . from ( copilotChats )
235+ if ( chatIds . length > 0 ) {
236+ const deleted = await db
237+ . delete ( copilotFeedback )
225238 . where (
226- and (
227- inArray ( copilotChats . workspaceId , workspaceIds ) ,
228- lt ( copilotChats . updatedAt , retentionDate )
239+ inArray (
240+ copilotFeedback . chatId ,
241+ chatIds . map ( ( c ) => c . id )
229242 )
230243 )
231- . limit ( BATCH_SIZE * MAX_BATCHES_PER_TABLE )
232-
233- if ( chatIds . length > 0 ) {
234- const deleted = await db
235- . delete ( copilotFeedback )
236- . where ( inArray ( copilotFeedback . chatId , chatIds . map ( ( c ) => c . id ) ) )
237- . returning ( { id : copilotFeedback . feedbackId } )
238- feedbackResult . deleted = deleted . length
239- logger . info ( `[${ feedbackResult . table } ] Deleted ${ deleted . length } rows` )
240- } else {
241- logger . info ( `[${ feedbackResult . table } ] No expired rows found` )
242- }
243- } catch ( error ) {
244- feedbackResult . failed ++
245- logger . error ( `[${ feedbackResult . table } ] Delete failed:` , { error } )
244+ . returning ( { id : copilotFeedback . feedbackId } )
245+ feedbackResult . deleted = deleted . length
246+ logger . info ( `[${ feedbackResult . table } ] Deleted ${ deleted . length } rows` )
247+ } else {
248+ logger . info ( `[${ feedbackResult . table } ] No expired rows found` )
246249 }
250+ } catch ( error ) {
251+ feedbackResult . failed ++
252+ logger . error ( `[${ feedbackResult . table } ] Delete failed:` , { error } )
253+ }
247254
248- // Delete copilot runs (has workspaceId directly, cascades checkpoints)
249- const runsResult = await cleanupTable (
250- copilotRuns ,
251- copilotRuns . workspaceId ,
252- copilotRuns . updatedAt ,
253- workspaceIds ,
254- retentionDate ,
255- `${ tierLabel } /copilotRuns`
256- )
257-
258- // Delete copilot chats (has workspaceId directly)
259- const chatsResult = await cleanupTable (
260- copilotChats ,
261- copilotChats . workspaceId ,
262- copilotChats . updatedAt ,
263- workspaceIds ,
264- retentionDate ,
265- `${ tierLabel } /copilotChats`
266- )
267-
268- // Delete mothership inbox tasks (has workspaceId directly)
269- const inboxResult = await cleanupTable (
270- mothershipInboxTask ,
271- mothershipInboxTask . workspaceId ,
272- mothershipInboxTask . createdAt ,
273- workspaceIds ,
274- retentionDate ,
275- `${ tierLabel } /mothershipInboxTask`
276- )
277-
278- const totalDeleted =
279- runChildResults . reduce ( ( s , r ) => s + r . deleted , 0 ) +
280- runsResult . deleted +
281- chatsResult . deleted +
282- inboxResult . deleted
283-
284- logger . info ( `[${ tierLabel } ] Complete: ${ totalDeleted } total rows deleted` )
285-
286- // Clean up copilot backend + storage files after DB rows are gone
287- await chatCleanup . execute ( )
255+ // Delete copilot runs (has workspaceId directly, cascades checkpoints)
256+ const runsResult = await cleanupTable (
257+ copilotRuns ,
258+ copilotRuns . workspaceId ,
259+ copilotRuns . updatedAt ,
260+ workspaceIds ,
261+ retentionDate ,
262+ `${ tierLabel } /copilotRuns`
263+ )
264+
265+ // Delete copilot chats (has workspaceId directly)
266+ const chatsResult = await cleanupTable (
267+ copilotChats ,
268+ copilotChats . workspaceId ,
269+ copilotChats . updatedAt ,
270+ workspaceIds ,
271+ retentionDate ,
272+ `${ tierLabel } /copilotChats`
273+ )
274+
275+ // Delete mothership inbox tasks (has workspaceId directly)
276+ const inboxResult = await cleanupTable (
277+ mothershipInboxTask ,
278+ mothershipInboxTask . workspaceId ,
279+ mothershipInboxTask . createdAt ,
280+ workspaceIds ,
281+ retentionDate ,
282+ `${ tierLabel } /mothershipInboxTask`
283+ )
284+
285+ const totalDeleted =
286+ runChildResults . reduce ( ( s , r ) => s + r . deleted , 0 ) +
287+ runsResult . deleted +
288+ chatsResult . deleted +
289+ inboxResult . deleted
290+
291+ logger . info ( `[${ tierLabel } ] Complete: ${ totalDeleted } total rows deleted` )
292+
293+ // Clean up copilot backend + storage files after DB rows are gone
294+ await chatCleanup . execute ( )
288295
289296 const timeElapsed = ( Date . now ( ) - startTime ) / 1000
290297 logger . info ( `Task cleanup completed in ${ timeElapsed . toFixed ( 2 ) } s` )
0 commit comments