fix(paired): stop silent halt on reviewer PROCEED + reviewer-unavailable
Two causes of the paired room "keeps stopping" symptom: - Reviewer approvals worded as "PROCEED" were parsed as 'continue' (a change request), causing an owner TASK_DONE <-> reviewer PROCEED ping-pong until the deadlock cap. parseReviewerVerdict() now treats a leading PROCEED as approval so the turn finalizes after one round. - When the Codex reviewer was unavailable, the owner's answer was held for review and the user saw nothing. Now the held owner answer is emitted with a "review skipped" notice on reviewer_codex_unavailable. Verified: tsc --noEmit clean; 27 related vitest tests pass. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -2,7 +2,9 @@ import { describe, expect, it } from 'vitest';
|
||||
|
||||
import {
|
||||
classifyArbiterVerdict,
|
||||
parseReviewerVerdict,
|
||||
parseVisibleVerdict,
|
||||
stripLeadingStatusLine,
|
||||
} from './paired-verdict.js';
|
||||
|
||||
describe('paired verdict parser', () => {
|
||||
@@ -80,6 +82,45 @@ describe('paired verdict parser', () => {
|
||||
).toBe('continue');
|
||||
});
|
||||
|
||||
it('treats a reviewer PROCEED line as an approval (DONE)', () => {
|
||||
expect(parseReviewerVerdict('PROCEED\n위치와 첨부는 맞습니다.')).toBe('done');
|
||||
expect(parseReviewerVerdict('**PROCEED**\n최종 답변 내용 맞습니다.')).toBe(
|
||||
'done',
|
||||
);
|
||||
expect(
|
||||
parseReviewerVerdict(
|
||||
['검토 결과를 정리합니다.', 'PROCEED', '확정합니다.'].join('\n'),
|
||||
),
|
||||
).toBe('done');
|
||||
});
|
||||
|
||||
it('does not upgrade non-approval reviewer verdicts', () => {
|
||||
// REVISE / RESET / plain prose remain a change request ('continue').
|
||||
expect(parseReviewerVerdict('REVISE\n이 부분을 고쳐주세요')).toBe('continue');
|
||||
expect(parseReviewerVerdict('RESET\n방향을 다시 잡읍시다')).toBe('continue');
|
||||
expect(parseReviewerVerdict('추가 수정이 필요합니다')).toBe('continue');
|
||||
// Documented reviewer tokens still parse to their own verdicts.
|
||||
expect(parseReviewerVerdict('TASK_DONE\n승인합니다')).toBe('task_done');
|
||||
expect(parseReviewerVerdict('DONE_WITH_CONCERNS\n우려가 있습니다')).toBe(
|
||||
'done_with_concerns',
|
||||
);
|
||||
// "PROCEED" mentioned in prose, not as the leading verdict, is ignored.
|
||||
expect(
|
||||
parseReviewerVerdict('리뷰어가 PROCEED 했는지 확인이 필요합니다'),
|
||||
).toBe('continue');
|
||||
});
|
||||
|
||||
it('strips a leading status line for direct delivery', () => {
|
||||
expect(stripLeadingStatusLine('TASK_DONE\n\n실제 답변입니다.')).toBe(
|
||||
'실제 답변입니다.',
|
||||
);
|
||||
expect(stripLeadingStatusLine('STEP_DONE\n다음 내용')).toBe('다음 내용');
|
||||
// No leading status token → text is returned untouched.
|
||||
expect(stripLeadingStatusLine('그냥 본문입니다.\n둘째 줄')).toBe(
|
||||
'그냥 본문입니다.\n둘째 줄',
|
||||
);
|
||||
});
|
||||
|
||||
it('classifies arbiter verdicts from leading visible lines', () => {
|
||||
expect(classifyArbiterVerdict('PROCEED\ncontinue')).toBe('proceed');
|
||||
expect(classifyArbiterVerdict('**VERDICT: REVISE**\nfix it')).toBe(
|
||||
|
||||
Reference in New Issue
Block a user