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

+34 -1
View File
@@ -63,8 +63,24 @@ const EXPECTED_TABLES = [
"server_mapping",
"organization_record",
"organization_file_result",
// tasks 包自有表(0013)与 agent 事件(0014)
"tasks_run_meta",
"tasks_fact",
"tasks_op_ledger",
"agent_run_event",
].sort();
/** 不走 entityTable() 的表:追加型/引擎自有表,没有 uuid id + created_at/updated_at 三件套 */
const NON_ENTITY_TABLES = [
"settings",
"audit_event",
"outbox_event",
"tasks_run_meta",
"tasks_fact",
"tasks_op_ledger",
"agent_run_event",
];
describe.skipIf(skip)("schema migrations (real PG)", () => {
let admin: Kysely<unknown>;
let db: Kysely<DB>;
@@ -108,7 +124,7 @@ describe.skipIf(skip)("schema migrations (real PG)", () => {
select table_name, column_name, column_default from information_schema.columns
where table_schema = current_schema() and column_name in ('id', 'created_at', 'updated_at')`.execute(db)
).rows;
for (const t of EXPECTED_TABLES.filter((x) => !["settings", "audit_event", "outbox_event"].includes(x))) {
for (const t of EXPECTED_TABLES.filter((x) => !NON_ENTITY_TABLES.includes(x))) {
const cols = rows.filter((r) => r.table_name === t);
expect(cols.map((c) => c.column_name).sort(), t).toEqual(["created_at", "id", "updated_at"]);
expect(cols.find((c) => c.column_name === "id")?.column_default, t).toBe("gen_random_uuid()");
@@ -291,6 +307,23 @@ describe.skipIf(skip)("schema migrations (real PG)", () => {
expect(hit.rows.map((r) => r.id)).toContain(ent.id);
});
it("agent_run_event:(run_id, seq) 唯一,删 run 级联删事件", async () => {
const run = await db
.insertInto("agent_run")
.values({ trigger: "user", goal: "g" })
.returning("id")
.executeTakeFirstOrThrow();
const ev = { run_id: run.id, seq: 0, type: "run_started" as const, payload: JSON.stringify({ goal: "g" }) };
await db.insertInto("agent_run_event").values(ev).execute();
await expect(db.insertInto("agent_run_event").values(ev).execute()).rejects.toThrow(/agent_run_event_pkey/);
await expect(
sql`insert into agent_run_event (run_id, seq, type) values (${run.id}, 1, 'bogus')`.execute(db),
).rejects.toThrow(/check constraint/);
await db.deleteFrom("agent_run").where("id", "=", run.id).execute();
const left = await db.selectFrom("agent_run_event").select("seq").where("run_id", "=", run.id).execute();
expect(left).toEqual([]);
});
it("迁移可完整 down 再 up(往返)", async () => {
const migrator = createMigrator(db, { migrationTableSchema: schema });
const down = await migrator.migrateTo(NO_MIGRATIONS);