Some checks failed
verify / verify (push) Has been cancelled
The Stage 1 foundation now proves provider contracts with mocks and validates PostgreSQL/pgvector ingestion, approval binding, retrieval, reranking, and idempotency using only synthetic data. Live Bailian validation remains gated on rotating the exposed key. Constraint: The key shown in chat is compromised and cannot be used or committed Rejected: Mark Stage 1 complete from mock and offline results | real three-model smoke is still required Confidence: high Scope-risk: moderate Reversibility: clean Directive: Do not enable real-data ingestion until Stage 3 cloud approval and outbound manifest controls are enforced end to end Tested: make verify; 41 pytest tests; strict mypy; Ruff; Compose config; pinned image build; empty-volume migration; role denial; two idempotent 20-vector seeds; database restart persistence Not-tested: Live Bailian calls require a newly rotated key; React product UI is not implemented
1016 lines
36 KiB
Python
1016 lines
36 KiB
Python
"""Create the initial RAG schema and vector index.
|
|
|
|
Revision ID: 0001_initial_schema
|
|
Revises: None
|
|
Create Date: 2026-07-12
|
|
"""
|
|
|
|
from collections.abc import Sequence
|
|
|
|
from alembic import op
|
|
|
|
revision: str = "0001_initial_schema"
|
|
down_revision: str | None = None
|
|
branch_labels: str | Sequence[str] | None = None
|
|
depends_on: str | Sequence[str] | None = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
op.execute(
|
|
"""
|
|
DO $migration$
|
|
BEGIN
|
|
IF NOT EXISTS (
|
|
SELECT 1 FROM pg_extension WHERE extname = 'vector'
|
|
) THEN
|
|
RAISE EXCEPTION
|
|
'pgvector extension is required; run the PostgreSQL bootstrap first';
|
|
END IF;
|
|
|
|
IF NOT EXISTS (
|
|
SELECT 1 FROM pg_namespace WHERE nspname = 'rag'
|
|
) THEN
|
|
RAISE EXCEPTION
|
|
'rag schema is required; run the PostgreSQL bootstrap first';
|
|
END IF;
|
|
END
|
|
$migration$;
|
|
"""
|
|
)
|
|
|
|
op.execute(
|
|
"""
|
|
CREATE TABLE rag.knowledge_bases (
|
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
name text NOT NULL,
|
|
description text,
|
|
created_at timestamptz NOT NULL DEFAULT now(),
|
|
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
CONSTRAINT knowledge_bases_name_nonempty
|
|
CHECK (btrim(name) <> '')
|
|
);
|
|
"""
|
|
)
|
|
|
|
op.execute(
|
|
"""
|
|
CREATE TABLE rag.access_scopes (
|
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
knowledge_base_id uuid NOT NULL,
|
|
name text NOT NULL,
|
|
created_at timestamptz NOT NULL DEFAULT now(),
|
|
CONSTRAINT access_scopes_knowledge_base_fk
|
|
FOREIGN KEY (knowledge_base_id)
|
|
REFERENCES rag.knowledge_bases (id)
|
|
ON DELETE CASCADE,
|
|
CONSTRAINT access_scopes_name_nonempty
|
|
CHECK (btrim(name) <> ''),
|
|
CONSTRAINT access_scopes_knowledge_base_name_key
|
|
UNIQUE (knowledge_base_id, name),
|
|
CONSTRAINT access_scopes_knowledge_base_id_key
|
|
UNIQUE (knowledge_base_id, id)
|
|
);
|
|
"""
|
|
)
|
|
|
|
op.execute(
|
|
"""
|
|
CREATE TABLE rag.documents (
|
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
knowledge_base_id uuid NOT NULL,
|
|
access_scope_id uuid NOT NULL,
|
|
raw_sha256 char(64) NOT NULL,
|
|
filename text NOT NULL,
|
|
storage_key text NOT NULL,
|
|
mime_type text NOT NULL,
|
|
status text NOT NULL DEFAULT 'UPLOADED',
|
|
active_version_id uuid,
|
|
deleted_at timestamptz,
|
|
created_at timestamptz NOT NULL DEFAULT now(),
|
|
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
CONSTRAINT documents_knowledge_base_fk
|
|
FOREIGN KEY (knowledge_base_id)
|
|
REFERENCES rag.knowledge_bases (id)
|
|
ON DELETE CASCADE,
|
|
CONSTRAINT documents_access_scope_fk
|
|
FOREIGN KEY (knowledge_base_id, access_scope_id)
|
|
REFERENCES rag.access_scopes (knowledge_base_id, id)
|
|
ON DELETE RESTRICT,
|
|
CONSTRAINT documents_hash_format
|
|
CHECK (raw_sha256 ~ '^[0-9a-f]{64}$'),
|
|
CONSTRAINT documents_filename_nonempty
|
|
CHECK (btrim(filename) <> ''),
|
|
CONSTRAINT documents_storage_key_nonempty
|
|
CHECK (btrim(storage_key) <> ''),
|
|
CONSTRAINT documents_mime_type_nonempty
|
|
CHECK (btrim(mime_type) <> ''),
|
|
CONSTRAINT documents_status_valid
|
|
CHECK (status IN (
|
|
'UPLOADED', 'QUARANTINED_LOCAL_REVIEW', 'LOCAL_PARSING',
|
|
'LOCAL_OCR_REQUIRED', 'LOCAL_OCR_RUNNING', 'LOCAL_NORMALIZING',
|
|
'LOCAL_CHUNKING_AND_REDACTION',
|
|
'LOCAL_PARSED_PENDING_CLOUD_REVIEW', 'CLOUD_APPROVED',
|
|
'REJECTED', 'EMBEDDING', 'INDEXING', 'VALIDATING',
|
|
'READY', 'FAILED', 'DELETED'
|
|
)),
|
|
CONSTRAINT documents_deleted_state_consistent
|
|
CHECK (
|
|
(status = 'DELETED' AND deleted_at IS NOT NULL AND active_version_id IS NULL)
|
|
OR (status <> 'DELETED' AND deleted_at IS NULL)
|
|
),
|
|
CONSTRAINT documents_ready_requires_active_version
|
|
CHECK (status <> 'READY' OR active_version_id IS NOT NULL),
|
|
CONSTRAINT documents_knowledge_base_hash_key
|
|
UNIQUE (knowledge_base_id, raw_sha256),
|
|
CONSTRAINT documents_knowledge_base_id_key
|
|
UNIQUE (knowledge_base_id, id)
|
|
);
|
|
"""
|
|
)
|
|
|
|
op.execute(
|
|
"""
|
|
CREATE TABLE rag.document_versions (
|
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
document_id uuid NOT NULL,
|
|
parser_profile_hash char(64) NOT NULL,
|
|
ocr_profile_hash char(64),
|
|
normalization_profile_hash char(64) NOT NULL,
|
|
chunk_profile_hash char(64) NOT NULL,
|
|
cloud_policy_id text NOT NULL,
|
|
outbound_manifest_sha256 char(64),
|
|
review_state text NOT NULL DEFAULT 'QUARANTINED_LOCAL_REVIEW',
|
|
cloud_approved_at timestamptz,
|
|
cloud_approved_by text,
|
|
embedding_profile_hash char(64),
|
|
status text NOT NULL DEFAULT 'PENDING',
|
|
expected_chunk_count integer,
|
|
error_code text,
|
|
created_at timestamptz NOT NULL DEFAULT now(),
|
|
completed_at timestamptz,
|
|
CONSTRAINT document_versions_document_fk
|
|
FOREIGN KEY (document_id)
|
|
REFERENCES rag.documents (id)
|
|
ON DELETE CASCADE,
|
|
CONSTRAINT document_versions_parser_hash_format
|
|
CHECK (parser_profile_hash ~ '^[0-9a-f]{64}$'),
|
|
CONSTRAINT document_versions_ocr_hash_format
|
|
CHECK (ocr_profile_hash IS NULL OR ocr_profile_hash ~ '^[0-9a-f]{64}$'),
|
|
CONSTRAINT document_versions_normalization_hash_format
|
|
CHECK (normalization_profile_hash ~ '^[0-9a-f]{64}$'),
|
|
CONSTRAINT document_versions_chunk_hash_format
|
|
CHECK (chunk_profile_hash ~ '^[0-9a-f]{64}$'),
|
|
CONSTRAINT document_versions_embedding_hash_format
|
|
CHECK (
|
|
embedding_profile_hash IS NULL
|
|
OR embedding_profile_hash ~ '^[0-9a-f]{64}$'
|
|
),
|
|
CONSTRAINT document_versions_cloud_policy_nonempty
|
|
CHECK (btrim(cloud_policy_id) <> ''),
|
|
CONSTRAINT document_versions_manifest_hash_format
|
|
CHECK (
|
|
outbound_manifest_sha256 IS NULL
|
|
OR outbound_manifest_sha256 ~ '^[0-9a-f]{64}$'
|
|
),
|
|
CONSTRAINT document_versions_review_state_valid
|
|
CHECK (review_state IN (
|
|
'QUARANTINED_LOCAL_REVIEW',
|
|
'LOCAL_PARSED_PENDING_CLOUD_REVIEW',
|
|
'CLOUD_APPROVED',
|
|
'REJECTED'
|
|
)),
|
|
CONSTRAINT document_versions_cloud_approval_bound
|
|
CHECK (
|
|
(
|
|
review_state = 'CLOUD_APPROVED'
|
|
AND outbound_manifest_sha256 IS NOT NULL
|
|
AND embedding_profile_hash IS NOT NULL
|
|
AND cloud_approved_at IS NOT NULL
|
|
AND cloud_approved_by IS NOT NULL
|
|
AND btrim(cloud_approved_by) <> ''
|
|
)
|
|
OR (
|
|
review_state <> 'CLOUD_APPROVED'
|
|
AND cloud_approved_at IS NULL
|
|
AND cloud_approved_by IS NULL
|
|
)
|
|
),
|
|
CONSTRAINT document_versions_status_valid
|
|
CHECK (status IN ('PENDING', 'PROCESSING', 'READY', 'FAILED')),
|
|
CONSTRAINT document_versions_chunk_count_valid
|
|
CHECK (expected_chunk_count IS NULL OR expected_chunk_count >= 0),
|
|
CONSTRAINT document_versions_completion_consistent
|
|
CHECK (
|
|
(status IN ('READY', 'FAILED') AND completed_at IS NOT NULL)
|
|
OR (status IN ('PENDING', 'PROCESSING') AND completed_at IS NULL)
|
|
),
|
|
CONSTRAINT document_versions_document_id_key
|
|
UNIQUE (document_id, id),
|
|
CONSTRAINT document_versions_approval_binding_key
|
|
UNIQUE (
|
|
id,
|
|
review_state,
|
|
outbound_manifest_sha256,
|
|
embedding_profile_hash
|
|
)
|
|
);
|
|
"""
|
|
)
|
|
|
|
op.execute(
|
|
"""
|
|
ALTER TABLE rag.documents
|
|
ADD CONSTRAINT documents_active_version_fk
|
|
FOREIGN KEY (id, active_version_id)
|
|
REFERENCES rag.document_versions (document_id, id)
|
|
DEFERRABLE INITIALLY DEFERRED;
|
|
"""
|
|
)
|
|
|
|
op.execute(
|
|
"""
|
|
CREATE TABLE rag.outbound_manifest_items (
|
|
document_version_id uuid NOT NULL,
|
|
ordinal integer NOT NULL,
|
|
outbound_manifest_sha256 char(64) NOT NULL,
|
|
cloud_text_sha256 char(64) NOT NULL,
|
|
embedding_text_sha256 char(64) NOT NULL,
|
|
created_at timestamptz NOT NULL DEFAULT now(),
|
|
CONSTRAINT outbound_manifest_items_version_fk
|
|
FOREIGN KEY (document_version_id)
|
|
REFERENCES rag.document_versions (id)
|
|
ON DELETE CASCADE,
|
|
CONSTRAINT outbound_manifest_items_ordinal_valid
|
|
CHECK (ordinal >= 0),
|
|
CONSTRAINT outbound_manifest_items_hashes_valid
|
|
CHECK (
|
|
outbound_manifest_sha256 ~ '^[0-9a-f]{64}$'
|
|
AND cloud_text_sha256 ~ '^[0-9a-f]{64}$'
|
|
AND embedding_text_sha256 ~ '^[0-9a-f]{64}$'
|
|
),
|
|
CONSTRAINT outbound_manifest_items_primary_key
|
|
PRIMARY KEY (document_version_id, ordinal),
|
|
CONSTRAINT outbound_manifest_items_chunk_binding_key
|
|
UNIQUE (
|
|
document_version_id,
|
|
ordinal,
|
|
outbound_manifest_sha256,
|
|
cloud_text_sha256,
|
|
embedding_text_sha256
|
|
)
|
|
);
|
|
"""
|
|
)
|
|
|
|
op.execute(
|
|
"""
|
|
CREATE FUNCTION rag.guard_approved_manifest_item_mutation()
|
|
RETURNS trigger
|
|
LANGUAGE plpgsql
|
|
SECURITY INVOKER
|
|
SET search_path = pg_catalog, rag
|
|
AS $function$
|
|
BEGIN
|
|
IF (
|
|
TG_OP = 'INSERT'
|
|
AND EXISTS (
|
|
SELECT 1 FROM rag.document_versions AS version
|
|
WHERE version.id = NEW.document_version_id
|
|
AND version.review_state = 'CLOUD_APPROVED'
|
|
)
|
|
) OR (
|
|
TG_OP = 'DELETE'
|
|
AND EXISTS (
|
|
SELECT 1 FROM rag.document_versions AS version
|
|
WHERE version.id = OLD.document_version_id
|
|
AND version.review_state = 'CLOUD_APPROVED'
|
|
)
|
|
) OR (
|
|
TG_OP = 'UPDATE'
|
|
AND EXISTS (
|
|
SELECT 1 FROM rag.document_versions AS version
|
|
WHERE version.id IN (OLD.document_version_id, NEW.document_version_id)
|
|
AND version.review_state = 'CLOUD_APPROVED'
|
|
)
|
|
) THEN
|
|
RAISE EXCEPTION
|
|
'approved outbound manifest items are immutable; revoke approval first'
|
|
USING ERRCODE = '23514';
|
|
END IF;
|
|
IF TG_OP = 'DELETE' THEN
|
|
RETURN OLD;
|
|
END IF;
|
|
RETURN NEW;
|
|
END
|
|
$function$;
|
|
"""
|
|
)
|
|
op.execute(
|
|
"""
|
|
CREATE TRIGGER outbound_manifest_items_immutable_after_approval
|
|
BEFORE INSERT OR UPDATE OR DELETE
|
|
ON rag.outbound_manifest_items
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION rag.guard_approved_manifest_item_mutation();
|
|
"""
|
|
)
|
|
|
|
op.execute(
|
|
"""
|
|
CREATE TABLE rag.chunks (
|
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
knowledge_base_id uuid NOT NULL,
|
|
document_id uuid NOT NULL,
|
|
document_version_id uuid NOT NULL,
|
|
access_scope_id uuid NOT NULL,
|
|
ordinal integer NOT NULL,
|
|
display_text text NOT NULL,
|
|
cloud_text text NOT NULL,
|
|
cloud_text_sha256 char(64) NOT NULL,
|
|
embedding_prefix text NOT NULL,
|
|
embedding_text text NOT NULL,
|
|
embedding_text_sha256 char(64) NOT NULL,
|
|
embedded_text_sha256 char(64),
|
|
embedding_profile_hash char(64),
|
|
outbound_manifest_sha256 char(64),
|
|
token_count integer NOT NULL,
|
|
page_start integer,
|
|
page_end integer,
|
|
section_path jsonb NOT NULL DEFAULT '[]'::jsonb,
|
|
metadata jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
embedding_model text NOT NULL,
|
|
embedding_dimension smallint NOT NULL DEFAULT 1024,
|
|
embedding vector(1024),
|
|
approval_status text NOT NULL DEFAULT 'QUARANTINED_LOCAL_REVIEW',
|
|
index_status text NOT NULL DEFAULT 'PENDING',
|
|
searchable boolean NOT NULL DEFAULT false,
|
|
deleted_at timestamptz,
|
|
created_at timestamptz NOT NULL DEFAULT now(),
|
|
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
CONSTRAINT chunks_document_fk
|
|
FOREIGN KEY (knowledge_base_id, document_id)
|
|
REFERENCES rag.documents (knowledge_base_id, id)
|
|
ON DELETE CASCADE,
|
|
CONSTRAINT chunks_document_version_fk
|
|
FOREIGN KEY (document_id, document_version_id)
|
|
REFERENCES rag.document_versions (document_id, id)
|
|
ON DELETE CASCADE,
|
|
CONSTRAINT chunks_access_scope_fk
|
|
FOREIGN KEY (knowledge_base_id, access_scope_id)
|
|
REFERENCES rag.access_scopes (knowledge_base_id, id)
|
|
ON DELETE RESTRICT,
|
|
CONSTRAINT chunks_approval_binding_fk
|
|
FOREIGN KEY (
|
|
document_version_id,
|
|
approval_status,
|
|
outbound_manifest_sha256,
|
|
embedding_profile_hash
|
|
)
|
|
REFERENCES rag.document_versions (
|
|
id,
|
|
review_state,
|
|
outbound_manifest_sha256,
|
|
embedding_profile_hash
|
|
)
|
|
DEFERRABLE INITIALLY DEFERRED,
|
|
CONSTRAINT chunks_manifest_item_binding_fk
|
|
FOREIGN KEY (
|
|
document_version_id,
|
|
ordinal,
|
|
outbound_manifest_sha256,
|
|
cloud_text_sha256,
|
|
embedding_text_sha256
|
|
)
|
|
REFERENCES rag.outbound_manifest_items (
|
|
document_version_id,
|
|
ordinal,
|
|
outbound_manifest_sha256,
|
|
cloud_text_sha256,
|
|
embedding_text_sha256
|
|
)
|
|
DEFERRABLE INITIALLY DEFERRED,
|
|
CONSTRAINT chunks_ordinal_valid
|
|
CHECK (ordinal >= 0),
|
|
CONSTRAINT chunks_text_nonempty
|
|
CHECK (
|
|
btrim(display_text) <> ''
|
|
AND btrim(cloud_text) <> ''
|
|
AND btrim(embedding_prefix) <> ''
|
|
AND btrim(embedding_text) <> ''
|
|
),
|
|
CONSTRAINT chunks_cloud_text_hash_matches
|
|
CHECK (
|
|
cloud_text_sha256 = encode(
|
|
sha256(convert_to(cloud_text, 'UTF8')),
|
|
'hex'
|
|
)
|
|
),
|
|
CONSTRAINT chunks_embedding_input_bound_to_cloud_text
|
|
CHECK (embedding_text = embedding_prefix || cloud_text),
|
|
CONSTRAINT chunks_embedding_text_hash_matches
|
|
CHECK (
|
|
embedding_text_sha256 = encode(
|
|
sha256(convert_to(embedding_text, 'UTF8')),
|
|
'hex'
|
|
)
|
|
),
|
|
CONSTRAINT chunks_embedded_text_hash_format
|
|
CHECK (
|
|
embedded_text_sha256 IS NULL
|
|
OR embedded_text_sha256 ~ '^[0-9a-f]{64}$'
|
|
),
|
|
CONSTRAINT chunks_embedding_profile_hash_format
|
|
CHECK (
|
|
embedding_profile_hash IS NULL
|
|
OR embedding_profile_hash ~ '^[0-9a-f]{64}$'
|
|
),
|
|
CONSTRAINT chunks_manifest_hash_format
|
|
CHECK (
|
|
outbound_manifest_sha256 IS NULL
|
|
OR outbound_manifest_sha256 ~ '^[0-9a-f]{64}$'
|
|
),
|
|
CONSTRAINT chunks_token_count_valid
|
|
CHECK (token_count > 0),
|
|
CONSTRAINT chunks_pages_valid
|
|
CHECK (
|
|
(page_start IS NULL AND page_end IS NULL)
|
|
OR (
|
|
page_start IS NOT NULL
|
|
AND page_end IS NOT NULL
|
|
AND page_start > 0
|
|
AND page_end >= page_start
|
|
)
|
|
),
|
|
CONSTRAINT chunks_section_path_array
|
|
CHECK (jsonb_typeof(section_path) = 'array'),
|
|
CONSTRAINT chunks_metadata_object
|
|
CHECK (jsonb_typeof(metadata) = 'object'),
|
|
CONSTRAINT chunks_embedding_model_nonempty
|
|
CHECK (btrim(embedding_model) <> ''),
|
|
CONSTRAINT chunks_embedding_dimension_1024
|
|
CHECK (embedding_dimension = 1024),
|
|
CONSTRAINT chunks_vector_dimension_1024
|
|
CHECK (embedding IS NULL OR vector_dims(embedding) = 1024),
|
|
CONSTRAINT chunks_approval_status_valid
|
|
CHECK (approval_status IN (
|
|
'QUARANTINED_LOCAL_REVIEW',
|
|
'LOCAL_PARSED_PENDING_CLOUD_REVIEW',
|
|
'CLOUD_APPROVED',
|
|
'REJECTED'
|
|
)),
|
|
CONSTRAINT chunks_cloud_approval_has_complete_binding
|
|
CHECK (
|
|
approval_status <> 'CLOUD_APPROVED'
|
|
OR (
|
|
outbound_manifest_sha256 IS NOT NULL
|
|
AND embedding_profile_hash IS NOT NULL
|
|
)
|
|
),
|
|
CONSTRAINT chunks_index_status_valid
|
|
CHECK (index_status IN ('PENDING', 'EMBEDDING', 'READY', 'FAILED')),
|
|
CONSTRAINT chunks_ready_index_has_current_embedding
|
|
CHECK (
|
|
index_status <> 'READY'
|
|
OR (
|
|
embedding IS NOT NULL
|
|
AND embedded_text_sha256 IS NOT NULL
|
|
AND embedded_text_sha256 = embedding_text_sha256
|
|
AND embedding_profile_hash IS NOT NULL
|
|
)
|
|
),
|
|
CONSTRAINT chunks_searchable_requires_ready_approved_embedding
|
|
CHECK (
|
|
NOT searchable
|
|
OR (
|
|
embedding IS NOT NULL
|
|
AND approval_status = 'CLOUD_APPROVED'
|
|
AND index_status = 'READY'
|
|
AND embedded_text_sha256 IS NOT NULL
|
|
AND embedded_text_sha256 = embedding_text_sha256
|
|
AND embedding_profile_hash IS NOT NULL
|
|
AND outbound_manifest_sha256 IS NOT NULL
|
|
AND deleted_at IS NULL
|
|
)
|
|
),
|
|
CONSTRAINT chunks_document_version_ordinal_key
|
|
UNIQUE (document_version_id, ordinal)
|
|
);
|
|
"""
|
|
)
|
|
|
|
op.execute(
|
|
"""
|
|
COMMENT ON COLUMN rag.chunks.display_text IS
|
|
'Local-only normalized source text; never implied safe for cloud egress.';
|
|
"""
|
|
)
|
|
op.execute(
|
|
"""
|
|
COMMENT ON COLUMN rag.chunks.cloud_text IS
|
|
'Redacted outbound text covered by the document-version approval manifest.';
|
|
"""
|
|
)
|
|
op.execute(
|
|
"""
|
|
COMMENT ON COLUMN rag.chunks.embedding_prefix IS
|
|
'Provider document prefix selected by the approved embedding profile.';
|
|
"""
|
|
)
|
|
op.execute(
|
|
"""
|
|
COMMENT ON COLUMN rag.chunks.embedding_text IS
|
|
'Exact provider input, database-enforced as embedding_prefix || cloud_text.';
|
|
"""
|
|
)
|
|
|
|
op.execute(
|
|
"""
|
|
CREATE INDEX chunks_embedding_hnsw
|
|
ON rag.chunks
|
|
USING hnsw (embedding vector_cosine_ops)
|
|
WHERE searchable;
|
|
"""
|
|
)
|
|
op.execute(
|
|
"""
|
|
CREATE INDEX chunks_access_filter
|
|
ON rag.chunks (knowledge_base_id, access_scope_id)
|
|
WHERE searchable;
|
|
"""
|
|
)
|
|
op.execute(
|
|
"""
|
|
CREATE INDEX chunks_document_lookup
|
|
ON rag.chunks (document_id, document_version_id);
|
|
"""
|
|
)
|
|
|
|
op.execute(
|
|
"""
|
|
CREATE FUNCTION rag.guard_approved_chunk_mutation()
|
|
RETURNS trigger
|
|
LANGUAGE plpgsql
|
|
SECURITY INVOKER
|
|
SET search_path = pg_catalog, rag
|
|
AS $function$
|
|
BEGIN
|
|
IF OLD.approval_status = 'CLOUD_APPROVED'
|
|
AND NEW.approval_status = 'CLOUD_APPROVED'
|
|
AND (
|
|
NEW.knowledge_base_id IS DISTINCT FROM OLD.knowledge_base_id
|
|
OR NEW.document_id IS DISTINCT FROM OLD.document_id
|
|
OR NEW.document_version_id IS DISTINCT FROM OLD.document_version_id
|
|
OR NEW.access_scope_id IS DISTINCT FROM OLD.access_scope_id
|
|
OR NEW.ordinal IS DISTINCT FROM OLD.ordinal
|
|
OR NEW.cloud_text IS DISTINCT FROM OLD.cloud_text
|
|
OR NEW.cloud_text_sha256 IS DISTINCT FROM OLD.cloud_text_sha256
|
|
OR NEW.embedding_prefix IS DISTINCT FROM OLD.embedding_prefix
|
|
OR NEW.embedding_text IS DISTINCT FROM OLD.embedding_text
|
|
OR NEW.embedding_text_sha256 IS DISTINCT FROM OLD.embedding_text_sha256
|
|
OR NEW.outbound_manifest_sha256
|
|
IS DISTINCT FROM OLD.outbound_manifest_sha256
|
|
OR NEW.section_path IS DISTINCT FROM OLD.section_path
|
|
OR NEW.metadata IS DISTINCT FROM OLD.metadata
|
|
OR NEW.page_start IS DISTINCT FROM OLD.page_start
|
|
OR NEW.page_end IS DISTINCT FROM OLD.page_end
|
|
) THEN
|
|
RAISE EXCEPTION
|
|
'approved outbound chunk fields are immutable; revoke version approval first'
|
|
USING ERRCODE = '23514';
|
|
END IF;
|
|
|
|
IF OLD.index_status = 'READY'
|
|
AND NEW.index_status = 'READY'
|
|
AND (
|
|
NEW.embedded_text_sha256 IS DISTINCT FROM OLD.embedded_text_sha256
|
|
OR NEW.embedding_profile_hash IS DISTINCT FROM OLD.embedding_profile_hash
|
|
) THEN
|
|
RAISE EXCEPTION
|
|
'ready embedding is immutable; leave READY and searchable state before replacing it'
|
|
USING ERRCODE = '23514';
|
|
END IF;
|
|
RETURN NEW;
|
|
END
|
|
$function$;
|
|
"""
|
|
)
|
|
|
|
op.execute(
|
|
"""
|
|
CREATE FUNCTION rag.guard_ready_embedding_update()
|
|
RETURNS trigger
|
|
LANGUAGE plpgsql
|
|
SECURITY INVOKER
|
|
SET search_path = pg_catalog, rag
|
|
AS $function$
|
|
BEGIN
|
|
IF OLD.index_status = 'READY' AND NEW.index_status = 'READY' THEN
|
|
RAISE EXCEPTION
|
|
'ready vector is immutable; set index_status to EMBEDDING before replacement'
|
|
USING ERRCODE = '23514';
|
|
END IF;
|
|
RETURN NEW;
|
|
END
|
|
$function$;
|
|
"""
|
|
)
|
|
op.execute(
|
|
"""
|
|
CREATE TRIGGER chunks_guard_ready_vector_update
|
|
BEFORE UPDATE OF embedding
|
|
ON rag.chunks
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION rag.guard_ready_embedding_update();
|
|
"""
|
|
)
|
|
op.execute(
|
|
"""
|
|
CREATE TRIGGER chunks_guard_approved_mutation
|
|
BEFORE UPDATE OF
|
|
knowledge_base_id,
|
|
document_id,
|
|
document_version_id,
|
|
access_scope_id,
|
|
ordinal,
|
|
cloud_text,
|
|
cloud_text_sha256,
|
|
embedding_prefix,
|
|
embedding_text,
|
|
embedding_text_sha256,
|
|
outbound_manifest_sha256,
|
|
section_path,
|
|
metadata,
|
|
page_start,
|
|
page_end,
|
|
embedding,
|
|
embedded_text_sha256,
|
|
embedding_profile_hash,
|
|
approval_status,
|
|
index_status
|
|
ON rag.chunks
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION rag.guard_approved_chunk_mutation();
|
|
"""
|
|
)
|
|
|
|
op.execute(
|
|
"""
|
|
CREATE FUNCTION rag.enforce_chunk_searchability()
|
|
RETURNS trigger
|
|
LANGUAGE plpgsql
|
|
SECURITY INVOKER
|
|
SET search_path = pg_catalog, rag
|
|
AS $function$
|
|
BEGIN
|
|
IF NEW.searchable AND NOT EXISTS (
|
|
SELECT 1
|
|
FROM rag.documents AS document
|
|
JOIN rag.document_versions AS version
|
|
ON version.id = NEW.document_version_id
|
|
AND version.document_id = document.id
|
|
WHERE document.id = NEW.document_id
|
|
AND document.knowledge_base_id = NEW.knowledge_base_id
|
|
AND document.active_version_id = NEW.document_version_id
|
|
AND document.status = 'READY'
|
|
AND document.deleted_at IS NULL
|
|
AND version.status = 'READY'
|
|
AND version.review_state = 'CLOUD_APPROVED'
|
|
AND version.outbound_manifest_sha256 = NEW.outbound_manifest_sha256
|
|
AND version.embedding_profile_hash = NEW.embedding_profile_hash
|
|
) THEN
|
|
RAISE EXCEPTION
|
|
'searchable chunk must belong to the active READY approved version'
|
|
USING ERRCODE = '23514';
|
|
END IF;
|
|
RETURN NEW;
|
|
END
|
|
$function$;
|
|
"""
|
|
)
|
|
op.execute(
|
|
"""
|
|
CREATE TRIGGER chunks_enforce_active_search_projection
|
|
BEFORE INSERT OR UPDATE OF
|
|
searchable,
|
|
knowledge_base_id,
|
|
document_id,
|
|
document_version_id,
|
|
approval_status,
|
|
outbound_manifest_sha256,
|
|
embedding_profile_hash,
|
|
index_status
|
|
ON rag.chunks
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION rag.enforce_chunk_searchability();
|
|
"""
|
|
)
|
|
|
|
op.execute(
|
|
"""
|
|
CREATE FUNCTION rag.enforce_document_activation()
|
|
RETURNS trigger
|
|
LANGUAGE plpgsql
|
|
SECURITY INVOKER
|
|
SET search_path = pg_catalog, rag
|
|
AS $function$
|
|
BEGIN
|
|
IF NEW.active_version_id IS NOT NULL AND NOT EXISTS (
|
|
SELECT 1
|
|
FROM rag.document_versions AS version
|
|
WHERE version.id = NEW.active_version_id
|
|
AND version.document_id = NEW.id
|
|
AND version.status = 'READY'
|
|
AND version.review_state = 'CLOUD_APPROVED'
|
|
) THEN
|
|
RAISE EXCEPTION
|
|
'active document version must be READY and CLOUD_APPROVED'
|
|
USING ERRCODE = '23514';
|
|
END IF;
|
|
|
|
IF NEW.active_version_id IS DISTINCT FROM OLD.active_version_id
|
|
OR NEW.status IS DISTINCT FROM OLD.status
|
|
OR NEW.deleted_at IS DISTINCT FROM OLD.deleted_at THEN
|
|
UPDATE rag.chunks
|
|
SET searchable = false,
|
|
updated_at = now()
|
|
WHERE document_id = OLD.id
|
|
AND (
|
|
NEW.active_version_id IS NULL
|
|
OR document_version_id <> NEW.active_version_id
|
|
OR NEW.status <> 'READY'
|
|
OR NEW.deleted_at IS NOT NULL
|
|
);
|
|
END IF;
|
|
RETURN NEW;
|
|
END
|
|
$function$;
|
|
"""
|
|
)
|
|
op.execute(
|
|
"""
|
|
CREATE TRIGGER documents_enforce_activation
|
|
BEFORE UPDATE OF active_version_id, status, deleted_at
|
|
ON rag.documents
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION rag.enforce_document_activation();
|
|
"""
|
|
)
|
|
|
|
op.execute(
|
|
"""
|
|
CREATE FUNCTION rag.revoke_cloud_approval_on_binding_change()
|
|
RETURNS trigger
|
|
LANGUAGE plpgsql
|
|
SECURITY INVOKER
|
|
SET search_path = pg_catalog, rag
|
|
AS $function$
|
|
DECLARE
|
|
revoked_state text;
|
|
BEGIN
|
|
IF OLD.review_state = 'CLOUD_APPROVED'
|
|
AND (
|
|
NEW.review_state IS DISTINCT FROM OLD.review_state
|
|
OR NEW.parser_profile_hash IS DISTINCT FROM OLD.parser_profile_hash
|
|
OR NEW.ocr_profile_hash IS DISTINCT FROM OLD.ocr_profile_hash
|
|
OR NEW.normalization_profile_hash
|
|
IS DISTINCT FROM OLD.normalization_profile_hash
|
|
OR NEW.chunk_profile_hash IS DISTINCT FROM OLD.chunk_profile_hash
|
|
OR NEW.cloud_policy_id IS DISTINCT FROM OLD.cloud_policy_id
|
|
OR NEW.embedding_profile_hash
|
|
IS DISTINCT FROM OLD.embedding_profile_hash
|
|
OR NEW.outbound_manifest_sha256
|
|
IS DISTINCT FROM OLD.outbound_manifest_sha256
|
|
) THEN
|
|
revoked_state := CASE
|
|
WHEN NEW.review_state = 'CLOUD_APPROVED'
|
|
THEN 'LOCAL_PARSED_PENDING_CLOUD_REVIEW'
|
|
ELSE NEW.review_state
|
|
END;
|
|
|
|
UPDATE rag.chunks
|
|
SET searchable = false,
|
|
approval_status = revoked_state,
|
|
outbound_manifest_sha256 = NULL,
|
|
index_status = 'PENDING',
|
|
embedding = NULL,
|
|
embedded_text_sha256 = NULL,
|
|
embedding_profile_hash = NULL,
|
|
updated_at = now()
|
|
WHERE document_version_id = OLD.id
|
|
AND approval_status = 'CLOUD_APPROVED';
|
|
|
|
UPDATE rag.documents
|
|
SET active_version_id = NULL,
|
|
status = CASE
|
|
WHEN revoked_state = 'REJECTED' THEN 'REJECTED'
|
|
ELSE 'LOCAL_PARSED_PENDING_CLOUD_REVIEW'
|
|
END,
|
|
updated_at = now()
|
|
WHERE active_version_id = OLD.id;
|
|
|
|
NEW.review_state := revoked_state;
|
|
NEW.cloud_approved_at := NULL;
|
|
NEW.cloud_approved_by := NULL;
|
|
END IF;
|
|
RETURN NEW;
|
|
END
|
|
$function$;
|
|
"""
|
|
)
|
|
|
|
op.execute(
|
|
"""
|
|
CREATE FUNCTION rag.deactivate_chunks_when_version_not_ready()
|
|
RETURNS trigger
|
|
LANGUAGE plpgsql
|
|
SECURITY INVOKER
|
|
SET search_path = pg_catalog, rag
|
|
AS $function$
|
|
BEGIN
|
|
IF OLD.status = 'READY' AND NEW.status <> 'READY' THEN
|
|
UPDATE rag.chunks
|
|
SET searchable = false,
|
|
index_status = CASE
|
|
WHEN NEW.status = 'FAILED' THEN 'FAILED'
|
|
ELSE index_status
|
|
END,
|
|
updated_at = now()
|
|
WHERE document_version_id = OLD.id;
|
|
|
|
UPDATE rag.documents
|
|
SET active_version_id = NULL,
|
|
status = CASE
|
|
WHEN NEW.status = 'FAILED' THEN 'FAILED'
|
|
ELSE 'VALIDATING'
|
|
END,
|
|
updated_at = now()
|
|
WHERE active_version_id = OLD.id;
|
|
END IF;
|
|
RETURN NEW;
|
|
END
|
|
$function$;
|
|
"""
|
|
)
|
|
op.execute(
|
|
"""
|
|
CREATE TRIGGER document_versions_deactivate_when_not_ready
|
|
BEFORE UPDATE OF status
|
|
ON rag.document_versions
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION rag.deactivate_chunks_when_version_not_ready();
|
|
"""
|
|
)
|
|
op.execute(
|
|
"""
|
|
CREATE TRIGGER document_versions_revoke_cloud_approval
|
|
BEFORE UPDATE OF
|
|
review_state,
|
|
parser_profile_hash,
|
|
ocr_profile_hash,
|
|
normalization_profile_hash,
|
|
chunk_profile_hash,
|
|
cloud_policy_id,
|
|
embedding_profile_hash,
|
|
outbound_manifest_sha256
|
|
ON rag.document_versions
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION rag.revoke_cloud_approval_on_binding_change();
|
|
"""
|
|
)
|
|
|
|
op.execute(
|
|
"""
|
|
CREATE TABLE rag.background_jobs (
|
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
job_type text NOT NULL,
|
|
required_capability text NOT NULL,
|
|
resource_type text NOT NULL,
|
|
resource_id uuid NOT NULL,
|
|
idempotency_key text NOT NULL,
|
|
payload jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
stage text NOT NULL DEFAULT 'PENDING',
|
|
status text NOT NULL DEFAULT 'QUEUED',
|
|
progress smallint NOT NULL DEFAULT 0,
|
|
priority integer NOT NULL DEFAULT 0,
|
|
attempt integer NOT NULL DEFAULT 0,
|
|
max_attempts integer NOT NULL DEFAULT 3,
|
|
run_after timestamptz NOT NULL DEFAULT now(),
|
|
lease_owner text,
|
|
lease_token uuid,
|
|
lease_until timestamptz,
|
|
last_error_code text,
|
|
last_error_message text,
|
|
created_at timestamptz NOT NULL DEFAULT now(),
|
|
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
finished_at timestamptz,
|
|
CONSTRAINT background_jobs_job_type_nonempty
|
|
CHECK (btrim(job_type) <> ''),
|
|
CONSTRAINT background_jobs_capability_nonempty
|
|
CHECK (btrim(required_capability) <> ''),
|
|
CONSTRAINT background_jobs_resource_type_nonempty
|
|
CHECK (btrim(resource_type) <> ''),
|
|
CONSTRAINT background_jobs_idempotency_key_nonempty
|
|
CHECK (btrim(idempotency_key) <> ''),
|
|
CONSTRAINT background_jobs_payload_object
|
|
CHECK (jsonb_typeof(payload) = 'object'),
|
|
CONSTRAINT background_jobs_stage_nonempty
|
|
CHECK (btrim(stage) <> ''),
|
|
CONSTRAINT background_jobs_status_valid
|
|
CHECK (status IN ('QUEUED', 'RUNNING', 'SUCCEEDED', 'FAILED', 'CANCELLED')),
|
|
CONSTRAINT background_jobs_progress_valid
|
|
CHECK (progress BETWEEN 0 AND 100),
|
|
CONSTRAINT background_jobs_attempts_valid
|
|
CHECK (
|
|
attempt >= 0
|
|
AND max_attempts > 0
|
|
AND attempt <= max_attempts
|
|
),
|
|
CONSTRAINT background_jobs_lease_consistent
|
|
CHECK (
|
|
(
|
|
status = 'RUNNING'
|
|
AND lease_owner IS NOT NULL
|
|
AND btrim(lease_owner) <> ''
|
|
AND lease_token IS NOT NULL
|
|
AND lease_until IS NOT NULL
|
|
)
|
|
OR (
|
|
status <> 'RUNNING'
|
|
AND lease_owner IS NULL
|
|
AND lease_token IS NULL
|
|
AND lease_until IS NULL
|
|
)
|
|
),
|
|
CONSTRAINT background_jobs_terminal_time_consistent
|
|
CHECK (
|
|
(
|
|
status IN ('SUCCEEDED', 'FAILED', 'CANCELLED')
|
|
AND finished_at IS NOT NULL
|
|
)
|
|
OR (
|
|
status IN ('QUEUED', 'RUNNING')
|
|
AND finished_at IS NULL
|
|
)
|
|
),
|
|
CONSTRAINT background_jobs_success_progress_complete
|
|
CHECK (status <> 'SUCCEEDED' OR progress = 100),
|
|
CONSTRAINT background_jobs_error_message_bounded
|
|
CHECK (last_error_message IS NULL OR length(last_error_message) <= 2000),
|
|
CONSTRAINT background_jobs_type_idempotency_key
|
|
UNIQUE (job_type, idempotency_key)
|
|
);
|
|
"""
|
|
)
|
|
op.execute(
|
|
"""
|
|
CREATE INDEX background_jobs_claim_queued
|
|
ON rag.background_jobs (
|
|
required_capability,
|
|
priority DESC,
|
|
run_after,
|
|
created_at
|
|
)
|
|
WHERE status = 'QUEUED';
|
|
"""
|
|
)
|
|
op.execute(
|
|
"""
|
|
CREATE INDEX background_jobs_reap_expired
|
|
ON rag.background_jobs (lease_until, required_capability)
|
|
WHERE status = 'RUNNING';
|
|
"""
|
|
)
|
|
op.execute(
|
|
"""
|
|
CREATE INDEX background_jobs_resource_lookup
|
|
ON rag.background_jobs (resource_type, resource_id, created_at DESC);
|
|
"""
|
|
)
|
|
|
|
|
|
def downgrade() -> None:
|
|
op.execute(
|
|
"""
|
|
ALTER TABLE rag.documents
|
|
DROP CONSTRAINT IF EXISTS documents_active_version_fk;
|
|
"""
|
|
)
|
|
op.execute("DROP TABLE IF EXISTS rag.background_jobs;")
|
|
op.execute("DROP TABLE IF EXISTS rag.chunks;")
|
|
op.execute("DROP FUNCTION IF EXISTS rag.guard_approved_chunk_mutation();")
|
|
op.execute("DROP FUNCTION IF EXISTS rag.guard_ready_embedding_update();")
|
|
op.execute("DROP FUNCTION IF EXISTS rag.enforce_chunk_searchability();")
|
|
op.execute("DROP TABLE IF EXISTS rag.outbound_manifest_items;")
|
|
op.execute("DROP FUNCTION IF EXISTS rag.guard_approved_manifest_item_mutation();")
|
|
op.execute("DROP TABLE IF EXISTS rag.document_versions;")
|
|
op.execute("DROP FUNCTION IF EXISTS rag.revoke_cloud_approval_on_binding_change();")
|
|
op.execute("DROP FUNCTION IF EXISTS rag.deactivate_chunks_when_version_not_ready();")
|
|
op.execute("DROP TABLE IF EXISTS rag.documents;")
|
|
op.execute("DROP FUNCTION IF EXISTS rag.enforce_document_activation();")
|
|
op.execute("DROP TABLE IF EXISTS rag.access_scopes;")
|
|
op.execute(
|
|
"""
|
|
DROP TABLE IF EXISTS rag.knowledge_bases;
|
|
"""
|
|
)
|