feat(db,contracts): full v0.1 data model migrations and shared zod contracts (W1-1)
- 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]>
This commit is contained in:
1 parent
7ba712ea55
commit
cc8b3a7df4
42 files changed
+7026
-25
No files matched your search
@@ -0,0 +1,125 @@
|
||||
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"]));
|
||||
}
|
||||
Reference in new issue
Block a user