fix: preserve reviewer delivery follow-ups
This commit is contained in:
@@ -21,11 +21,50 @@ export type PairedFollowUpSource = Parameters<
|
||||
typeof resolveFollowUpDispatch
|
||||
>[0]['source'];
|
||||
|
||||
type PairedTurnOutputContext = ReturnType<typeof getPairedTurnOutputs>[number];
|
||||
|
||||
type PairedFollowUpTaskContext = Pick<
|
||||
PairedTask,
|
||||
'id' | 'status' | 'round_trip_count' | 'updated_at' | 'owner_failure_count'
|
||||
>;
|
||||
|
||||
function timestampMs(value: string | null | undefined): number | null {
|
||||
if (!value) {
|
||||
return null;
|
||||
}
|
||||
const parsed = Date.parse(value);
|
||||
return Number.isFinite(parsed) ? parsed : null;
|
||||
}
|
||||
|
||||
function isPersistedOutputOlderThanTaskRevision(args: {
|
||||
output: PairedTurnOutputContext;
|
||||
task: Partial<Pick<PairedTask, 'updated_at'>> | null | undefined;
|
||||
}): boolean {
|
||||
const outputCreatedAt = timestampMs(args.output.created_at);
|
||||
const taskUpdatedAt = timestampMs(args.task?.updated_at);
|
||||
if (outputCreatedAt == null || taskUpdatedAt == null) {
|
||||
return false;
|
||||
}
|
||||
return outputCreatedAt < taskUpdatedAt;
|
||||
}
|
||||
|
||||
function shouldPreferFallbackTurnOutputContext(args: {
|
||||
output: PairedTurnOutputContext | null | undefined;
|
||||
task: Partial<Pick<PairedTask, 'updated_at'>> | null | undefined;
|
||||
fallbackRole: PairedRoomRole | null | undefined;
|
||||
}): boolean {
|
||||
if (!args.output || !args.fallbackRole) {
|
||||
return false;
|
||||
}
|
||||
if (args.output.role === args.fallbackRole) {
|
||||
return false;
|
||||
}
|
||||
return isPersistedOutputOlderThanTaskRevision({
|
||||
output: args.output,
|
||||
task: args.task,
|
||||
});
|
||||
}
|
||||
|
||||
export interface PairedFollowUpDecision {
|
||||
taskId: string | null;
|
||||
taskStatus: PairedTaskStatus | null;
|
||||
@@ -49,7 +88,10 @@ export type PairedFollowUpDispatchResult =
|
||||
});
|
||||
|
||||
export function resolveLatestPairedTurnOutputContext(args: {
|
||||
task: Pick<PairedTask, 'id'> | null | undefined;
|
||||
task:
|
||||
| (Pick<PairedTask, 'id'> & Partial<Pick<PairedTask, 'updated_at'>>)
|
||||
| null
|
||||
| undefined;
|
||||
fallbackLastTurnOutputRole?: PairedRoomRole | null;
|
||||
fallbackLastTurnOutputVerdict?: VisibleVerdict | null;
|
||||
}): {
|
||||
@@ -59,12 +101,19 @@ export function resolveLatestPairedTurnOutputContext(args: {
|
||||
const latestOutput = args.task
|
||||
? getPairedTurnOutputs(args.task.id).at(-1)
|
||||
: null;
|
||||
const output = shouldPreferFallbackTurnOutputContext({
|
||||
output: latestOutput,
|
||||
task: args.task,
|
||||
fallbackRole: args.fallbackLastTurnOutputRole,
|
||||
})
|
||||
? null
|
||||
: latestOutput;
|
||||
return {
|
||||
role: latestOutput?.role ?? args.fallbackLastTurnOutputRole ?? null,
|
||||
role: output?.role ?? args.fallbackLastTurnOutputRole ?? null,
|
||||
verdict:
|
||||
resolveStoredVisibleVerdict({
|
||||
verdict: latestOutput?.verdict ?? null,
|
||||
outputText: latestOutput?.output_text ?? null,
|
||||
verdict: output?.verdict ?? null,
|
||||
outputText: output?.output_text ?? null,
|
||||
}) ??
|
||||
args.fallbackLastTurnOutputVerdict ??
|
||||
null,
|
||||
@@ -73,7 +122,10 @@ export function resolveLatestPairedTurnOutputContext(args: {
|
||||
|
||||
export function resolvePairedFollowUpDecision(args: {
|
||||
task:
|
||||
| Pick<PairedFollowUpTaskContext, 'id' | 'status' | 'owner_failure_count'>
|
||||
| Pick<
|
||||
PairedFollowUpTaskContext,
|
||||
'id' | 'status' | 'updated_at' | 'owner_failure_count'
|
||||
>
|
||||
| null
|
||||
| undefined;
|
||||
source: PairedFollowUpSource;
|
||||
|
||||
@@ -2727,9 +2727,20 @@ describe('createMessageRuntime', () => {
|
||||
vi.mocked(db.getLatestOpenPairedTaskForChat).mockImplementation(
|
||||
() => pairedTask,
|
||||
);
|
||||
vi.mocked(db.getPairedTurnOutputs).mockReturnValue([
|
||||
{
|
||||
id: 1,
|
||||
task_id: pairedTask.id,
|
||||
turn_number: 1,
|
||||
role: 'owner',
|
||||
output_text: 'STEP_DONE\nowner work ready for review',
|
||||
created_at: '2026-03-30T00:00:00.500Z',
|
||||
},
|
||||
] as any);
|
||||
vi.mocked(agentRunner.runAgentProcess).mockImplementation(
|
||||
async (_group, _input, _onProcess, onOutput) => {
|
||||
pairedTask.status = 'active';
|
||||
pairedTask.updated_at = '2026-03-30T00:00:01.000Z';
|
||||
await onOutput?.({
|
||||
status: 'success',
|
||||
phase: 'final',
|
||||
|
||||
Reference in New Issue
Block a user