feat(db): 0013 tasks tables and 0014 agent_run_event; tasks uses db migrations

tasks_run_meta/tasks_fact/tasks_op_ledger move into packages/db (if not exists, old
tasks_migration tables dropped); packages/tasks/migrations and migrateTasksTables
removed, tests migrate their schema with @umt/db. agent_run_event(run_id, seq, type,
at, payload) with (run_id, seq) primary key; types regenerated, db-sync covers it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-09-27 19:15:50 +08:00
1 parent db5352eecd
commit da8e53bbca
13 files changed
+250 -36

No files matched your search

+10 -18
View File
@@ -1,6 +1,7 @@
import { migrateToLatest } from "@umt/db";
import { Kysely, PostgresDialect, sql } from "kysely";
import pg from "pg";
import { migrateTasksTables, OpenWorkflowEngine, silentSink, type TaskRegistry } from "../src/index.ts";
import { OpenWorkflowEngine, silentSink, type TaskRegistry } from "../src/index.ts";
export const PG_URL =
process.env.TASKS_TEST_DATABASE_URL ?? process.env.DATABASE_URL ?? "postgres://umt:umt@localhost:55433/umt";
@@ -32,11 +33,17 @@ export function testSchemas(suffix: string) {
return { biz, ow: `${biz}_ow` };
}
/** search_path 带上 public:db 迁移用到的 pg_trgm 操作符类装在 public */
export function connectDb(schema: string): TestDb {
const pool = new pg.Pool({ connectionString: PG_URL, max: 6, options: `-c search_path=${schema}` });
const pool = new pg.Pool({ connectionString: PG_URL, max: 6, options: `-c search_path=${schema},public` });
return new Kysely({ dialect: new PostgresDialect({ pool }) });
}
/** 用 @umt/db 的统一迁移在测试 schema 里建全部业务表(含 tasks_* 与 outbox_event) */
export async function migrateBizSchema(db: TestDb, schema: string) {
await migrateToLatest(db, { migrationTableSchema: schema });
}
export async function resetSchemas(schemas: { biz: string; ow: string }) {
const admin = connectDb("public");
try {
@@ -58,21 +65,6 @@ export async function dropSchemas(schemas: { biz: string; ow: string }) {
}
}
/** 与 packages/db 0001_init + 0002_foundation 的 outbox_event 同构 */
export async function createOutboxTable(db: TestDb) {
await sql`CREATE TABLE outbox_event (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
at timestamptz NOT NULL DEFAULT now(),
type text NOT NULL,
payload jsonb NOT NULL DEFAULT '{}'::jsonb,
published_at timestamptz,
idempotency_key text UNIQUE,
status text NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'dispatched', 'failed', 'dead')),
attempts integer NOT NULL DEFAULT 0,
next_attempt_at timestamptz NOT NULL DEFAULT now(),
last_error jsonb)`.execute(db);
}
export async function createSideEffectTable(db: TestDb) {
await sql`CREATE TABLE IF NOT EXISTS side_effect (
id bigserial PRIMARY KEY, run_id text NOT NULL, step text NOT NULL, note text, pid int,
@@ -85,8 +77,8 @@ export async function sideEffects(db: TestDb, runId: string) {
return r.rows;
}
/** 只连接引擎;业务表由调用方先 migrateBizSchema 建好 */
export async function setupEngine(db: TestDb, schemas: { ow: string }, registry: TaskRegistry) {
await migrateTasksTables(db);
return OpenWorkflowEngine.connect({
databaseUrl: PG_URL,
db,