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

@@ -119,6 +119,10 @@ export function applyBaseSchema(database: Database): void {
review_requested_at TEXT,
round_trip_count INTEGER NOT NULL DEFAULT 0,
owner_failure_count INTEGER NOT NULL DEFAULT 0,
owner_step_done_streak INTEGER NOT NULL DEFAULT 0,
finalize_step_done_count INTEGER NOT NULL DEFAULT 0,
task_done_then_user_reopen_count INTEGER NOT NULL DEFAULT 0,
empty_step_done_streak INTEGER NOT NULL DEFAULT 0,
status TEXT NOT NULL DEFAULT 'active',
arbiter_verdict TEXT,
arbiter_requested_at TEXT,
@@ -154,6 +158,7 @@ export function applyBaseSchema(database: Database): void {
turn_number INTEGER NOT NULL,
role TEXT NOT NULL,
output_text TEXT NOT NULL,
verdict TEXT,
created_at TEXT NOT NULL,
UNIQUE(task_id, turn_number, role)
);

View File

@@ -36,6 +36,7 @@ function getExpectedSchemaMigrations(): Array<{
{ version: 9, name: 'paired_workspace_project_schema_cleanup' },
{ version: 10, name: 'paired_turn_provenance_upgrade' },
{ version: 11, name: 'owner_failure_count' },
{ version: 12, name: 'paired_verdict_and_step_telemetry' },
];
}

View File

@@ -0,0 +1,75 @@
import type { Database } from 'bun:sqlite';
import { tableHasColumn } from './helpers.js';
import type { SchemaMigrationDefinition } from './types.js';
export const PAIRED_VERDICT_AND_STEP_TELEMETRY_MIGRATION: SchemaMigrationDefinition =
{
version: 12,
name: 'paired_verdict_and_step_telemetry',
apply(database: Database) {
if (!tableHasColumn(database, 'paired_turn_outputs', 'verdict')) {
database.exec(`
ALTER TABLE paired_turn_outputs
ADD COLUMN verdict TEXT
`);
}
if (!tableHasColumn(database, 'paired_tasks', 'owner_step_done_streak')) {
database.exec(`
ALTER TABLE paired_tasks
ADD COLUMN owner_step_done_streak INTEGER NOT NULL DEFAULT 0
`);
}
if (
!tableHasColumn(database, 'paired_tasks', 'finalize_step_done_count')
) {
database.exec(`
ALTER TABLE paired_tasks
ADD COLUMN finalize_step_done_count INTEGER NOT NULL DEFAULT 0
`);
}
if (
!tableHasColumn(
database,
'paired_tasks',
'task_done_then_user_reopen_count',
)
) {
database.exec(`
ALTER TABLE paired_tasks
ADD COLUMN task_done_then_user_reopen_count INTEGER NOT NULL DEFAULT 0
`);
}
if (!tableHasColumn(database, 'paired_tasks', 'empty_step_done_streak')) {
database.exec(`
ALTER TABLE paired_tasks
ADD COLUMN empty_step_done_streak INTEGER NOT NULL DEFAULT 0
`);
}
database.exec(`
UPDATE paired_tasks
SET owner_step_done_streak = 0
WHERE owner_step_done_streak IS NULL
`);
database.exec(`
UPDATE paired_tasks
SET finalize_step_done_count = 0
WHERE finalize_step_done_count IS NULL
`);
database.exec(`
UPDATE paired_tasks
SET task_done_then_user_reopen_count = 0
WHERE task_done_then_user_reopen_count IS NULL
`);
database.exec(`
UPDATE paired_tasks
SET empty_step_done_streak = 0
WHERE empty_step_done_streak IS NULL
`);
},
};

View File

@@ -11,6 +11,7 @@ import { PAIRED_TASK_SCHEMA_CLEANUP_MIGRATION } from './008_paired-task-schema-c
import { PAIRED_WORKSPACE_PROJECT_SCHEMA_CLEANUP_MIGRATION } from './009_paired-workspace-project-schema-cleanup.js';
import { PAIRED_TURN_PROVENANCE_UPGRADE_MIGRATION } from './010_paired-turn-provenance-upgrade.js';
import { OWNER_FAILURE_COUNT_MIGRATION } from './011_owner-failure-count.js';
import { PAIRED_VERDICT_AND_STEP_TELEMETRY_MIGRATION } from './012_paired-verdict-and-step-telemetry.js';
import type {
SchemaMigrationArgs,
SchemaMigrationDefinition,
@@ -30,6 +31,7 @@ const ORDERED_SCHEMA_MIGRATIONS: readonly SchemaMigrationDefinition[] = [
PAIRED_WORKSPACE_PROJECT_SCHEMA_CLEANUP_MIGRATION,
PAIRED_TURN_PROVENANCE_UPGRADE_MIGRATION,
OWNER_FAILURE_COUNT_MIGRATION,
PAIRED_VERDICT_AND_STEP_TELEMETRY_MIGRATION,
];
function ensureSchemaMigrationsTable(database: Database): void {

View File

@@ -51,6 +51,10 @@ export type PairedTaskUpdates = Partial<
| 'review_requested_at'
| 'round_trip_count'
| 'owner_failure_count'
| 'owner_step_done_streak'
| 'finalize_step_done_count'
| 'task_done_then_user_reopen_count'
| 'empty_step_done_streak'
| 'status'
| 'arbiter_verdict'
| 'arbiter_requested_at'
@@ -173,6 +177,10 @@ export function createPairedTaskInDatabase(
review_requested_at,
round_trip_count,
owner_failure_count,
owner_step_done_streak,
finalize_step_done_count,
task_done_then_user_reopen_count,
empty_step_done_streak,
status,
arbiter_verdict,
arbiter_requested_at,
@@ -180,7 +188,7 @@ export function createPairedTaskInDatabase(
created_at,
updated_at
)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`,
)
.run(
@@ -198,6 +206,10 @@ export function createPairedTaskInDatabase(
task.review_requested_at,
task.round_trip_count,
task.owner_failure_count ?? 0,
task.owner_step_done_streak ?? 0,
task.finalize_step_done_count ?? 0,
task.task_done_then_user_reopen_count ?? 0,
task.empty_step_done_streak ?? 0,
task.status,
task.arbiter_verdict,
task.arbiter_requested_at,
@@ -306,6 +318,22 @@ export function updatePairedTaskInDatabase(
fields.push('owner_failure_count = ?');
values.push(updates.owner_failure_count);
}
if (updates.owner_step_done_streak !== undefined) {
fields.push('owner_step_done_streak = ?');
values.push(updates.owner_step_done_streak);
}
if (updates.finalize_step_done_count !== undefined) {
fields.push('finalize_step_done_count = ?');
values.push(updates.finalize_step_done_count);
}
if (updates.task_done_then_user_reopen_count !== undefined) {
fields.push('task_done_then_user_reopen_count = ?');
values.push(updates.task_done_then_user_reopen_count);
}
if (updates.empty_step_done_streak !== undefined) {
fields.push('empty_step_done_streak = ?');
values.push(updates.empty_step_done_streak);
}
if (updates.status !== undefined) {
fields.push('status = ?');
values.push(updates.status);
@@ -369,6 +397,22 @@ export function updatePairedTaskIfUnchangedInDatabase(
fields.push('owner_failure_count = ?');
values.push(updates.owner_failure_count);
}
if (updates.owner_step_done_streak !== undefined) {
fields.push('owner_step_done_streak = ?');
values.push(updates.owner_step_done_streak);
}
if (updates.finalize_step_done_count !== undefined) {
fields.push('finalize_step_done_count = ?');
values.push(updates.finalize_step_done_count);
}
if (updates.task_done_then_user_reopen_count !== undefined) {
fields.push('task_done_then_user_reopen_count = ?');
values.push(updates.task_done_then_user_reopen_count);
}
if (updates.empty_step_done_streak !== undefined) {
fields.push('empty_step_done_streak = ?');
values.push(updates.empty_step_done_streak);
}
if (updates.status !== undefined) {
fields.push('status = ?');
values.push(updates.status);

View File

@@ -1,6 +1,7 @@
import { Database } from 'bun:sqlite';
import { logger } from '../logger.js';
import { parseVisibleVerdict } from '../paired-verdict.js';
import { PairedRoomRole, PairedTurnOutput } from '../types.js';
const MAX_TURN_OUTPUT_CHARS = 50_000;
@@ -29,14 +30,15 @@ export function insertPairedTurnOutputInDatabase(
database
.prepare(
`INSERT OR REPLACE INTO paired_turn_outputs
(task_id, turn_number, role, output_text, created_at)
VALUES (?, ?, ?, ?, ?)`,
(task_id, turn_number, role, output_text, verdict, created_at)
VALUES (?, ?, ?, ?, ?, ?)`,
)
.run(
taskId,
turnNumber,
role,
outputText.slice(0, MAX_TURN_OUTPUT_CHARS),
parseVisibleVerdict(outputText),
createdAt ?? new Date().toISOString(),
);
}