-- +goose Up CREATE TABLE projects ( id uuid PRIMARY KEY, slug text NOT NULL UNIQUE, name text NOT NULL, description text NOT NULL DEFAULT '', created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now() ); CREATE TABLE integration_instances ( id uuid PRIMARY KEY, project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE, name text NOT NULL, kind text NOT NULL CHECK (kind IN ( 'source', 'retrieval_backend', 'artifact_store', 'embedding_provider', 'rerank_provider', 'generation_provider', 'executor' )), implementation text NOT NULL, schema_version text NOT NULL DEFAULT 'v1', capabilities jsonb NOT NULL DEFAULT '{}'::jsonb, config jsonb NOT NULL DEFAULT '{}'::jsonb, secret_ref text, enabled boolean NOT NULL DEFAULT true, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), UNIQUE (project_id, name) ); CREATE TABLE knowledge_sources ( id uuid PRIMARY KEY, project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE, integration_instance_id uuid NOT NULL REFERENCES integration_instances(id), key text NOT NULL, name text NOT NULL, revision text, cursor jsonb NOT NULL DEFAULT '{}'::jsonb, sync_state text NOT NULL DEFAULT 'pending' CHECK (sync_state IN ('pending', 'syncing', 'ready', 'failed', 'disabled')), last_synced_at timestamptz, tombstone_checked_at timestamptz, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), UNIQUE (project_id, key) ); CREATE TABLE knowledge_bases ( id uuid PRIMARY KEY, project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE, key text NOT NULL, name text NOT NULL, description text NOT NULL DEFAULT '', created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), UNIQUE (project_id, key) ); CREATE TABLE knowledge_base_sources ( knowledge_base_id uuid NOT NULL REFERENCES knowledge_bases(id) ON DELETE CASCADE, knowledge_source_id uuid NOT NULL REFERENCES knowledge_sources(id) ON DELETE CASCADE, created_at timestamptz NOT NULL DEFAULT now(), PRIMARY KEY (knowledge_base_id, knowledge_source_id) ); CREATE TABLE artifacts ( id uuid PRIMARY KEY, project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE, kind text NOT NULL, uri text NOT NULL, content_hash text NOT NULL, size_bytes bigint NOT NULL CHECK (size_bytes >= 0), media_type text NOT NULL DEFAULT 'application/octet-stream', schema_version text NOT NULL DEFAULT 'v1', metadata jsonb NOT NULL DEFAULT '{}'::jsonb, status text NOT NULL DEFAULT 'ready' CHECK (status IN ('pending', 'ready', 'failed', 'deleted')), created_at timestamptz NOT NULL DEFAULT now(), UNIQUE (project_id, content_hash, kind) ); CREATE TABLE artifact_lineage ( parent_artifact_id uuid NOT NULL REFERENCES artifacts(id) ON DELETE CASCADE, child_artifact_id uuid NOT NULL REFERENCES artifacts(id) ON DELETE CASCADE, relation text NOT NULL, created_at timestamptz NOT NULL DEFAULT now(), PRIMARY KEY (parent_artifact_id, child_artifact_id, relation), CHECK (parent_artifact_id <> child_artifact_id) ); CREATE TABLE datasets ( id uuid PRIMARY KEY, project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE, key text NOT NULL, name text NOT NULL, purpose text NOT NULL CHECK (purpose IN ( 'raw_snapshot', 'normalized', 'retrieval', 'evaluation', 'training_candidate', 'training', 'validation', 'holdout' )), created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), UNIQUE (project_id, key) ); CREATE TABLE workflow_definitions ( id uuid PRIMARY KEY, project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE, key text NOT NULL, name text NOT NULL, kind text NOT NULL, created_at timestamptz NOT NULL DEFAULT now(), UNIQUE (project_id, key) ); CREATE TABLE workflow_versions ( id uuid PRIMARY KEY, workflow_definition_id uuid NOT NULL REFERENCES workflow_definitions(id) ON DELETE CASCADE, version integer NOT NULL CHECK (version > 0), spec jsonb NOT NULL, created_at timestamptz NOT NULL DEFAULT now(), UNIQUE (workflow_definition_id, version) ); CREATE TABLE schedules ( id uuid PRIMARY KEY, project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE, workflow_version_id uuid NOT NULL REFERENCES workflow_versions(id), name text NOT NULL, cron_expression text NOT NULL, timezone text NOT NULL DEFAULT 'UTC', enabled boolean NOT NULL DEFAULT true, input jsonb NOT NULL DEFAULT '{}'::jsonb, next_run_at timestamptz, last_run_at timestamptz, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), UNIQUE (project_id, name) ); CREATE TABLE workflow_runs ( id uuid PRIMARY KEY, project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE, workflow_version_id uuid NOT NULL REFERENCES workflow_versions(id), status text NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'running', 'succeeded', 'failed', 'cancelled')), trigger_type text NOT NULL, input jsonb NOT NULL DEFAULT '{}'::jsonb, output jsonb NOT NULL DEFAULT '{}'::jsonb, error jsonb NOT NULL DEFAULT '{}'::jsonb, idempotency_key text, started_at timestamptz, finished_at timestamptz, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now() ); CREATE UNIQUE INDEX workflow_runs_idempotency_idx ON workflow_runs (project_id, idempotency_key) WHERE idempotency_key IS NOT NULL; CREATE TABLE step_runs ( id uuid PRIMARY KEY, project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE, workflow_run_id uuid NOT NULL REFERENCES workflow_runs(id) ON DELETE CASCADE, step_key text NOT NULL, executor_kind text NOT NULL, status text NOT NULL DEFAULT 'pending' CHECK (status IN ( 'pending', 'leased', 'running', 'succeeded', 'retryable_failed', 'terminal_failed', 'cancelled' )), attempt integer NOT NULL DEFAULT 0 CHECK (attempt >= 0), max_attempts integer NOT NULL DEFAULT 3 CHECK (max_attempts > 0), priority integer NOT NULL DEFAULT 0, input jsonb NOT NULL DEFAULT '{}'::jsonb, output jsonb NOT NULL DEFAULT '{}'::jsonb, error jsonb NOT NULL DEFAULT '{}'::jsonb, idempotency_key text NOT NULL, available_at timestamptz NOT NULL DEFAULT now(), leased_by text, leased_until timestamptz, started_at timestamptz, finished_at timestamptz, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), UNIQUE (project_id, idempotency_key) ); CREATE INDEX step_runs_lease_idx ON step_runs (executor_kind, status, available_at, priority DESC, created_at) WHERE status IN ('pending', 'retryable_failed'); CREATE INDEX step_runs_workflow_idx ON step_runs (workflow_run_id, created_at); CREATE TABLE dataset_versions ( id uuid PRIMARY KEY, dataset_id uuid NOT NULL REFERENCES datasets(id) ON DELETE CASCADE, version integer NOT NULL CHECK (version > 0), artifact_id uuid NOT NULL REFERENCES artifacts(id), source_revision text, recipe jsonb NOT NULL DEFAULT '{}'::jsonb, stats jsonb NOT NULL DEFAULT '{}'::jsonb, status text NOT NULL DEFAULT 'candidate' CHECK (status IN ('candidate', 'active', 'retired', 'failed')), created_by_step_run_id uuid REFERENCES step_runs(id), created_at timestamptz NOT NULL DEFAULT now(), UNIQUE (dataset_id, version) ); CREATE TABLE evaluation_suites ( id uuid PRIMARY KEY, project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE, key text NOT NULL, name text NOT NULL, description text NOT NULL DEFAULT '', created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), UNIQUE (project_id, key) ); CREATE TABLE evaluation_cases ( id uuid PRIMARY KEY, evaluation_suite_id uuid NOT NULL REFERENCES evaluation_suites(id) ON DELETE CASCADE, case_key text NOT NULL, input jsonb NOT NULL, expectations jsonb NOT NULL DEFAULT '{}'::jsonb, tags text[] NOT NULL DEFAULT ARRAY[]::text[], enabled boolean NOT NULL DEFAULT true, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), UNIQUE (evaluation_suite_id, case_key) ); CREATE TABLE evaluation_runs ( id uuid PRIMARY KEY, project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE, evaluation_suite_id uuid NOT NULL REFERENCES evaluation_suites(id), subject_type text NOT NULL, subject_id uuid NOT NULL, baseline_subject_id uuid, workflow_run_id uuid REFERENCES workflow_runs(id), status text NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'running', 'succeeded', 'failed', 'cancelled')), metrics jsonb NOT NULL DEFAULT '{}'::jsonb, passed boolean, started_at timestamptz, finished_at timestamptz, created_at timestamptz NOT NULL DEFAULT now() ); CREATE TABLE evaluation_results ( id uuid PRIMARY KEY, evaluation_run_id uuid NOT NULL REFERENCES evaluation_runs(id) ON DELETE CASCADE, evaluation_case_id uuid REFERENCES evaluation_cases(id) ON DELETE CASCADE, metric_key text NOT NULL, score double precision, passed boolean, details jsonb NOT NULL DEFAULT '{}'::jsonb, created_at timestamptz NOT NULL DEFAULT now() ); CREATE INDEX evaluation_results_run_idx ON evaluation_results (evaluation_run_id); CREATE TABLE releases ( id uuid PRIMARY KEY, project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE, knowledge_base_id uuid NOT NULL REFERENCES knowledge_bases(id) ON DELETE CASCADE, alias text NOT NULL DEFAULT 'production', version integer NOT NULL CHECK (version > 0), status text NOT NULL DEFAULT 'candidate' CHECK (status IN ('candidate', 'active', 'retired', 'failed')), dataset_version_id uuid NOT NULL REFERENCES dataset_versions(id), index_artifact_id uuid REFERENCES artifacts(id), retrieval_policy jsonb NOT NULL DEFAULT '{}'::jsonb, generation_policy jsonb NOT NULL DEFAULT '{}'::jsonb, evaluation_run_id uuid REFERENCES evaluation_runs(id), created_by_workflow_run_id uuid REFERENCES workflow_runs(id), promoted_at timestamptz, created_at timestamptz NOT NULL DEFAULT now(), UNIQUE (knowledge_base_id, alias, version) ); CREATE UNIQUE INDEX releases_active_alias_idx ON releases (knowledge_base_id, alias) WHERE status = 'active'; CREATE TABLE rag_traces ( id uuid PRIMARY KEY, project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE, knowledge_base_id uuid NOT NULL REFERENCES knowledge_bases(id), release_id uuid NOT NULL REFERENCES releases(id), request_kind text NOT NULL CHECK (request_kind IN ('retrieve', 'answer')), trace_id uuid NOT NULL UNIQUE, query_hash text NOT NULL, query_text text, response_text text, status text NOT NULL CHECK (status IN ('succeeded', 'failed')), latency_ms bigint NOT NULL CHECK (latency_ms >= 0), expires_at timestamptz NOT NULL, created_at timestamptz NOT NULL DEFAULT now() ); CREATE INDEX rag_traces_project_created_idx ON rag_traces (project_id, created_at DESC); CREATE INDEX rag_traces_expiry_idx ON rag_traces (expires_at); CREATE TABLE rag_trace_evidence ( id uuid PRIMARY KEY, rag_trace_id uuid NOT NULL REFERENCES rag_traces(id) ON DELETE CASCADE, rank integer NOT NULL CHECK (rank > 0), artifact_id uuid REFERENCES artifacts(id), external_ref text, score double precision, citation jsonb NOT NULL DEFAULT '{}'::jsonb, metadata jsonb NOT NULL DEFAULT '{}'::jsonb, created_at timestamptz NOT NULL DEFAULT now(), UNIQUE (rag_trace_id, rank) ); CREATE INDEX integration_instances_project_kind_idx ON integration_instances (project_id, kind, enabled); CREATE INDEX knowledge_sources_project_idx ON knowledge_sources (project_id); CREATE INDEX knowledge_bases_project_idx ON knowledge_bases (project_id); CREATE INDEX artifacts_project_kind_idx ON artifacts (project_id, kind, created_at DESC); CREATE INDEX datasets_project_idx ON datasets (project_id); CREATE INDEX workflow_runs_project_status_idx ON workflow_runs (project_id, status, created_at DESC); CREATE INDEX releases_project_idx ON releases (project_id, created_at DESC); -- +goose Down DROP TABLE rag_trace_evidence; DROP TABLE rag_traces; DROP TABLE releases; DROP TABLE evaluation_results; DROP TABLE evaluation_runs; DROP TABLE evaluation_cases; DROP TABLE evaluation_suites; DROP TABLE dataset_versions; DROP TABLE step_runs; DROP TABLE workflow_runs; DROP TABLE schedules; DROP TABLE workflow_versions; DROP TABLE workflow_definitions; DROP TABLE datasets; DROP TABLE artifact_lineage; DROP TABLE artifacts; DROP TABLE knowledge_base_sources; DROP TABLE knowledge_bases; DROP TABLE knowledge_sources; DROP TABLE integration_instances; DROP TABLE projects;