refactor(db): rename agent_runs/agent_run_actions to scout_*

Rename Drizzle table definitions: agentRuns → scoutRuns,
agentRunActions → scoutRunActions. Column agentId → scoutId.
Hand-crafted migration 0007_scouts_rename.sql uses ALTER TABLE RENAME
+ CREATE/INSERT/DROP for column rename (SQLite limitation). Updated
all main-process consumers (backend-client, agent-scheduler, router).
Renderer-side type/component rename deferred to Tasks 8-9.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Roberto
2026-05-16 01:06:21 +02:00
parent c1b1b289c1
commit 9b66dc3329
7 changed files with 1137 additions and 28 deletions

View File

@@ -9,7 +9,7 @@
import { getLocalAgents, saveLocalAgent, getDeviceId } from '../store';
import { getBackendClient } from '../api/backend-client';
import { getDb } from '../db';
import { agentRuns } from '../db/schema';
import { scoutRuns } from '../db/schema';
// ---------------------------------------------------------------------------
// Constants
@@ -99,9 +99,9 @@ async function tickAgentScheduler(): Promise<void> {
// the agent finds nothing to create/update.
if (response?.id) {
try {
await getDb().insert(agentRuns).values({
await getDb().insert(scoutRuns).values({
id: response.id,
agentId: agent.id,
scoutId: agent.id,
status: 'running',
startedAt: now,
}).onConflictDoNothing();

View File

@@ -32,7 +32,7 @@ import type {
} from '../../shared/api-types';
import { DrizzleExecutor } from './drizzle-executor';
import { getDb } from '../db';
import { agentRuns, agentRunActions } from '../db/schema';
import { scoutRuns, scoutRunActions } from '../db/schema';
// ---------------------------------------------------------------------------
// Agent run logging helpers
@@ -60,10 +60,10 @@ async function recordRunAction(
entityTitle: string | null,
): Promise<void> {
try {
await getDb().insert(agentRunActions).values({
await getDb().insert(scoutRunActions).values({
id: crypto.randomUUID(),
runId,
agentId,
scoutId: agentId,
verb,
entityType,
entityId: entityId ?? null,
@@ -989,9 +989,9 @@ export class BackendClient {
void (async () => {
try {
const db = getDb();
await db.update(agentRuns)
await db.update(scoutRuns)
.set({ status: status === 'success' ? 'completed' : status === 'partial' ? 'partial' : 'failed', completedAt: Date.now() })
.where(eq(agentRuns.id, runContext.runId));
.where(eq(scoutRuns.id, runContext.runId));
} catch (err) {
console.warn('[RunLog] Failed to close run:', err);
}

View File

@@ -0,0 +1,44 @@
-- Rename agent_runs → scout_runs and agent_run_actions → scout_run_actions
-- SQLite supports ALTER TABLE RENAME TO; column rename (agent_id → scout_id) requires recreate.
-- Step 1: rename agent_runs table
ALTER TABLE `agent_runs` RENAME TO `scout_runs`;
--> statement-breakpoint
-- Step 2: rename agent_run_actions table
ALTER TABLE `agent_run_actions` RENAME TO `scout_run_actions`;
--> statement-breakpoint
-- Step 3: rename agent_id column in scout_runs (SQLite requires full table recreate for column rename)
CREATE TABLE `__new_scout_runs` (
`id` text PRIMARY KEY NOT NULL,
`scout_id` text NOT NULL,
`status` text DEFAULT 'running' NOT NULL,
`started_at` integer NOT NULL,
`completed_at` integer
);
--> statement-breakpoint
INSERT INTO `__new_scout_runs` SELECT `id`, `agent_id`, `status`, `started_at`, `completed_at` FROM `scout_runs`;
--> statement-breakpoint
DROP TABLE `scout_runs`;
--> statement-breakpoint
ALTER TABLE `__new_scout_runs` RENAME TO `scout_runs`;
--> statement-breakpoint
-- Step 4: rename agent_id column in scout_run_actions
CREATE TABLE `__new_scout_run_actions` (
`id` text PRIMARY KEY NOT NULL,
`run_id` text NOT NULL,
`scout_id` text NOT NULL,
`verb` text NOT NULL,
`entity_type` text NOT NULL,
`entity_id` text,
`entity_title` text,
`created_at` integer NOT NULL
);
--> statement-breakpoint
INSERT INTO `__new_scout_run_actions` SELECT `id`, `run_id`, `agent_id`, `verb`, `entity_type`, `entity_id`, `entity_title`, `created_at` FROM `scout_run_actions`;
--> statement-breakpoint
DROP TABLE `scout_run_actions`;
--> statement-breakpoint
ALTER TABLE `__new_scout_run_actions` RENAME TO `scout_run_actions`;

File diff suppressed because it is too large Load Diff

View File

@@ -50,6 +50,13 @@
"when": 1778777130582,
"tag": "0006_misty_cammi",
"breakpoints": true
},
{
"idx": 7,
"version": "6",
"when": 1747353600000,
"tag": "0007_scouts_rename",
"breakpoints": true
}
]
}

View File

@@ -169,18 +169,18 @@ export const taskBriefChats = sqliteTable('task_brief_chats', {
export type TaskBriefChat = InferSelectModel<typeof taskBriefChats>;
export type NewTaskBriefChat = InferInsertModel<typeof taskBriefChats>;
export const agentRuns = sqliteTable('agent_runs', {
export const scoutRuns = sqliteTable('scout_runs', {
id: text('id').primaryKey(),
agentId: text('agent_id').notNull(),
scoutId: text('scout_id').notNull(),
status: text('status', { enum: ['running', 'completed', 'failed', 'partial'] }).notNull().default('running'),
startedAt: integer('started_at', { mode: 'number' }).notNull(),
completedAt: integer('completed_at', { mode: 'number' }),
});
export const agentRunActions = sqliteTable('agent_run_actions', {
export const scoutRunActions = sqliteTable('scout_run_actions', {
id: text('id').primaryKey(),
runId: text('run_id').notNull(),
agentId: text('agent_id').notNull(),
scoutId: text('scout_id').notNull(),
/** 'created' | 'updated' | 'deleted' | 'commented' */
verb: text('verb').notNull(),
/** 'task' | 'note' | 'project' | 'timeline' | 'comment' */
@@ -190,10 +190,10 @@ export const agentRunActions = sqliteTable('agent_run_actions', {
createdAt: integer('created_at', { mode: 'number' }).notNull(),
});
export type AgentRun = InferSelectModel<typeof agentRuns>;
export type NewAgentRun = InferInsertModel<typeof agentRuns>;
export type AgentRunAction = InferSelectModel<typeof agentRunActions>;
export type NewAgentRunAction = InferInsertModel<typeof agentRunActions>;
export type ScoutRun = InferSelectModel<typeof scoutRuns>;
export type NewScoutRun = InferInsertModel<typeof scoutRuns>;
export type ScoutRunAction = InferSelectModel<typeof scoutRunActions>;
export type NewScoutRunAction = InferInsertModel<typeof scoutRunActions>;
export type NoteEdit = InferSelectModel<typeof noteEdits>;
export type NewNoteEdit = InferInsertModel<typeof noteEdits>;

View File

@@ -6,7 +6,7 @@ import { dialog, shell } from 'electron';
import { randomUUID } from 'node:crypto';
import { stat } from 'node:fs/promises';
import { getDb } from '../db';
import { clients, projects, tasks, timelineEvents, timelineEventDependencies, notes, noteEdits, taskComments, taskAttachments, agentRuns, agentRunActions, taskBriefings, taskBriefChats } from '../db/schema';
import { clients, projects, tasks, timelineEvents, timelineEventDependencies, notes, noteEdits, taskComments, taskAttachments, scoutRuns, scoutRunActions, taskBriefings, taskBriefChats } from '../db/schema';
import { copyIntoTask, deleteStored, absolutePath, deleteTaskDir } from '../attachments/storage';
import { createHash } from 'crypto';
import { getStore, getDeviceId, getLocalAgents, getLocalAgent, saveLocalAgent, deleteLocalAgent, getFormatPrefs, setFormatPrefs, getUiLanguage, setUiLanguage, getTimelineZoom, setTimelineZoom } from '../store';
@@ -1289,18 +1289,18 @@ const agentRouter = router({
const offset = input.offset ?? 0;
const rows = await db
.select()
.from(agentRuns)
.where(eq(agentRuns.agentId, input.agentId))
.orderBy(desc(agentRuns.startedAt))
.from(scoutRuns)
.where(eq(scoutRuns.scoutId, input.agentId))
.orderBy(desc(scoutRuns.startedAt))
.limit(limit)
.offset(offset);
// Compute per-run action counts in one query
const runIds = rows.map(r => r.id);
const actionRows = runIds.length > 0
? await db.select({ runId: agentRunActions.runId, verb: agentRunActions.verb, entityType: agentRunActions.entityType })
.from(agentRunActions)
.where(inArray(agentRunActions.runId, runIds))
? await db.select({ runId: scoutRunActions.runId, verb: scoutRunActions.verb, entityType: scoutRunActions.entityType })
.from(scoutRunActions)
.where(inArray(scoutRunActions.runId, runIds))
: [];
type ActionCounts = { created: number; updated: number; deleted: number };
@@ -1332,9 +1332,9 @@ const agentRouter = router({
const db = getDb();
return await db
.select()
.from(agentRunActions)
.where(eq(agentRunActions.runId, input.runId))
.orderBy(asc(agentRunActions.createdAt));
.from(scoutRunActions)
.where(eq(scoutRunActions.runId, input.runId))
.orderBy(asc(scoutRunActions.createdAt));
} catch (err) {
console.error('[Agent] runActions error:', err);
return [];
@@ -1383,9 +1383,9 @@ const agentRouter = router({
// Create the run row so it appears in history even with zero mutations
if (result?.id) {
try {
await getDb().insert(agentRuns).values({
await getDb().insert(scoutRuns).values({
id: result.id,
agentId: agent.id,
scoutId: agent.id,
status: 'running',
startedAt: Date.now(),
}).onConflictDoNothing();