Self-heal tribunal rooms with no work_dir so the owner never goes silent

A tribunal room whose work_dir was never provisioned (null) made
resolveOwnerTaskForHumanMessage return a null task, so the owner never ran and
the room went completely silent — the user's messages got no reply at all (seen
in the tts_site room, where days of requests were dropped). ensurePairedProject
now provisions the canonical workspace on demand when work_dir is missing,
guarded to tribunal rooms so a single-mode room never gets a spurious paired
workspace. Idempotent via ensurePairedWorkspaceProvisioned. Adds tests for both
the tribunal self-heal and the single-mode no-op.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Codex
2026-08-25 20:47:00 +09:00
parent 6fad6dcfab
commit 03e6f8bcc6
2 changed files with 69 additions and 1 deletions

View File

@@ -19,6 +19,7 @@ import {
import {
cancelPairedTurn,
createPairedTask,
getEffectiveRuntimeRoomMode,
getLatestPairedTaskForChat,
getLatestOpenPairedTaskForChat,
getPairedTaskById,
@@ -48,6 +49,7 @@ import {
} from './paired-task-status.js';
import { resolveCanonicalSourceRef } from './paired-source-ref.js';
import {
ensurePairedWorkspaceProvisioned,
isOwnerWorkspaceRepairNeededError,
prepareReviewerWorkspaceForExecution,
provisionOwnerWorkspaceForPairedTask,
@@ -74,7 +76,21 @@ function ensurePairedProject(
chatJid: string,
): string | null {
if (!group.workDir) {
return null;
// Self-heal: a tribunal room whose work_dir was never provisioned (null)
// makes resolveOwnerTaskForHumanMessage return a null task, so the owner
// never runs and the room goes completely silent — the user's messages get
// no reply at all. Provision the canonical workspace on demand so the owner
// can always start. Guarded to tribunal rooms so a single-mode room never
// gets a spurious paired workspace. Idempotent: ensurePairedWorkspace-
// Provisioned skips git init/commit when they already exist and upserts the
// paired project itself.
if (getEffectiveRuntimeRoomMode(chatJid) !== 'tribunal') {
return null;
}
return ensurePairedWorkspaceProvisioned({
chatJid,
groupFolder: group.folder,
});
}
const now = new Date().toISOString();