diff --git a/src/db.ts b/src/db.ts index a42bc36..3ca44f5 100644 --- a/src/db.ts +++ b/src/db.ts @@ -1411,6 +1411,17 @@ export function deleteTask(id: string): void { } } +export function hasActiveCiWatcherForChat(chatJid: string): boolean { + const row = db + .prepare( + `SELECT 1 FROM scheduled_tasks + WHERE chat_jid = ? AND status = 'active' AND prompt LIKE '[BACKGROUND CI WATCH]%' + LIMIT 1`, + ) + .get(chatJid); + return !!row; +} + export function getDueTasks(): ScheduledTask[] { const now = new Date().toISOString(); return db diff --git a/src/paired-execution-context.ts b/src/paired-execution-context.ts index d552e2f..1f7e050 100644 --- a/src/paired-execution-context.ts +++ b/src/paired-execution-context.ts @@ -10,6 +10,7 @@ import { getLatestOpenPairedTaskForChat, getPairedTaskById, getPairedWorkspace, + hasActiveCiWatcherForChat, updatePairedTask, upsertPairedProject, } from './db.js'; @@ -390,6 +391,17 @@ export function completePairedExecutionContext(args: { } } + // Active CI watcher → skip auto-review until watcher completes. + // The watcher result will be posted via reviewer bot, triggering + // the owner to act on it, which then resumes the review loop. + if (hasActiveCiWatcherForChat(task.chat_jid)) { + logger.info( + { taskId, chatJid: task.chat_jid }, + 'Active CI watcher found, deferring auto-review until watcher completes', + ); + return; + } + // Normal turn → auto-trigger reviewer (if within round trip limit) if (task.round_trip_count >= PAIRED_MAX_ROUND_TRIPS) { logger.info(