From 7ed0af98a2cde2d92938a191e843c804199712bc Mon Sep 17 00:00:00 2001 From: Codex Date: Wed, 27 May 2026 11:20:30 +0900 Subject: [PATCH] fix one-shot review for rooms without workDir --- src/message-runtime.ts | 13 +++++++------ src/one-shot-review.test.ts | 31 ++++++++++++++++++++++++++++--- src/one-shot-review.ts | 26 ++++++++++++++++++-------- 3 files changed, 53 insertions(+), 17 deletions(-) diff --git a/src/message-runtime.ts b/src/message-runtime.ts index 6879936..b7c1cd8 100644 --- a/src/message-runtime.ts +++ b/src/message-runtime.ts @@ -655,15 +655,16 @@ export function createMessageRuntime(deps: MessageRuntimeDeps): { return gateResult.success; } - maybeActivateOneShotReviewLease({ - chatJid, - group, - messages: missedMessages, - }); + const oneShotReviewGroup = + maybeActivateOneShotReviewLease({ + chatJid, + group, + messages: missedMessages, + }) ?? group; return runQueuedGroupTurn({ chatJid, - group, + group: oneShotReviewGroup, runId, log, timezone: deps.timezone, diff --git a/src/one-shot-review.test.ts b/src/one-shot-review.test.ts index 53990c3..a4f025d 100644 --- a/src/one-shot-review.test.ts +++ b/src/one-shot-review.test.ts @@ -58,13 +58,16 @@ describe('one-shot review', () => { expect(getEffectiveRuntimeRoomMode('dc:single-room')).toBe('single'); expect(hasReviewerLease('dc:single-room')).toBe(false); - const activated = maybeActivateOneShotReviewLease({ + const reviewGroup = maybeActivateOneShotReviewLease({ chatJid: 'dc:single-room', group: group!, messages: [humanMessage('이번 요청만 리뷰어 사용해서 검증해줘')], }); - expect(activated).toBe(true); + expect(reviewGroup).toMatchObject({ + folder: 'single_room', + workDir: '/tmp/ejclaw-single-room', + }); expect(getEffectiveRuntimeRoomMode('dc:single-room')).toBe('single'); expect(getChannelOwnerLease('dc:single-room')).toMatchObject({ reason: ONE_SHOT_REVIEW_LEASE_REASON, @@ -88,10 +91,32 @@ describe('one-shot review', () => { group: group!, messages: [humanMessage('그냥 구현해줘')], }), - ).toBe(false); + ).toBeNull(); expect(getChannelOwnerLease('dc:single-room')).toBeUndefined(); }); + it('uses the group folder as the workDir for one-shot review in rooms without workDir', () => { + const group = assignRoom('dc:single-room', { + name: 'single room', + folder: 'single_room', + roomMode: 'single', + ownerAgentType: 'claude-code', + }); + + const reviewGroup = maybeActivateOneShotReviewLease({ + chatJid: 'dc:single-room', + group: group!, + messages: [humanMessage('리뷰어로 검증해줘')], + }); + + expect(reviewGroup?.folder).toBe('single_room'); + expect(reviewGroup?.workDir).toMatch(/\/groups\/single_room$/); + expect(getChannelOwnerLease('dc:single-room')).toMatchObject({ + reason: ONE_SHOT_REVIEW_LEASE_REASON, + }); + expect(hasReviewerLease('dc:single-room')).toBe(true); + }); + it('clears an idle one-shot reviewer lease', () => { const group = assignRoom('dc:single-room', { name: 'single room', diff --git a/src/one-shot-review.ts b/src/one-shot-review.ts index f01e3b7..4112140 100644 --- a/src/one-shot-review.ts +++ b/src/one-shot-review.ts @@ -12,6 +12,7 @@ import { setChannelOwnerLease, } from './db.js'; import { logger } from './logger.js'; +import { resolveGroupFolderPath } from './group-folder.js'; import { resolveRoleAgentPlan } from './role-agent-plan.js'; import { resolveRoleServiceShadow } from './role-service-shadow.js'; import { @@ -50,11 +51,18 @@ export function maybeActivateOneShotReviewLease(args: { chatJid: string; group: RegisteredGroup; messages: NewMessage[]; -}): boolean { - if (!args.group.workDir) return false; - if (getEffectiveRuntimeRoomMode(args.chatJid) !== 'single') return false; - if (hasReviewerLease(args.chatJid)) return false; - if (!hasHumanOneShotReviewRequest(args.messages)) return false; +}): RegisteredGroup | null { + if (getEffectiveRuntimeRoomMode(args.chatJid) !== 'single') return null; + if (hasReviewerLease(args.chatJid)) return null; + if (!hasHumanOneShotReviewRequest(args.messages)) return null; + + let effectiveGroup = args.group; + if (!effectiveGroup.workDir) { + effectiveGroup = { + ...effectiveGroup, + workDir: resolveGroupFolderPath(effectiveGroup.folder), + }; + } const lease = getEffectiveChannelLease(args.chatJid); const storedPlan = getStoredRoomRoleAgentPlan(args.chatJid); @@ -76,7 +84,7 @@ export function maybeActivateOneShotReviewLease(args: { rolePlan.reviewerAgentType, ); if (!reviewerServiceId || !rolePlan.reviewerAgentType) { - return false; + return null; } setChannelOwnerLease({ @@ -96,13 +104,15 @@ export function maybeActivateOneShotReviewLease(args: { logger.info( { chatJid: args.chatJid, - groupFolder: args.group.folder, + groupFolder: effectiveGroup.folder, + workDir: effectiveGroup.workDir, + usedDefaultWorkDir: !args.group.workDir, ownerAgentType: rolePlan.ownerAgentType, reviewerAgentType: rolePlan.reviewerAgentType, }, 'Activated one-shot reviewer lease for a single-mode room', ); - return true; + return effectiveGroup; } export function clearOneShotReviewLeaseIfIdle(chatJid: string): boolean {