- migrations 0002-0012 split by domain (foundation/instances/content/identity/ sources/assets/policy/execution/agent/ops/organization): 41 tables, uuid pk, created_at/updated_at + touch trigger, text+CHECK status sets, pgcrypto/pg_trgm - key constraints: source_release(site_id, site_release_id), observation(feed_id, guid), external_identity accepted partial unique, download_task(instance_id, torrent_hash), one active acquisition_plan per slot, one active path_binding per entity/season, asset_location root+path uniqueness, organization_record change_seq/change_xid cursor - secret table (ciphertext/nonce/kdf) + secret_ref columns; outbox dispatch fields (ADR-002) - src/types.ts generated from the migrated schema (scripts/gen-types.ts), drift-checked in tests; int8 -> number, date -> string parsers; migrator accepts migrationTableSchema - contracts: Row/Create/Update schemas per domain, public organization-record API (changeSeq cursor), GlobalSettings, CapabilityDescriptor + 14 capability IO schemas, typed SSE events, Api endpoint table - tests: migration up/down round trip on real PG in an isolated schema, constraint checks, contracts<->db column/enum sync, positive/negative parse for every exported schema Co-Authored-By: Claude Fable 5.1 <[email protected]>
126 lines
4.9 KiB
TypeScript
126 lines
4.9 KiB
TypeScript
import type { Kysely } from "kysely";
|
|
import { dropTables, entityTable, execDdl, oneOf } from "../src/migration-helpers.ts";
|
|
|
|
// 人工/AI 域:decision(待处理中心)、approval、automation_grant、agent_run、artifact(REQ §10、§11.3、§11.4)
|
|
// 提案不直接晋升事实;审批绑定 plan ID + payload hash + expected revision;授权先落库再唤醒任务。
|
|
|
|
// biome-ignore lint/suspicious/noExplicitAny: 迁移不依赖应用表类型
|
|
export async function up(db: Kysely<any>): Promise<void> {
|
|
await execDdl(
|
|
db,
|
|
`
|
|
${entityTable(
|
|
"agent_run",
|
|
`
|
|
task_id uuid references task (id) on delete set null,
|
|
parent_run_id uuid references agent_run (id) on delete set null,
|
|
trigger text not null ${oneOf("trigger", ["user", "event", "schedule", "subagent"])},
|
|
goal text not null,
|
|
status text not null default 'queued' ${oneOf("status", ["queued", "running", "waiting", "succeeded", "failed", "cancelled", "budget_exceeded"])},
|
|
provider text,
|
|
model text,
|
|
thinking_level text,
|
|
scope jsonb not null default '{}'::jsonb,
|
|
budget jsonb not null default '{}'::jsonb,
|
|
usage jsonb not null default '{}'::jsonb,
|
|
evidence_version text,
|
|
session_state jsonb,
|
|
summary text,
|
|
tool_call_count integer not null default 0,
|
|
last_event_seq bigint not null default 0,
|
|
error jsonb,
|
|
cancel_requested_at timestamptz,
|
|
started_at timestamptz,
|
|
finished_at timestamptz,
|
|
created_by text`,
|
|
)}
|
|
create index agent_run_status_idx on agent_run (status);
|
|
create index agent_run_task_idx on agent_run (task_id) where task_id is not null;
|
|
create index agent_run_created_idx on agent_run (created_at desc);
|
|
|
|
${entityTable(
|
|
"artifact",
|
|
`
|
|
kind text not null ${oneOf("kind", ["evidence", "plan", "proposal", "report", "snapshot", "log"])},
|
|
agent_run_id uuid references agent_run (id) on delete set null,
|
|
task_id uuid references task (id) on delete set null,
|
|
subject_type text,
|
|
subject_id uuid,
|
|
title text not null,
|
|
mime_type text not null default 'application/json',
|
|
content jsonb not null default '{}'::jsonb,
|
|
content_hash text not null,
|
|
size_bytes bigint,
|
|
expires_at timestamptz`,
|
|
)}
|
|
create index artifact_run_idx on artifact (agent_run_id) where agent_run_id is not null;
|
|
create index artifact_subject_idx on artifact (subject_type, subject_id) where subject_id is not null;
|
|
|
|
${entityTable(
|
|
"decision",
|
|
`
|
|
kind text not null ${oneOf("kind", ["identity_ambiguous", "download_stalled", "download_failed", "path_conflict", "space_insufficient", "approval_required", "metadata_conflict", "source_unreachable", "other"])},
|
|
status text not null default 'open' ${oneOf("status", ["open", "investigating", "awaiting_user", "resolved", "dismissed", "expired"])},
|
|
severity text not null default 'warning' ${oneOf("severity", ["info", "warning", "critical"])},
|
|
title text not null,
|
|
summary text,
|
|
subject_type text,
|
|
subject_id uuid,
|
|
dedupe_key text not null,
|
|
evidence jsonb not null default '{}'::jsonb,
|
|
options jsonb not null default '[]'::jsonb,
|
|
resolution jsonb,
|
|
task_id uuid references task (id) on delete set null,
|
|
agent_run_id uuid references agent_run (id) on delete set null,
|
|
attempts integer not null default 0,
|
|
next_review_at timestamptz,
|
|
resolved_by text,
|
|
resolved_at timestamptz`,
|
|
)}
|
|
create unique index decision_dedupe_open_key on decision (dedupe_key) where status in ('open', 'investigating', 'awaiting_user');
|
|
create index decision_status_idx on decision (status, created_at desc);
|
|
create index decision_next_review_idx on decision (next_review_at) where next_review_at is not null;
|
|
|
|
${entityTable(
|
|
"approval",
|
|
`
|
|
plan_type text not null,
|
|
plan_id uuid not null,
|
|
payload_hash text not null,
|
|
expected_revision integer not null,
|
|
scope jsonb not null default '{}'::jsonb,
|
|
status text not null default 'pending' ${oneOf("status", ["pending", "approved", "rejected", "revoked", "expired", "consumed"])},
|
|
decision_id uuid references decision (id) on delete set null,
|
|
requested_by text not null,
|
|
decided_by text,
|
|
decided_at timestamptz,
|
|
expires_at timestamptz,
|
|
reason text`,
|
|
)}
|
|
create index approval_plan_idx on approval (plan_type, plan_id);
|
|
create index approval_pending_idx on approval (created_at) where status = 'pending';
|
|
|
|
${entityTable(
|
|
"automation_grant",
|
|
`
|
|
action text not null,
|
|
policy_revision_id uuid references policy_revision (id) on delete set null,
|
|
scope jsonb not null default '{}'::jsonb,
|
|
budget jsonb not null default '{}'::jsonb,
|
|
status text not null default 'active' ${oneOf("status", ["active", "revoked", "expired"])},
|
|
granted_by text not null,
|
|
reason text,
|
|
expires_at timestamptz,
|
|
revoked_at timestamptz,
|
|
revoked_by text`,
|
|
)}
|
|
create index automation_grant_active_idx on automation_grant (action) where status = 'active';
|
|
`,
|
|
);
|
|
}
|
|
|
|
// biome-ignore lint/suspicious/noExplicitAny: 同上
|
|
export async function down(db: Kysely<any>): Promise<void> {
|
|
await execDdl(db, dropTables(["automation_grant", "approval", "decision", "artifact", "agent_run"]));
|
|
}
|