Ctrl K
ComunidadGuías…

src/runner/runnable.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import { readFile } from 'node:fs/promises';
import { join } from 'node:path';
import { hasHumanReplies } from './human-feedback.ts';
import { readState, statePath } from './state.ts';
import { patchpawPaths } from '../config/paths.ts';
import { readUnfinishedConflictApproval } from './conflict-approval.ts';
import { readUnfinishedApprovalPlanClaim } from './approval-plans.ts';

async function json(path: string) {
  try { return JSON.parse(await readFile(path, 'utf8')); }
  catch (error) { if ((error as NodeJS.ErrnoException).code === 'ENOENT') return null; throw error; }
}

// One wake predicate for PR-local mechanical work. Pure outbound retries are
// handled by the outbox scheduler, while this predicate lets legacy review,
// notice and close recovery run without requiring another human comment.
export async function hasRunnableWork(root: string, repo: string, number: number) {
  const path = statePath(patchpawPaths(root).state, repo, number);
  if (await hasHumanReplies(path)) return true;
  const state = await readState(path);
  if (!state) return false;
  // An approval claim/repair is durable work even after the source comment has been
  // marked handled. This lets a webhook redelivery or a later human entry wake the
  // mechanical recovery path without consuming the approval a second time.
  if (await readUnfinishedConflictApproval(path)) return true;
  if (await readUnfinishedApprovalPlanClaim(path)) return true;
  if (state.completion_notice_status === 'pending' || state.pending_close_refusal) return true;
  const journal = await json(`${path}.close.json`);
  if (journal?.status === 'closing') return true;
  const runDir = join(patchpawPaths(root).runs, state.run_id);
  const review = await json(join(runDir, 'review.json'));
  const publication = await json(join(runDir, 'review-publication.json'));
  if (review && !publication && ['review_ready', 'review_publishing'].includes(state.phase)) return true;
  const notice = await json(join(runDir, 'run-notice.json'));
  const notification = await json(join(runDir, 'notification.json'));
  return !!notice && notification?.status !== 'published';
}