fix one-shot review for rooms without workDir

This commit is contained in:
Codex
2026-05-27 11:20:30 +09:00
parent 51512c9aeb
commit 7ed0af98a2
3 changed files with 53 additions and 17 deletions

View File

@@ -655,15 +655,16 @@ export function createMessageRuntime(deps: MessageRuntimeDeps): {
return gateResult.success; return gateResult.success;
} }
maybeActivateOneShotReviewLease({ const oneShotReviewGroup =
chatJid, maybeActivateOneShotReviewLease({
group, chatJid,
messages: missedMessages, group,
}); messages: missedMessages,
}) ?? group;
return runQueuedGroupTurn({ return runQueuedGroupTurn({
chatJid, chatJid,
group, group: oneShotReviewGroup,
runId, runId,
log, log,
timezone: deps.timezone, timezone: deps.timezone,

View File

@@ -58,13 +58,16 @@ describe('one-shot review', () => {
expect(getEffectiveRuntimeRoomMode('dc:single-room')).toBe('single'); expect(getEffectiveRuntimeRoomMode('dc:single-room')).toBe('single');
expect(hasReviewerLease('dc:single-room')).toBe(false); expect(hasReviewerLease('dc:single-room')).toBe(false);
const activated = maybeActivateOneShotReviewLease({ const reviewGroup = maybeActivateOneShotReviewLease({
chatJid: 'dc:single-room', chatJid: 'dc:single-room',
group: group!, group: group!,
messages: [humanMessage('이번 요청만 리뷰어 사용해서 검증해줘')], 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(getEffectiveRuntimeRoomMode('dc:single-room')).toBe('single');
expect(getChannelOwnerLease('dc:single-room')).toMatchObject({ expect(getChannelOwnerLease('dc:single-room')).toMatchObject({
reason: ONE_SHOT_REVIEW_LEASE_REASON, reason: ONE_SHOT_REVIEW_LEASE_REASON,
@@ -88,10 +91,32 @@ describe('one-shot review', () => {
group: group!, group: group!,
messages: [humanMessage('그냥 구현해줘')], messages: [humanMessage('그냥 구현해줘')],
}), }),
).toBe(false); ).toBeNull();
expect(getChannelOwnerLease('dc:single-room')).toBeUndefined(); 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', () => { it('clears an idle one-shot reviewer lease', () => {
const group = assignRoom('dc:single-room', { const group = assignRoom('dc:single-room', {
name: 'single room', name: 'single room',

View File

@@ -12,6 +12,7 @@ import {
setChannelOwnerLease, setChannelOwnerLease,
} from './db.js'; } from './db.js';
import { logger } from './logger.js'; import { logger } from './logger.js';
import { resolveGroupFolderPath } from './group-folder.js';
import { resolveRoleAgentPlan } from './role-agent-plan.js'; import { resolveRoleAgentPlan } from './role-agent-plan.js';
import { resolveRoleServiceShadow } from './role-service-shadow.js'; import { resolveRoleServiceShadow } from './role-service-shadow.js';
import { import {
@@ -50,11 +51,18 @@ export function maybeActivateOneShotReviewLease(args: {
chatJid: string; chatJid: string;
group: RegisteredGroup; group: RegisteredGroup;
messages: NewMessage[]; messages: NewMessage[];
}): boolean { }): RegisteredGroup | null {
if (!args.group.workDir) return false; if (getEffectiveRuntimeRoomMode(args.chatJid) !== 'single') return null;
if (getEffectiveRuntimeRoomMode(args.chatJid) !== 'single') return false; if (hasReviewerLease(args.chatJid)) return null;
if (hasReviewerLease(args.chatJid)) return false; if (!hasHumanOneShotReviewRequest(args.messages)) return null;
if (!hasHumanOneShotReviewRequest(args.messages)) return false;
let effectiveGroup = args.group;
if (!effectiveGroup.workDir) {
effectiveGroup = {
...effectiveGroup,
workDir: resolveGroupFolderPath(effectiveGroup.folder),
};
}
const lease = getEffectiveChannelLease(args.chatJid); const lease = getEffectiveChannelLease(args.chatJid);
const storedPlan = getStoredRoomRoleAgentPlan(args.chatJid); const storedPlan = getStoredRoomRoleAgentPlan(args.chatJid);
@@ -76,7 +84,7 @@ export function maybeActivateOneShotReviewLease(args: {
rolePlan.reviewerAgentType, rolePlan.reviewerAgentType,
); );
if (!reviewerServiceId || !rolePlan.reviewerAgentType) { if (!reviewerServiceId || !rolePlan.reviewerAgentType) {
return false; return null;
} }
setChannelOwnerLease({ setChannelOwnerLease({
@@ -96,13 +104,15 @@ export function maybeActivateOneShotReviewLease(args: {
logger.info( logger.info(
{ {
chatJid: args.chatJid, chatJid: args.chatJid,
groupFolder: args.group.folder, groupFolder: effectiveGroup.folder,
workDir: effectiveGroup.workDir,
usedDefaultWorkDir: !args.group.workDir,
ownerAgentType: rolePlan.ownerAgentType, ownerAgentType: rolePlan.ownerAgentType,
reviewerAgentType: rolePlan.reviewerAgentType, reviewerAgentType: rolePlan.reviewerAgentType,
}, },
'Activated one-shot reviewer lease for a single-mode room', 'Activated one-shot reviewer lease for a single-mode room',
); );
return true; return effectiveGroup;
} }
export function clearOneShotReviewLeaseIfIdle(chatJid: string): boolean { export function clearOneShotReviewLeaseIfIdle(chatJid: string): boolean {