This commit is contained in:
2026-06-22 08:55:57 +08:00
parent dbb87823e8
commit 6ade6e8fa9
325 changed files with 41131 additions and 855 deletions
+2
View File
@@ -0,0 +1,2 @@
DROP TABLE IF EXISTS acp_turns;
ALTER TABLE acp_sessions DROP COLUMN IF EXISTS last_stop_reason;
+19
View File
@@ -0,0 +1,19 @@
-- 回合完成检测:per-turn 审计记录 + session 上记录最后一次 stop reason。
-- last_stop_reason 不加 CHECK:agent 可能返回 vendor-specific 值,由 Go 层归一化但原值落库。
ALTER TABLE acp_sessions ADD COLUMN last_stop_reason TEXT;
CREATE TABLE acp_turns (
id BIGSERIAL PRIMARY KEY,
session_id UUID NOT NULL REFERENCES acp_sessions(id) ON DELETE CASCADE,
turn_index INT NOT NULL,
prompt_request_id TEXT,
status TEXT NOT NULL,
stop_reason TEXT,
update_count INT NOT NULL DEFAULT 0,
started_at TIMESTAMPTZ NOT NULL DEFAULT now(),
completed_at TIMESTAMPTZ,
CONSTRAINT acp_turns_status_check CHECK (status IN ('in_progress','completed','aborted')),
CONSTRAINT acp_turns_session_index_unique UNIQUE (session_id, turn_index)
);
CREATE INDEX idx_acp_turns_session ON acp_turns(session_id, turn_index);
CREATE INDEX idx_acp_turns_created ON acp_turns(started_at);
+3
View File
@@ -0,0 +1,3 @@
DROP INDEX IF EXISTS idx_change_requests_open;
DROP INDEX IF EXISTS idx_change_requests_workspace;
DROP TABLE IF EXISTS change_requests;
+37
View File
@@ -0,0 +1,37 @@
-- change_requests: one row links project/workspace/(requirement|issue)/source+target
-- branch with a review verdict + CI state + the external (Gitea) PR id. This is
-- the server-side merge gate: an agent opens a reviewable PR; a human approves
-- via the web before merge_pull_request can succeed.
--
-- number is a per-project sequence (max(number)+1 within project_id), mirroring
-- requirements/issues. Columns are appended in physical order; sqlc SELECT/RETURNING
-- column order must match this order (sqlc 列序约定,见 migrations/0012 comment)。
CREATE TABLE change_requests (
id UUID PRIMARY KEY,
project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
workspace_id UUID NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
requirement_id UUID REFERENCES requirements(id) ON DELETE SET NULL,
issue_id UUID REFERENCES issues(id) ON DELETE SET NULL,
number INT NOT NULL,
title TEXT NOT NULL,
description TEXT NOT NULL DEFAULT '',
source_branch TEXT NOT NULL,
target_branch TEXT NOT NULL,
state TEXT NOT NULL DEFAULT 'open' CHECK (state IN ('open','merged','closed')),
review_verdict TEXT NOT NULL DEFAULT 'pending' CHECK (review_verdict IN ('pending','approved','changes_requested','rejected')),
ci_state TEXT NOT NULL DEFAULT 'unknown' CHECK (ci_state IN ('unknown','pending','success','failure')),
provider TEXT NOT NULL DEFAULT 'gitea',
external_id BIGINT,
external_url TEXT NOT NULL DEFAULT '',
merge_commit_sha TEXT NOT NULL DEFAULT '',
reviewed_by UUID REFERENCES users(id) ON DELETE SET NULL,
reviewed_at TIMESTAMPTZ,
created_by UUID NOT NULL REFERENCES users(id) ON DELETE RESTRICT,
merged_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
UNIQUE (project_id, number)
);
CREATE INDEX idx_change_requests_workspace ON change_requests(workspace_id);
CREATE INDEX idx_change_requests_open ON change_requests(workspace_id) WHERE state = 'open';
+10
View File
@@ -0,0 +1,10 @@
-- 回滚 0018:丢弃指针表与 verdict 列,恢复三阶段 CHECK。
-- implementing/reviewing 产物在回滚时若存在会违反旧 CHECK,先删除它们(旧 schema 无处安放)。
DROP TABLE IF EXISTS requirement_phase_pointers;
ALTER TABLE requirement_artifacts DROP COLUMN IF EXISTS verdict;
DELETE FROM requirement_artifacts WHERE phase IN ('implementing','reviewing');
ALTER TABLE requirement_artifacts DROP CONSTRAINT requirement_artifacts_phase_check;
ALTER TABLE requirement_artifacts ADD CONSTRAINT requirement_artifacts_phase_check
CHECK (phase IN ('planning','prototyping','auditing'));
+24
View File
@@ -0,0 +1,24 @@
-- Phase state machine gates: extend artifact phases to 5, add per-version
-- verdict, and a per-(requirement,phase) approved/current artifact pointer.
-- (1) 扩展产物阶段 CHECK,纳入 implementing/reviewing(审计报告/评审记录也是文档型产物)。
ALTER TABLE requirement_artifacts DROP CONSTRAINT requirement_artifacts_phase_check;
ALTER TABLE requirement_artifacts ADD CONSTRAINT requirement_artifacts_phase_check
CHECK (phase IN ('planning','prototyping','auditing','implementing','reviewing'));
-- (2) 每版本评审结论。default 'none' 让现存行全部合法(无需数据迁移)。
-- 新列放在表末尾,满足 sqlc 列序约定(见 0012 注释)。
ALTER TABLE requirement_artifacts ADD COLUMN verdict TEXT NOT NULL DEFAULT 'none'
CHECK (verdict IN ('none','pass','fail'));
-- (3) 每 (requirement, phase) 的"已批准/当前"产物指针。仅在首次批准后才有行。
CREATE TABLE requirement_phase_pointers (
requirement_id UUID NOT NULL REFERENCES requirements(id) ON DELETE CASCADE,
phase TEXT NOT NULL,
approved_artifact_id UUID REFERENCES requirement_artifacts(id) ON DELETE SET NULL,
approved_by UUID REFERENCES users(id) ON DELETE SET NULL,
approved_at TIMESTAMPTZ,
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
PRIMARY KEY (requirement_id, phase)
);
CREATE INDEX idx_req_phase_pointers_artifact ON requirement_phase_pointers(approved_artifact_id);
+5
View File
@@ -0,0 +1,5 @@
DROP INDEX IF EXISTS idx_acp_sessions_orch_step;
ALTER TABLE acp_sessions DROP COLUMN IF EXISTS orchestrator_step_id;
DROP TABLE IF EXISTS orchestrator_steps;
DROP TABLE IF EXISTS orchestrator_runs;
+72
View File
@@ -0,0 +1,72 @@
-- 编排器/规划器:per-requirement run 聚合根,驱动 6 阶段状态机。每个 step 由
-- jobs (orchestrator.step) 驱动,可在重启后恢复。owner_id 是"特权但真实"的身份,
-- 用于创建所有子 session(不放松 checkNotSystemToken)。
--
-- phase CHECK 复用 requirements 的六阶段集合(planning/prototyping/auditing/
-- implementing/reviewing/done),与 internal/project Phase 枚举一致。
CREATE TABLE orchestrator_runs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
requirement_id UUID NOT NULL REFERENCES requirements(id) ON DELETE CASCADE,
workspace_id UUID NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
owner_id UUID NOT NULL REFERENCES users(id) ON DELETE RESTRICT,
status TEXT NOT NULL,
current_phase TEXT NOT NULL,
config JSONB NOT NULL DEFAULT '{}',
last_error TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
ended_at TIMESTAMPTZ,
CONSTRAINT orchestrator_runs_status_check
CHECK (status IN ('pending','running','paused','succeeded','failed','canceled')),
CONSTRAINT orchestrator_runs_phase_check
CHECK (current_phase IN ('planning','prototyping','auditing','implementing','reviewing','done'))
);
-- 每个 requirement 同一时间只允许一个活跃 run。
CREATE UNIQUE INDEX uq_orch_runs_active_requirement
ON orchestrator_runs(requirement_id)
WHERE status IN ('pending','running','paused');
CREATE INDEX idx_orch_runs_status ON orchestrator_runs(status);
CREATE TABLE orchestrator_steps (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
run_id UUID NOT NULL REFERENCES orchestrator_runs(id) ON DELETE CASCADE,
phase TEXT NOT NULL,
attempt INT NOT NULL DEFAULT 1,
parent_step_id UUID REFERENCES orchestrator_steps(id) ON DELETE SET NULL,
depth INT NOT NULL DEFAULT 0,
status TEXT NOT NULL,
agent_kind_id UUID NOT NULL REFERENCES acp_agent_kinds(id) ON DELETE RESTRICT,
acp_session_id UUID REFERENCES acp_sessions(id) ON DELETE SET NULL,
prompt TEXT NOT NULL,
stop_reason TEXT,
gate_result JSONB,
last_error TEXT,
job_id UUID,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
ended_at TIMESTAMPTZ,
CONSTRAINT orchestrator_steps_status_check
CHECK (status IN ('pending','spawning','running','awaiting_gate','succeeded','failed','dead')),
CONSTRAINT orchestrator_steps_phase_check
CHECK (phase IN ('planning','prototyping','auditing','implementing','reviewing','done'))
);
CREATE INDEX idx_orch_steps_run ON orchestrator_steps(run_id, created_at);
CREATE INDEX idx_orch_steps_session ON orchestrator_steps(acp_session_id)
WHERE acp_session_id IS NOT NULL;
CREATE INDEX idx_orch_steps_active ON orchestrator_steps(status)
WHERE status IN ('spawning','running','awaiting_gate');
CREATE INDEX idx_orch_steps_parent ON orchestrator_steps(parent_step_id)
WHERE parent_step_id IS NOT NULL;
-- 反向链接:让 ACP supervisor 的 onExit / 启动 reaper 能把崩溃 session 映射回 step。
-- manual session 留 NULL。
ALTER TABLE acp_sessions
ADD COLUMN orchestrator_step_id UUID REFERENCES orchestrator_steps(id) ON DELETE SET NULL;
CREATE INDEX idx_acp_sessions_orch_step ON acp_sessions(orchestrator_step_id)
WHERE orchestrator_step_id IS NOT NULL;
+20
View File
@@ -0,0 +1,20 @@
-- Reverse of 0020_acp_cost_budgets.up.sql (drop in reverse order).
DROP TABLE IF EXISTS acp_session_usage;
DROP INDEX IF EXISTS idx_acp_sessions_active_activity;
ALTER TABLE acp_sessions DROP COLUMN IF EXISTS terminated_reason;
ALTER TABLE acp_sessions DROP COLUMN IF EXISTS budget_max_wall_clock_seconds;
ALTER TABLE acp_sessions DROP COLUMN IF EXISTS budget_max_tokens;
ALTER TABLE acp_sessions DROP COLUMN IF EXISTS budget_max_cost_usd;
ALTER TABLE acp_sessions DROP COLUMN IF EXISTS last_activity_at;
ALTER TABLE acp_sessions DROP COLUMN IF EXISTS total_cost_usd;
ALTER TABLE acp_sessions DROP COLUMN IF EXISTS thinking_tokens;
ALTER TABLE acp_sessions DROP COLUMN IF EXISTS completion_tokens;
ALTER TABLE acp_sessions DROP COLUMN IF EXISTS prompt_tokens;
ALTER TABLE acp_agent_kinds DROP COLUMN IF EXISTS max_wall_clock_seconds;
ALTER TABLE acp_agent_kinds DROP COLUMN IF EXISTS max_tokens;
ALTER TABLE acp_agent_kinds DROP COLUMN IF EXISTS max_cost_usd;
ALTER TABLE acp_agent_kinds DROP COLUMN IF EXISTS model_id;
+49
View File
@@ -0,0 +1,49 @@
-- ACP session cost accounting + budgets + reaper inputs.
-- Column-order rule: ALTER ADD COLUMN appends at the table end, so every
-- SELECT/RETURNING that reuses the table model must list the new columns LAST.
-- ============ AgentKind: model price link + default budget caps ============
ALTER TABLE acp_agent_kinds ADD COLUMN model_id UUID REFERENCES llm_models(id) ON DELETE SET NULL;
ALTER TABLE acp_agent_kinds ADD COLUMN max_cost_usd NUMERIC(12,6);
ALTER TABLE acp_agent_kinds ADD COLUMN max_tokens BIGINT;
ALTER TABLE acp_agent_kinds ADD COLUMN max_wall_clock_seconds INT;
-- ============ Sessions: running accumulators + budget snapshot + reaper inputs ============
ALTER TABLE acp_sessions ADD COLUMN prompt_tokens BIGINT NOT NULL DEFAULT 0;
ALTER TABLE acp_sessions ADD COLUMN completion_tokens BIGINT NOT NULL DEFAULT 0;
ALTER TABLE acp_sessions ADD COLUMN thinking_tokens BIGINT NOT NULL DEFAULT 0;
ALTER TABLE acp_sessions ADD COLUMN total_cost_usd NUMERIC(14,6) NOT NULL DEFAULT 0;
ALTER TABLE acp_sessions ADD COLUMN last_activity_at TIMESTAMPTZ NOT NULL DEFAULT now();
ALTER TABLE acp_sessions ADD COLUMN budget_max_cost_usd NUMERIC(12,6);
ALTER TABLE acp_sessions ADD COLUMN budget_max_tokens BIGINT;
ALTER TABLE acp_sessions ADD COLUMN budget_max_wall_clock_seconds INT;
ALTER TABLE acp_sessions ADD COLUMN terminated_reason TEXT;
-- Index for the reaper to scan active sessions by last activity cheaply.
CREATE INDEX idx_acp_sessions_active_activity
ON acp_sessions(last_activity_at)
WHERE status IN ('starting','running');
-- ============ Per-turn usage ledger (mirrors llm_usage) ============
CREATE TABLE acp_session_usage (
id BIGSERIAL PRIMARY KEY,
session_id UUID NOT NULL REFERENCES acp_sessions(id) ON DELETE CASCADE,
user_id UUID NOT NULL,
project_id UUID NOT NULL,
agent_kind_id UUID NOT NULL,
model_id UUID,
prompt_tokens BIGINT NOT NULL DEFAULT 0,
completion_tokens BIGINT NOT NULL DEFAULT 0,
thinking_tokens BIGINT NOT NULL DEFAULT 0,
cost_usd NUMERIC(14,6) NOT NULL DEFAULT 0,
source_event_id BIGINT REFERENCES acp_events(id) ON DELETE SET NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX idx_acp_session_usage_session ON acp_session_usage(session_id);
CREATE INDEX idx_acp_session_usage_created ON acp_session_usage(created_at);
CREATE INDEX idx_acp_session_usage_user_created ON acp_session_usage(user_id, created_at);
CREATE INDEX idx_acp_session_usage_project_created ON acp_session_usage(project_id, created_at);
-- De-dup: a given source event must produce at most one ledger row.
CREATE UNIQUE INDEX uq_acp_session_usage_source_event
ON acp_session_usage(source_event_id)
WHERE source_event_id IS NOT NULL;
@@ -0,0 +1,16 @@
-- Reverse 0021_secret_key_versioning.
DROP INDEX IF EXISTS llm_endpoints_keyver_idx;
DROP INDEX IF EXISTS git_credentials_keyver_idx;
DROP INDEX IF EXISTS workspace_run_profiles_keyver_idx;
DROP INDEX IF EXISTS acp_agent_kind_config_files_keyver_idx;
DROP INDEX IF EXISTS acp_agent_kinds_keyver_idx;
ALTER TABLE acp_sessions DROP COLUMN IF EXISTS sandbox_mode;
ALTER TABLE llm_endpoints DROP COLUMN IF EXISTS key_version;
ALTER TABLE git_credentials DROP COLUMN IF EXISTS key_version;
ALTER TABLE workspace_run_profiles DROP COLUMN IF EXISTS key_version;
ALTER TABLE acp_agent_kind_config_files DROP COLUMN IF EXISTS key_version;
ALTER TABLE acp_agent_kinds DROP COLUMN IF EXISTS key_version;
DROP TABLE IF EXISTS crypto_keys;
@@ -0,0 +1,44 @@
-- ============ Secret key versioning (master-key rotation) ============
-- crypto_keys tracks WHICH key versions exist and which one is the current
-- write ("active") key. The key material itself stays in env/KMS — only the
-- version number lives in the DB, both here and in each encrypted row's
-- key_version column. This lets master-key rotation re-encrypt blobs version by
-- version without a magic ciphertext prefix.
CREATE TABLE crypto_keys (
version INT PRIMARY KEY,
provider TEXT NOT NULL DEFAULT 'env',
key_ref TEXT,
status TEXT NOT NULL DEFAULT 'active'
CHECK (status IN ('active', 'retiring', 'retired')),
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- Seed the historical single key as version 1, active.
INSERT INTO crypto_keys (version, provider, status) VALUES (1, 'env', 'active');
-- Per-row key_version on every encrypted-blob owner. DEFAULT 1 backfills every
-- existing row to the historical key. The re-encrypt job finds rows on an old
-- version and Decrypt selects the right key by this column.
ALTER TABLE acp_agent_kinds ADD COLUMN key_version SMALLINT NOT NULL DEFAULT 1;
ALTER TABLE acp_agent_kind_config_files ADD COLUMN key_version SMALLINT NOT NULL DEFAULT 1;
ALTER TABLE workspace_run_profiles ADD COLUMN key_version SMALLINT NOT NULL DEFAULT 1;
-- 注意:git 凭据密文在 git_credentials.encrypted_secret,不在 workspaces(后者无密文列)。
ALTER TABLE git_credentials ADD COLUMN key_version SMALLINT NOT NULL DEFAULT 1;
ALTER TABLE llm_endpoints ADD COLUMN key_version SMALLINT NOT NULL DEFAULT 1;
-- Partial indexes to make the re-encrypt scan (WHERE key_version < active AND
-- blob IS NOT NULL) cheap. Only index rows that actually carry ciphertext.
CREATE INDEX acp_agent_kinds_keyver_idx ON acp_agent_kinds (key_version)
WHERE encrypted_env IS NOT NULL OR encrypted_mcp_servers IS NOT NULL;
CREATE INDEX acp_agent_kind_config_files_keyver_idx ON acp_agent_kind_config_files (key_version)
WHERE encrypted_content IS NOT NULL;
CREATE INDEX workspace_run_profiles_keyver_idx ON workspace_run_profiles (key_version)
WHERE encrypted_env IS NOT NULL;
CREATE INDEX git_credentials_keyver_idx ON git_credentials (key_version)
WHERE encrypted_secret IS NOT NULL;
CREATE INDEX llm_endpoints_keyver_idx ON llm_endpoints (key_version)
WHERE api_key_encrypted IS NOT NULL;
-- Record which sandbox mode a session ran under (audit/forensics). Cheap and
-- recommended by the spec; NULL for sessions that predate this column.
ALTER TABLE acp_sessions ADD COLUMN sandbox_mode TEXT;
@@ -0,0 +1,5 @@
DROP TABLE IF EXISTS code_chunks;
DROP TABLE IF EXISTS code_index_runs;
-- Only this migration created the extension; drop it on full rollback. Guarded
-- so it is a no-op if a later feature already depends on it.
DROP EXTENSION IF EXISTS vector;
@@ -0,0 +1,49 @@
-- Code index (pgvector RAG). Guarded CREATE EXTENSION so deployments without
-- superuser/owner privileges or without the pgvector image fail loudly here
-- (and the code degrades to keyword fallback when no embedding endpoint is set).
CREATE EXTENSION IF NOT EXISTS vector;
-- code_index_runs tracks one (workspace, commit, embedding_model) build attempt.
CREATE TABLE code_index_runs (
id uuid PRIMARY KEY,
workspace_id uuid NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
worktree_id uuid NULL REFERENCES workspace_worktrees(id) ON DELETE SET NULL,
branch text NOT NULL,
commit_sha text NOT NULL,
status text NOT NULL DEFAULT 'pending'
CHECK (status IN ('pending','running','completed','failed','stale')),
embedding_endpoint_id uuid REFERENCES llm_endpoints(id),
embedding_model text NOT NULL,
dims int NOT NULL,
files_indexed int NOT NULL DEFAULT 0,
chunks_indexed int NOT NULL DEFAULT 0,
error text,
started_at timestamptz,
finished_at timestamptz,
created_at timestamptz NOT NULL DEFAULT now(),
UNIQUE (workspace_id, commit_sha, embedding_model)
);
CREATE INDEX idx_code_index_runs_ws_status ON code_index_runs (workspace_id, status);
-- code_chunks stores embedded line-window chunks. The vector dimension is fixed
-- at migration time; one embedding model per deployment is enforced via config.
CREATE TABLE code_chunks (
id uuid PRIMARY KEY,
run_id uuid NOT NULL REFERENCES code_index_runs(id) ON DELETE CASCADE,
workspace_id uuid NOT NULL,
commit_sha text NOT NULL,
file_path text NOT NULL,
lang text,
start_line int NOT NULL,
end_line int NOT NULL,
content text NOT NULL,
content_sha bytea NOT NULL,
token_count int,
embedding vector(1536) NOT NULL,
created_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX idx_code_chunks_embedding ON code_chunks USING hnsw (embedding vector_cosine_ops);
CREATE INDEX idx_code_chunks_ws_commit ON code_chunks (workspace_id, commit_sha);
CREATE INDEX idx_code_chunks_file ON code_chunks (workspace_id, file_path);
+1
View File
@@ -0,0 +1 @@
DROP TABLE IF EXISTS project_memory;
+21
View File
@@ -0,0 +1,21 @@
-- project_memory: durable, typed project knowledge with optional embeddings.
CREATE TABLE project_memory (
id uuid PRIMARY KEY,
project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
workspace_id uuid NULL REFERENCES workspaces(id) ON DELETE CASCADE,
kind text NOT NULL CHECK (kind IN ('decision','convention','file_map','gotcha')),
title text NOT NULL,
body text NOT NULL,
tags text[] NOT NULL DEFAULT '{}',
source text NOT NULL DEFAULT 'agent' CHECK (source IN ('agent','user','auto')),
embedding vector(1536) NULL,
embedding_model text NULL,
created_by uuid REFERENCES users(id),
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX idx_project_memory_proj ON project_memory (project_id, kind);
CREATE INDEX idx_project_memory_embedding ON project_memory USING hnsw (embedding vector_cosine_ops)
WHERE embedding IS NOT NULL;
CREATE INDEX idx_project_memory_tags ON project_memory USING gin (tags);
+1
View File
@@ -0,0 +1 @@
DROP TABLE IF EXISTS commit_summaries;
+18
View File
@@ -0,0 +1,18 @@
-- commit_summaries: auto-generated commit / PR markdown summaries.
CREATE TABLE commit_summaries (
id uuid PRIMARY KEY,
workspace_id uuid NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
worktree_id uuid NULL,
branch text NOT NULL,
commit_sha text NOT NULL,
kind text NOT NULL DEFAULT 'commit' CHECK (kind IN ('commit','pr')),
title text,
body_md text NOT NULL,
model text,
status text NOT NULL DEFAULT 'pending' CHECK (status IN ('pending','completed','failed')),
error text,
created_at timestamptz NOT NULL DEFAULT now(),
UNIQUE (workspace_id, commit_sha, kind)
);
CREATE INDEX idx_commit_summaries_ws ON commit_summaries (workspace_id, commit_sha);
+7
View File
@@ -0,0 +1,7 @@
DROP TABLE IF EXISTS issue_dependencies;
DROP INDEX IF EXISTS idx_issues_parent;
ALTER TABLE issues DROP CONSTRAINT IF EXISTS issues_priority_check;
ALTER TABLE issues DROP CONSTRAINT IF EXISTS issues_parent_not_self;
ALTER TABLE issues DROP COLUMN IF EXISTS priority;
ALTER TABLE issues DROP COLUMN IF EXISTS parent_id;
+34
View File
@@ -0,0 +1,34 @@
-- Task decomposition + dependency graph (autonomy roadmap §10).
-- Additive only (follows the existing convention where 0003/0015 patched issues).
-- ============ issues: parent_id + priority ============
-- parent_id: self-FK for subtask hierarchy. ON DELETE SET NULL so deleting a
-- parent orphans (not cascades) its subtasks. CHECK forbids self-parent.
ALTER TABLE issues
ADD COLUMN parent_id UUID REFERENCES issues(id) ON DELETE SET NULL;
ALTER TABLE issues
ADD CONSTRAINT issues_parent_not_self CHECK (parent_id IS NULL OR parent_id <> id);
-- priority: higher = scheduled sooner. 0..3 (0 default = normal).
ALTER TABLE issues
ADD COLUMN priority INT NOT NULL DEFAULT 0;
ALTER TABLE issues
ADD CONSTRAINT issues_priority_check CHECK (priority BETWEEN 0 AND 3);
CREATE INDEX idx_issues_parent ON issues(parent_id) WHERE parent_id IS NOT NULL;
-- ============ issue_dependencies ============
-- Semantics: blocked_id is blocked-by blocker_id; blocked_id becomes ready only
-- when blocker_id.status='closed'. project_id denormalized for scope/cycle scoping.
CREATE TABLE issue_dependencies (
id UUID PRIMARY KEY,
project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
blocked_id UUID NOT NULL REFERENCES issues(id) ON DELETE CASCADE,
blocker_id UUID NOT NULL REFERENCES issues(id) ON DELETE CASCADE,
created_by UUID NOT NULL REFERENCES users(id) ON DELETE RESTRICT,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
UNIQUE (blocked_id, blocker_id),
CONSTRAINT issue_deps_not_self CHECK (blocked_id <> blocker_id)
);
CREATE INDEX idx_issue_deps_blocked ON issue_dependencies(blocked_id);
CREATE INDEX idx_issue_deps_blocker ON issue_dependencies(blocker_id);
@@ -0,0 +1,12 @@
-- Down migration for 0026_observability_hitl (reverse order).
DROP TABLE IF EXISTS notification_prefs;
DROP INDEX IF EXISTS idx_project_members_user;
DROP TABLE IF EXISTS project_members;
ALTER TABLE acp_sessions DROP COLUMN IF EXISTS tokens_total;
ALTER TABLE acp_sessions DROP COLUMN IF EXISTS cost_usd;
DROP INDEX IF EXISTS idx_audit_target;
DROP INDEX IF EXISTS idx_audit_created;
+45
View File
@@ -0,0 +1,45 @@
-- 0026_observability_hitl: Observability + HITL data model (autonomy roadmap §11).
--
-- 1) audit_logs read/search/retention indexes (idx_audit_action_created already
-- exists from 0001; add the time-only and target indexes).
-- 2) acp_sessions cost_usd / tokens_total columns for the run-history dashboard.
-- 3) project_members team/role model (backfill owner -> 'admin').
-- 4) notification_prefs per-user channel routing (im_webhook_url crypto-encrypted).
-- ============ 1. audit read/search/retention indexes ============
CREATE INDEX IF NOT EXISTS idx_audit_created ON audit_logs (created_at DESC);
CREATE INDEX IF NOT EXISTS idx_audit_target ON audit_logs (target_type, target_id);
-- ============ 2. acp_sessions cost rollup columns ============
-- ADD COLUMN puts new columns LAST physically; SELECT/RETURNING order must match.
ALTER TABLE acp_sessions ADD COLUMN IF NOT EXISTS cost_usd NUMERIC NOT NULL DEFAULT 0;
ALTER TABLE acp_sessions ADD COLUMN IF NOT EXISTS tokens_total BIGINT NOT NULL DEFAULT 0;
-- ============ 3. project_members team/role model ============
CREATE TABLE IF NOT EXISTS project_members (
project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
role TEXT NOT NULL CHECK (role IN ('viewer','member','admin')),
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
added_by UUID REFERENCES users(id) ON DELETE SET NULL,
PRIMARY KEY (project_id, user_id)
);
CREATE INDEX IF NOT EXISTS idx_project_members_user ON project_members (user_id);
-- Backfill: every existing project's owner becomes a project 'admin' so the
-- current owner-or-global-admin behavior is preserved under the role model.
INSERT INTO project_members (project_id, user_id, role, added_by)
SELECT p.id, p.owner_id, 'admin', p.owner_id
FROM projects p
ON CONFLICT (project_id, user_id) DO NOTHING;
-- ============ 4. notification_prefs per-user routing ============
CREATE TABLE IF NOT EXISTS notification_prefs (
user_id UUID PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE,
email_enabled BOOLEAN NOT NULL DEFAULT false,
im_enabled BOOLEAN NOT NULL DEFAULT false,
im_webhook_url_enc BYTEA,
min_severity TEXT NOT NULL DEFAULT 'warning' CHECK (min_severity IN ('info','warning','error')),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);