-- Regulatory ISO mappings are global, tenant-independent knowledge keyed by rule -- identity plus pipeline versions (see docs/REGULATORY_RAG.md). Batches are -- project-scoped runs over a filtered finding set. CREATE TABLE assessor_regulatory_mappings ( id text PRIMARY KEY, rule_key text NOT NULL, source_tool text NOT NULL, source_rule_id text, run_status text NOT NULL CHECK (run_status IN ('queued', 'running', 'succeeded', 'failed')), query_hash text NOT NULL CHECK (query_hash ~ '^[a-f0-9]{64}$'), corpus_id text NOT NULL, corpus_version text NOT NULL, embedding_model text NOT NULL, retrieval_version text NOT NULL, prompt_version text NOT NULL, output_schema_version integer NOT NULL, model text NOT NULL, created_at timestamptz NOT NULL, updated_at timestamptz NOT NULL, data jsonb NOT NULL, -- The id is a deterministic digest of (rule_key, corpus/retrieval/prompt versions, model), -- so there is exactly one row per rule per pipeline version tuple. CONSTRAINT assessor_regulatory_mappings_identity_uq UNIQUE (rule_key, corpus_version, retrieval_version, prompt_version, model) ); CREATE INDEX assessor_regulatory_mappings_rule_idx ON assessor_regulatory_mappings (rule_key, updated_at DESC, id DESC); CREATE TABLE assessor_regulatory_mapping_decisions ( id text PRIMARY KEY, rule_key text NOT NULL, source_tool text NOT NULL, source_rule_id text, mapping_id text NOT NULL, control_id text NOT NULL, decision text NOT NULL CHECK (decision IN ('accepted', 'rejected')), previous_decision_id text REFERENCES assessor_regulatory_mapping_decisions(id), created_at timestamptz NOT NULL, data jsonb NOT NULL ); CREATE INDEX assessor_regulatory_mapping_decisions_rule_control_idx ON assessor_regulatory_mapping_decisions (rule_key, control_id, created_at DESC, id DESC); CREATE TABLE assessor_regulatory_mapping_batches ( id text PRIMARY KEY, project_id text NOT NULL REFERENCES assessor_projects(id) ON DELETE CASCADE, scope_key text NOT NULL, filter_hash text NOT NULL CHECK (filter_hash ~ '^[a-f0-9]{64}$'), idempotency_key text NOT NULL, run_status text NOT NULL CHECK (run_status IN ('queued', 'running', 'completed', 'failed')), created_at timestamptz NOT NULL, updated_at timestamptz NOT NULL, data jsonb NOT NULL, CONSTRAINT assessor_regulatory_mapping_batches_idempotency_uq UNIQUE (project_id, idempotency_key) ); CREATE INDEX assessor_regulatory_mapping_batches_scope_idx ON assessor_regulatory_mapping_batches (project_id, scope_key, created_at DESC, id DESC); CREATE UNIQUE INDEX assessor_regulatory_mapping_batches_active_scope_uq ON assessor_regulatory_mapping_batches (project_id, scope_key) WHERE run_status IN ('queued', 'running');