You've already forked agentic-coding-workflow
bugfix
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
DROP TABLE IF EXISTS acp_turns;
|
||||
ALTER TABLE acp_sessions DROP COLUMN IF EXISTS last_stop_reason;
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
@@ -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';
|
||||
@@ -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'));
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
@@ -0,0 +1 @@
|
||||
DROP TABLE IF EXISTS project_memory;
|
||||
@@ -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);
|
||||
@@ -0,0 +1 @@
|
||||
DROP TABLE IF EXISTS commit_summaries;
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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()
|
||||
);
|
||||
Reference in New Issue
Block a user