paired: preserve prior owner and reviewer finals across superseded tasks
This commit is contained in:
@@ -92,6 +92,7 @@ export {
|
|||||||
getLastBotFinalMessage,
|
getLastBotFinalMessage,
|
||||||
getLatestOpenPairedTaskForChat,
|
getLatestOpenPairedTaskForChat,
|
||||||
getLatestPairedTaskForChat,
|
getLatestPairedTaskForChat,
|
||||||
|
getLatestPreviousPairedTaskForChat,
|
||||||
getLatestTurnNumber,
|
getLatestTurnNumber,
|
||||||
getPairedProject,
|
getPairedProject,
|
||||||
getPairedTaskById,
|
getPairedTaskById,
|
||||||
|
|||||||
@@ -254,6 +254,26 @@ export function getLatestOpenPairedTaskForChatFromDatabase(
|
|||||||
return row ? hydratePairedTaskRow(database, row) : undefined;
|
return row ? hydratePairedTaskRow(database, row) : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getLatestPreviousPairedTaskForChatFromDatabase(
|
||||||
|
database: Database,
|
||||||
|
chatJid: string,
|
||||||
|
currentTaskId: string,
|
||||||
|
): PairedTask | undefined {
|
||||||
|
const row = database
|
||||||
|
.prepare(
|
||||||
|
`
|
||||||
|
SELECT *
|
||||||
|
FROM paired_tasks
|
||||||
|
WHERE chat_jid = ?
|
||||||
|
AND id != ?
|
||||||
|
ORDER BY updated_at DESC, created_at DESC
|
||||||
|
LIMIT 1
|
||||||
|
`,
|
||||||
|
)
|
||||||
|
.get(chatJid, currentTaskId) as StoredPairedTaskRow | undefined;
|
||||||
|
return row ? hydratePairedTaskRow(database, row) : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
export function updatePairedTaskInDatabase(
|
export function updatePairedTaskInDatabase(
|
||||||
database: Database,
|
database: Database,
|
||||||
id: string,
|
id: string,
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import {
|
|||||||
createPairedTaskInDatabase,
|
createPairedTaskInDatabase,
|
||||||
getLastBotFinalMessageFromDatabase,
|
getLastBotFinalMessageFromDatabase,
|
||||||
getLatestOpenPairedTaskForChatFromDatabase,
|
getLatestOpenPairedTaskForChatFromDatabase,
|
||||||
|
getLatestPreviousPairedTaskForChatFromDatabase,
|
||||||
getLatestPairedTaskForChatFromDatabase,
|
getLatestPairedTaskForChatFromDatabase,
|
||||||
getPairedProjectFromDatabase,
|
getPairedProjectFromDatabase,
|
||||||
getPairedTaskByIdFromDatabase,
|
getPairedTaskByIdFromDatabase,
|
||||||
@@ -101,6 +102,17 @@ export function getLatestOpenPairedTaskForChat(
|
|||||||
return getLatestOpenPairedTaskForChatFromDatabase(requireDatabase(), chatJid);
|
return getLatestOpenPairedTaskForChatFromDatabase(requireDatabase(), chatJid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getLatestPreviousPairedTaskForChat(
|
||||||
|
chatJid: string,
|
||||||
|
currentTaskId: string,
|
||||||
|
): PairedTask | undefined {
|
||||||
|
return getLatestPreviousPairedTaskForChatFromDatabase(
|
||||||
|
requireDatabase(),
|
||||||
|
chatJid,
|
||||||
|
currentTaskId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function updatePairedTask(id: string, updates: PairedTaskUpdates): void {
|
export function updatePairedTask(id: string, updates: PairedTaskUpdates): void {
|
||||||
updatePairedTaskInDatabase(requireDatabase(), id, updates);
|
updatePairedTaskInDatabase(requireDatabase(), id, updates);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
getLastHumanMessageContent,
|
getLastHumanMessageContent,
|
||||||
|
getLatestPreviousPairedTaskForChat,
|
||||||
getPairedTurnOutputs,
|
getPairedTurnOutputs,
|
||||||
getRecentChatMessages,
|
getRecentChatMessages,
|
||||||
} from './db.js';
|
} from './db.js';
|
||||||
@@ -10,6 +11,7 @@ import {
|
|||||||
buildOwnerPendingPrompt,
|
buildOwnerPendingPrompt,
|
||||||
buildPairedTurnPrompt,
|
buildPairedTurnPrompt,
|
||||||
buildReviewerPendingPrompt,
|
buildReviewerPendingPrompt,
|
||||||
|
type PriorTaskPromptContext,
|
||||||
} from './message-runtime-prompts.js';
|
} from './message-runtime-prompts.js';
|
||||||
import {
|
import {
|
||||||
requeuePendingPairedTurn,
|
requeuePendingPairedTurn,
|
||||||
@@ -32,6 +34,7 @@ import type {
|
|||||||
NewMessage,
|
NewMessage,
|
||||||
PairedTask,
|
PairedTask,
|
||||||
PairedRoomRole,
|
PairedRoomRole,
|
||||||
|
PairedTurnOutput,
|
||||||
PairedTurnReservationIntentKind,
|
PairedTurnReservationIntentKind,
|
||||||
RegisteredGroup,
|
RegisteredGroup,
|
||||||
} from './types.js';
|
} from './types.js';
|
||||||
@@ -115,6 +118,7 @@ export function buildPendingPairedTurn(args: {
|
|||||||
const cursor = lastRaw?.seq ?? lastRaw?.timestamp ?? null;
|
const cursor = lastRaw?.seq ?? lastRaw?.timestamp ?? null;
|
||||||
const taskStatus = task.status;
|
const taskStatus = task.status;
|
||||||
const turnOutputs = getPairedTurnOutputs(task.id);
|
const turnOutputs = getPairedTurnOutputs(task.id);
|
||||||
|
const priorTaskContext = resolvePriorTaskPromptContext(chatJid, task, turnOutputs);
|
||||||
const lastTurnOutput = turnOutputs[turnOutputs.length - 1];
|
const lastTurnOutput = turnOutputs[turnOutputs.length - 1];
|
||||||
const nextTurnAction = resolveNextTurnAction({
|
const nextTurnAction = resolveNextTurnAction({
|
||||||
taskStatus,
|
taskStatus,
|
||||||
@@ -131,6 +135,7 @@ export function buildPendingPairedTurn(args: {
|
|||||||
turnOutputs,
|
turnOutputs,
|
||||||
recentHumanMessages,
|
recentHumanMessages,
|
||||||
lastHumanMessage,
|
lastHumanMessage,
|
||||||
|
priorTaskContext,
|
||||||
}),
|
}),
|
||||||
channel: resolveChannel(taskStatus),
|
channel: resolveChannel(taskStatus),
|
||||||
cursor,
|
cursor,
|
||||||
@@ -182,6 +187,7 @@ export function buildPendingPairedTurn(args: {
|
|||||||
turnOutputs,
|
turnOutputs,
|
||||||
recentHumanMessages,
|
recentHumanMessages,
|
||||||
lastHumanMessage,
|
lastHumanMessage,
|
||||||
|
priorTaskContext,
|
||||||
}),
|
}),
|
||||||
channel: resolveChannel(taskStatus),
|
channel: resolveChannel(taskStatus),
|
||||||
cursor,
|
cursor,
|
||||||
@@ -195,6 +201,38 @@ export function buildPendingPairedTurn(args: {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function resolvePriorTaskPromptContext(
|
||||||
|
chatJid: string,
|
||||||
|
task: PairedTask,
|
||||||
|
turnOutputs: PairedTurnOutput[],
|
||||||
|
): PriorTaskPromptContext | null {
|
||||||
|
if (turnOutputs.length > 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const previousTask = getLatestPreviousPairedTaskForChat(chatJid, task.id);
|
||||||
|
if (!previousTask || previousTask.status !== 'completed') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const previousOutputs = getPairedTurnOutputs(previousTask.id);
|
||||||
|
const ownerFinal = [...previousOutputs]
|
||||||
|
.reverse()
|
||||||
|
.find((output) => output.role === 'owner')?.output_text;
|
||||||
|
const reviewerFinal = [...previousOutputs]
|
||||||
|
.reverse()
|
||||||
|
.find((output) => output.role === 'reviewer')?.output_text;
|
||||||
|
|
||||||
|
if (!ownerFinal && !reviewerFinal) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
ownerFinal: ownerFinal ?? null,
|
||||||
|
reviewerFinal: reviewerFinal ?? null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export async function executePendingPairedTurn(args: {
|
export async function executePendingPairedTurn(args: {
|
||||||
pendingTurn: Exclude<PendingPairedTurn, null>;
|
pendingTurn: Exclude<PendingPairedTurn, null>;
|
||||||
chatJid: string;
|
chatJid: string;
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ describe('message-runtime-prompts carry-forward guidance', () => {
|
|||||||
],
|
],
|
||||||
recentHumanMessages: [makeHumanMessage('이제 새 질문')],
|
recentHumanMessages: [makeHumanMessage('이제 새 질문')],
|
||||||
lastHumanMessage: '이제 새 질문',
|
lastHumanMessage: '이제 새 질문',
|
||||||
|
priorTaskContext: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
@@ -84,6 +85,7 @@ describe('message-runtime-prompts carry-forward guidance', () => {
|
|||||||
],
|
],
|
||||||
recentHumanMessages: [makeHumanMessage('새 owner 질문')],
|
recentHumanMessages: [makeHumanMessage('새 owner 질문')],
|
||||||
lastHumanMessage: '새 owner 질문',
|
lastHumanMessage: '새 owner 질문',
|
||||||
|
priorTaskContext: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
@@ -108,4 +110,43 @@ describe('message-runtime-prompts carry-forward guidance', () => {
|
|||||||
prompt.startsWith('System note:\nIf you see a message beginning with'),
|
prompt.startsWith('System note:\nIf you see a message beginning with'),
|
||||||
).toBe(false);
|
).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('includes previous owner and reviewer finals in reviewer pending prompts when the current task has no outputs', () => {
|
||||||
|
const prompt = buildReviewerPendingPrompt({
|
||||||
|
chatJid: 'group@test',
|
||||||
|
timezone: 'UTC',
|
||||||
|
turnOutputs: [],
|
||||||
|
recentHumanMessages: [makeHumanMessage('추가 질문')],
|
||||||
|
lastHumanMessage: '추가 질문',
|
||||||
|
priorTaskContext: {
|
||||||
|
ownerFinal: 'DONE\n이전 owner 결론',
|
||||||
|
reviewerFinal: 'DONE_WITH_CONCERNS\n이전 reviewer 결론',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(prompt).toContain('Background from the previous completed paired task:');
|
||||||
|
expect(prompt).toContain('Previous task owner final:');
|
||||||
|
expect(prompt).toContain('이전 owner 결론');
|
||||||
|
expect(prompt).toContain('Previous task reviewer final:');
|
||||||
|
expect(prompt).toContain('이전 reviewer 결론');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('includes previous owner and reviewer finals in owner pending prompts when the current task has no outputs', () => {
|
||||||
|
const prompt = buildOwnerPendingPrompt({
|
||||||
|
chatJid: 'group@test',
|
||||||
|
timezone: 'UTC',
|
||||||
|
turnOutputs: [],
|
||||||
|
recentHumanMessages: [makeHumanMessage('추가 owner 질문')],
|
||||||
|
lastHumanMessage: '추가 owner 질문',
|
||||||
|
priorTaskContext: {
|
||||||
|
ownerFinal: 'DONE\n이전 owner 결론',
|
||||||
|
reviewerFinal: 'DONE_WITH_CONCERNS\n이전 reviewer 결론',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(prompt).toContain('Background from the previous completed paired task:');
|
||||||
|
expect(prompt).toContain('Previous task owner final:');
|
||||||
|
expect(prompt).toContain('Previous task reviewer final:');
|
||||||
|
expect(prompt).toContain('추가 owner 질문');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,6 +8,11 @@ const CARRIED_FORWARD_OWNER_FINAL_MARKER =
|
|||||||
const CARRIED_FORWARD_OWNER_FINAL_GUIDANCE = `System note:
|
const CARRIED_FORWARD_OWNER_FINAL_GUIDANCE = `System note:
|
||||||
If you see a message beginning with "${CARRIED_FORWARD_OWNER_FINAL_MARKER}", treat it as background only. Do not repeat, continue, or answer that carried-forward final directly. Respond only to the latest human request and the current task.`;
|
If you see a message beginning with "${CARRIED_FORWARD_OWNER_FINAL_MARKER}", treat it as background only. Do not repeat, continue, or answer that carried-forward final directly. Respond only to the latest human request and the current task.`;
|
||||||
|
|
||||||
|
export interface PriorTaskPromptContext {
|
||||||
|
ownerFinal?: string | null;
|
||||||
|
reviewerFinal?: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
function turnOutputsToMessages(
|
function turnOutputsToMessages(
|
||||||
outputs: PairedTurnOutput[],
|
outputs: PairedTurnOutput[],
|
||||||
chatJid: string,
|
chatJid: string,
|
||||||
@@ -51,6 +56,40 @@ function prependCarriedForwardGuidance(
|
|||||||
return `${CARRIED_FORWARD_OWNER_FINAL_GUIDANCE}\n\n${prompt}`;
|
return `${CARRIED_FORWARD_OWNER_FINAL_GUIDANCE}\n\n${prompt}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function truncatePriorTaskFinal(text: string, maxChars = 1200): string {
|
||||||
|
const normalized = text.trim();
|
||||||
|
if (normalized.length <= maxChars) {
|
||||||
|
return normalized;
|
||||||
|
}
|
||||||
|
return `${normalized.slice(0, maxChars)}...`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatPriorTaskPromptContext(
|
||||||
|
priorTaskContext?: PriorTaskPromptContext | null,
|
||||||
|
): string {
|
||||||
|
if (!priorTaskContext) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
const sections: string[] = [];
|
||||||
|
if (priorTaskContext.ownerFinal?.trim()) {
|
||||||
|
sections.push(
|
||||||
|
`Previous task owner final:\n---\n${truncatePriorTaskFinal(priorTaskContext.ownerFinal)}\n---`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (priorTaskContext.reviewerFinal?.trim()) {
|
||||||
|
sections.push(
|
||||||
|
`Previous task reviewer final:\n---\n${truncatePriorTaskFinal(priorTaskContext.reviewerFinal)}\n---`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sections.length === 0) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return `Background from the previous completed paired task:\n${sections.join('\n\n')}\n\n`;
|
||||||
|
}
|
||||||
|
|
||||||
export function buildPairedTurnPrompt(args: {
|
export function buildPairedTurnPrompt(args: {
|
||||||
taskId: string;
|
taskId: string;
|
||||||
chatJid: string;
|
chatJid: string;
|
||||||
@@ -85,6 +124,7 @@ export function buildReviewerPendingPrompt(args: {
|
|||||||
turnOutputs: PairedTurnOutput[];
|
turnOutputs: PairedTurnOutput[];
|
||||||
recentHumanMessages: NewMessage[];
|
recentHumanMessages: NewMessage[];
|
||||||
lastHumanMessage: string | null | undefined;
|
lastHumanMessage: string | null | undefined;
|
||||||
|
priorTaskContext?: PriorTaskPromptContext | null;
|
||||||
}): string {
|
}): string {
|
||||||
if (args.turnOutputs.length > 0) {
|
if (args.turnOutputs.length > 0) {
|
||||||
return prependCarriedForwardGuidance(
|
return prependCarriedForwardGuidance(
|
||||||
@@ -101,10 +141,10 @@ export function buildReviewerPendingPrompt(args: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!args.lastHumanMessage) {
|
if (!args.lastHumanMessage) {
|
||||||
return 'Review the latest owner changes in the workspace.';
|
return `${formatPriorTaskPromptContext(args.priorTaskContext)}Review the latest owner changes in the workspace.`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return `User request:\n---\n${args.lastHumanMessage}\n---\n\nReview the latest owner changes in the workspace.`;
|
return `${formatPriorTaskPromptContext(args.priorTaskContext)}User request:\n---\n${args.lastHumanMessage}\n---\n\nReview the latest owner changes in the workspace.`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildOwnerPendingPrompt(args: {
|
export function buildOwnerPendingPrompt(args: {
|
||||||
@@ -113,6 +153,7 @@ export function buildOwnerPendingPrompt(args: {
|
|||||||
turnOutputs: PairedTurnOutput[];
|
turnOutputs: PairedTurnOutput[];
|
||||||
recentHumanMessages: NewMessage[];
|
recentHumanMessages: NewMessage[];
|
||||||
lastHumanMessage: string | null | undefined;
|
lastHumanMessage: string | null | undefined;
|
||||||
|
priorTaskContext?: PriorTaskPromptContext | null;
|
||||||
}): string {
|
}): string {
|
||||||
if (args.turnOutputs.length > 0) {
|
if (args.turnOutputs.length > 0) {
|
||||||
return prependCarriedForwardGuidance(
|
return prependCarriedForwardGuidance(
|
||||||
@@ -129,10 +170,10 @@ export function buildOwnerPendingPrompt(args: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!args.lastHumanMessage) {
|
if (!args.lastHumanMessage) {
|
||||||
return 'Continue the owner turn using the latest reviewer or arbiter feedback.';
|
return `${formatPriorTaskPromptContext(args.priorTaskContext)}Continue the owner turn using the latest reviewer or arbiter feedback.`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return `User request:\n---\n${args.lastHumanMessage}\n---\n\nContinue the owner turn using the latest reviewer or arbiter feedback.`;
|
return `${formatPriorTaskPromptContext(args.priorTaskContext)}User request:\n---\n${args.lastHumanMessage}\n---\n\nContinue the owner turn using the latest reviewer or arbiter feedback.`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildArbiterPromptForTask(args: {
|
export function buildArbiterPromptForTask(args: {
|
||||||
|
|||||||
@@ -161,6 +161,7 @@ vi.mock('./db.js', () => {
|
|||||||
),
|
),
|
||||||
hasActiveCiWatcherForChat: vi.fn(() => false),
|
hasActiveCiWatcherForChat: vi.fn(() => false),
|
||||||
getLatestOpenPairedTaskForChat: vi.fn(() => undefined),
|
getLatestOpenPairedTaskForChat: vi.fn(() => undefined),
|
||||||
|
getLatestPreviousPairedTaskForChat: vi.fn(() => undefined),
|
||||||
getPairedTaskById: vi.fn(() => undefined),
|
getPairedTaskById: vi.fn(() => undefined),
|
||||||
getPairedTurnOutputs: vi.fn(() => []),
|
getPairedTurnOutputs: vi.fn(() => []),
|
||||||
getRecentChatMessages: vi.fn(() => []),
|
getRecentChatMessages: vi.fn(() => []),
|
||||||
@@ -1921,6 +1922,83 @@ If your first line is DONE_WITH_CONCERNS, the system will reopen review instead
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('includes previous task owner and reviewer finals in a new reviewer prompt when the current task has no outputs', () => {
|
||||||
|
const chatJid = 'group@test';
|
||||||
|
const group = makeGroup('claude-code');
|
||||||
|
const currentTask = {
|
||||||
|
id: 'task-current-reviewer',
|
||||||
|
chat_jid: chatJid,
|
||||||
|
group_folder: group.folder,
|
||||||
|
owner_service_id: 'claude',
|
||||||
|
reviewer_service_id: 'codex-main',
|
||||||
|
title: null,
|
||||||
|
source_ref: 'HEAD',
|
||||||
|
plan_notes: null,
|
||||||
|
review_requested_at: '2026-03-30T00:00:10.000Z',
|
||||||
|
round_trip_count: 1,
|
||||||
|
status: 'review_ready',
|
||||||
|
arbiter_verdict: null,
|
||||||
|
arbiter_requested_at: null,
|
||||||
|
completion_reason: null,
|
||||||
|
created_at: '2026-03-30T00:00:10.000Z',
|
||||||
|
updated_at: '2026-03-30T00:00:10.000Z',
|
||||||
|
} as any;
|
||||||
|
const previousTask = {
|
||||||
|
...currentTask,
|
||||||
|
id: 'task-previous-reviewer',
|
||||||
|
status: 'completed',
|
||||||
|
completion_reason: 'superseded',
|
||||||
|
created_at: '2026-03-30T00:00:00.000Z',
|
||||||
|
updated_at: '2026-03-30T00:00:09.000Z',
|
||||||
|
} as any;
|
||||||
|
|
||||||
|
vi.mocked(db.getLatestPreviousPairedTaskForChat).mockReturnValue(previousTask);
|
||||||
|
vi.mocked(db.getPairedTurnOutputs).mockImplementation((taskId: string) => {
|
||||||
|
if (taskId === currentTask.id) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
if (taskId === previousTask.id) {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
task_id: previousTask.id,
|
||||||
|
turn_number: 1,
|
||||||
|
role: 'owner',
|
||||||
|
output_text: 'DONE\n이전 owner 답변',
|
||||||
|
created_at: '2026-03-30T00:00:05.000Z',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
task_id: previousTask.id,
|
||||||
|
turn_number: 2,
|
||||||
|
role: 'reviewer',
|
||||||
|
output_text: 'DONE_WITH_CONCERNS\n이전 reviewer 피드백',
|
||||||
|
created_at: '2026-03-30T00:00:06.000Z',
|
||||||
|
},
|
||||||
|
] as any;
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
});
|
||||||
|
vi.mocked(db.getLastHumanMessageContent).mockReturnValue('추가 질문');
|
||||||
|
|
||||||
|
const pending = buildPendingPairedTurn({
|
||||||
|
chatJid,
|
||||||
|
timezone: 'UTC',
|
||||||
|
task: currentTask,
|
||||||
|
rawMissedMessages: [{ seq: 42, timestamp: '2026-03-30T00:00:11.000Z' }],
|
||||||
|
recentHumanMessages: [],
|
||||||
|
labeledRecentMessages: [],
|
||||||
|
resolveChannel: () => makeChannel(chatJid, 'discord-review', false),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(pending).not.toBeNull();
|
||||||
|
expect(pending?.prompt).toContain('Background from the previous completed paired task:');
|
||||||
|
expect(pending?.prompt).toContain('Previous task owner final:');
|
||||||
|
expect(pending?.prompt).toContain('이전 owner 답변');
|
||||||
|
expect(pending?.prompt).toContain('Previous task reviewer final:');
|
||||||
|
expect(pending?.prompt).toContain('이전 reviewer 피드백');
|
||||||
|
});
|
||||||
|
|
||||||
it('re-enqueues reviewer after a successful owner delivery moves the task to review_ready', async () => {
|
it('re-enqueues reviewer after a successful owner delivery moves the task to review_ready', async () => {
|
||||||
const chatJid = 'group@test';
|
const chatJid = 'group@test';
|
||||||
const group = makeGroup('codex');
|
const group = makeGroup('codex');
|
||||||
|
|||||||
Reference in New Issue
Block a user