Add STEP_DONE guards, verdict storage, and stale delivery suppression

This commit is contained in:
ejclaw
2026-04-23 15:27:49 +09:00
parent afd9c7a538
commit 9ef79e3828
18 changed files with 767 additions and 64 deletions

View File

@@ -30,6 +30,7 @@ import {
hasActiveCiWatcherForChat,
insertPairedTurnOutput,
releasePairedTaskExecutionLease,
updatePairedTask,
upsertPairedProject,
} from './db.js';
import { logger } from './logger.js';
@@ -72,6 +73,8 @@ import type {
} from './types.js';
import { resolveRoleAgentPlan } from './role-agent-plan.js';
const TASK_DONE_REOPEN_WINDOW_MS = 10 * 60_000;
function ensurePairedProject(
group: RegisteredGroup,
chatJid: string,
@@ -144,6 +147,10 @@ function createActiveTaskForRoom(args: {
review_requested_at: null,
round_trip_count: 0,
owner_failure_count: 0,
owner_step_done_streak: 0,
finalize_step_done_count: 0,
task_done_then_user_reopen_count: 0,
empty_step_done_streak: 0,
status: 'active',
arbiter_verdict: null,
arbiter_requested_at: null,
@@ -164,6 +171,39 @@ function createActiveTaskForRoom(args: {
return task;
}
function maybeRecordTaskDoneReopen(previousTask: PairedTask | null): void {
if (
!previousTask ||
previousTask.status !== 'completed' ||
previousTask.completion_reason !== 'done'
) {
return;
}
const completedAt = Date.parse(previousTask.updated_at);
if (!Number.isFinite(completedAt)) {
return;
}
if (Date.now() - completedAt > TASK_DONE_REOPEN_WINDOW_MS) {
return;
}
updatePairedTask(previousTask.id, {
task_done_then_user_reopen_count:
(previousTask.task_done_then_user_reopen_count ?? 0) + 1,
updated_at: new Date().toISOString(),
});
logger.info(
{
taskId: previousTask.id,
chatJid: previousTask.chat_jid,
reopenCount: (previousTask.task_done_then_user_reopen_count ?? 0) + 1,
completionReason: previousTask.completion_reason,
},
'Recorded paired task reopen shortly after TASK_DONE completion',
);
}
function cancelOutstandingFinalizeOwnerTurn(task: PairedTask): void {
const turnIdentity = buildPairedTurnIdentity({
taskId: task.id,
@@ -253,6 +293,7 @@ export function resolveOwnerTaskForHumanMessage(args: {
args.existingTask ?? getLatestOpenPairedTaskForChat(args.chatJid) ?? null;
if (!existing) {
maybeRecordTaskDoneReopen(getLatestPairedTaskForChat(args.chatJid) ?? null);
return {
task: canonicalWorkDir
? createActiveTaskForRoom({
@@ -432,7 +473,12 @@ export function preparePairedExecutionContext(args: {
updatedAt: now,
patch: {
...(hasHuman
? { round_trip_count: 0, owner_failure_count: 0 }
? {
round_trip_count: 0,
owner_failure_count: 0,
owner_step_done_streak: 0,
empty_step_done_streak: 0,
}
: {}),
},
});
@@ -443,7 +489,12 @@ export function preparePairedExecutionContext(args: {
updatedAt: now,
patch: {
...(hasHuman
? { round_trip_count: 0, owner_failure_count: 0 }
? {
round_trip_count: 0,
owner_failure_count: 0,
owner_step_done_streak: 0,
empty_step_done_streak: 0,
}
: {}),
},
});