20260723_110_ai_data_rules.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. """Add governed AI data rules, standards, production lines, and deployments."""
  2. from alembic import op
  3. revision = "20260723_110"
  4. down_revision = "20260722_110"
  5. branch_labels = None
  6. depends_on = None
  7. def upgrade() -> None:
  8. op.execute(
  9. """
  10. CREATE TABLE public.data_rules (
  11. id UUID PRIMARY KEY,
  12. rule_uid UUID NOT NULL UNIQUE,
  13. name VARCHAR(200) NOT NULL,
  14. category VARCHAR(50) NOT NULL,
  15. owner_uid UUID REFERENCES public.users(id) ON DELETE SET NULL,
  16. status VARCHAR(20) NOT NULL DEFAULT 'active'
  17. CHECK (status IN ('active','deprecated')),
  18. created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
  19. updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
  20. );
  21. CREATE TABLE public.data_rule_versions (
  22. id UUID PRIMARY KEY,
  23. rule_uid UUID NOT NULL REFERENCES public.data_rules(rule_uid)
  24. ON DELETE RESTRICT,
  25. version_no INTEGER NOT NULL CHECK (version_no > 0),
  26. source_text TEXT NOT NULL,
  27. source_language VARCHAR(20) NOT NULL DEFAULT 'zh-CN',
  28. rule_spec JSONB NOT NULL,
  29. spec_hash CHAR(64) NOT NULL,
  30. generated_kind VARCHAR(30) NOT NULL DEFAULT 'rulespec'
  31. CHECK (generated_kind IN (
  32. 'rulespec','cel','sql','polars','generated_python'
  33. )),
  34. status VARCHAR(20) NOT NULL DEFAULT 'draft'
  35. CHECK (status IN (
  36. 'draft','validated','published','deprecated','rejected'
  37. )),
  38. created_by UUID REFERENCES public.users(id) ON DELETE SET NULL,
  39. created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
  40. published_at TIMESTAMPTZ,
  41. UNIQUE (rule_uid, version_no),
  42. UNIQUE (rule_uid, spec_hash)
  43. );
  44. CREATE INDEX idx_data_rule_versions_status
  45. ON public.data_rule_versions(rule_uid, status, version_no DESC);
  46. CREATE TABLE public.rule_generation_runs (
  47. id UUID PRIMARY KEY,
  48. rule_version_id UUID REFERENCES public.data_rule_versions(id)
  49. ON DELETE SET NULL,
  50. authoring_surface VARCHAR(30) NOT NULL
  51. CHECK (authoring_surface IN ('data_standard','data_flow')),
  52. source_text_hash CHAR(64) NOT NULL,
  53. model_provider VARCHAR(80) NOT NULL,
  54. model_name VARCHAR(120) NOT NULL,
  55. prompt_version VARCHAR(80) NOT NULL,
  56. schema_version VARCHAR(40) NOT NULL,
  57. context_hash CHAR(64) NOT NULL,
  58. candidate_hash CHAR(64) NOT NULL,
  59. confidence NUMERIC(5,4) NOT NULL
  60. CHECK (confidence >= 0 AND confidence <= 1),
  61. assumptions JSONB NOT NULL DEFAULT '[]'::jsonb,
  62. ambiguities JSONB NOT NULL DEFAULT '[]'::jsonb,
  63. repair_attempts INTEGER NOT NULL DEFAULT 0
  64. CHECK (repair_attempts >= 0 AND repair_attempts <= 2),
  65. decision VARCHAR(30) NOT NULL CHECK (decision IN (
  66. 'ready','clarification_required','rejected','failed'
  67. )),
  68. decision_detail JSONB NOT NULL DEFAULT '{}'::jsonb,
  69. correlation_id UUID NOT NULL,
  70. created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
  71. );
  72. CREATE INDEX idx_rule_generation_correlation
  73. ON public.rule_generation_runs(correlation_id, created_at);
  74. CREATE TABLE public.data_standards (
  75. id UUID PRIMARY KEY,
  76. standard_uid UUID NOT NULL UNIQUE,
  77. name VARCHAR(200) NOT NULL,
  78. owner_uid UUID REFERENCES public.users(id) ON DELETE SET NULL,
  79. status VARCHAR(20) NOT NULL DEFAULT 'active'
  80. CHECK (status IN ('active','deprecated')),
  81. created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
  82. updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
  83. );
  84. CREATE TABLE public.data_standard_versions (
  85. id UUID PRIMARY KEY,
  86. standard_uid UUID NOT NULL
  87. REFERENCES public.data_standards(standard_uid)
  88. ON DELETE RESTRICT,
  89. version_no INTEGER NOT NULL CHECK (version_no > 0),
  90. source_text TEXT NOT NULL,
  91. standard_spec JSONB NOT NULL,
  92. spec_hash CHAR(64) NOT NULL,
  93. scope JSONB NOT NULL,
  94. status VARCHAR(20) NOT NULL DEFAULT 'draft'
  95. CHECK (status IN (
  96. 'draft','validated','published','deprecated','rejected'
  97. )),
  98. created_by UUID REFERENCES public.users(id) ON DELETE SET NULL,
  99. created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
  100. published_at TIMESTAMPTZ,
  101. UNIQUE (standard_uid, version_no),
  102. UNIQUE (standard_uid, spec_hash)
  103. );
  104. CREATE INDEX idx_data_standard_versions_status
  105. ON public.data_standard_versions(
  106. standard_uid, status, version_no DESC
  107. );
  108. CREATE TABLE public.standard_rule_bindings (
  109. id UUID PRIMARY KEY,
  110. standard_version_id UUID NOT NULL
  111. REFERENCES public.data_standard_versions(id)
  112. ON DELETE RESTRICT,
  113. clause_id VARCHAR(100) NOT NULL,
  114. rule_version_id UUID NOT NULL
  115. REFERENCES public.data_rule_versions(id)
  116. ON DELETE RESTRICT,
  117. severity VARCHAR(20) NOT NULL CHECK (
  118. severity IN ('info','warning','error','critical')
  119. ),
  120. exception_policy VARCHAR(20) NOT NULL CHECK (
  121. exception_policy IN ('reject','quarantine','warn','fail')
  122. ),
  123. created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
  124. UNIQUE (standard_version_id, clause_id)
  125. );
  126. CREATE INDEX idx_standard_rule_binding_rule
  127. ON public.standard_rule_bindings(rule_version_id);
  128. CREATE TABLE public.dataflow_versions (
  129. id UUID PRIMARY KEY,
  130. dataflow_uid UUID NOT NULL,
  131. version_no INTEGER NOT NULL CHECK (version_no > 0),
  132. name VARCHAR(200) NOT NULL,
  133. source_text TEXT,
  134. dataflow_spec JSONB NOT NULL,
  135. input_schema_hashes JSONB NOT NULL,
  136. output_schema_hash CHAR(64) NOT NULL,
  137. package JSONB,
  138. package_hash CHAR(64),
  139. status VARCHAR(20) NOT NULL DEFAULT 'draft'
  140. CHECK (status IN (
  141. 'draft','validated','released','deprecated','rejected'
  142. )),
  143. created_by UUID REFERENCES public.users(id) ON DELETE SET NULL,
  144. created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
  145. released_at TIMESTAMPTZ,
  146. UNIQUE (dataflow_uid, version_no)
  147. );
  148. CREATE INDEX idx_dataflow_versions_status
  149. ON public.dataflow_versions(
  150. dataflow_uid, status, version_no DESC
  151. );
  152. CREATE TABLE public.dataflow_component_bindings (
  153. id UUID PRIMARY KEY,
  154. dataflow_version_id UUID NOT NULL
  155. REFERENCES public.dataflow_versions(id)
  156. ON DELETE RESTRICT,
  157. component_id VARCHAR(100) NOT NULL,
  158. component_kind VARCHAR(30) NOT NULL CHECK (
  159. component_kind IN (
  160. 'standard.enforce','rule.apply','quality.check'
  161. )
  162. ),
  163. standard_version_id UUID
  164. REFERENCES public.data_standard_versions(id)
  165. ON DELETE RESTRICT,
  166. rule_version_id UUID
  167. REFERENCES public.data_rule_versions(id)
  168. ON DELETE RESTRICT,
  169. stage VARCHAR(30) NOT NULL,
  170. order_no INTEGER NOT NULL CHECK (order_no >= 0),
  171. idempotency JSONB,
  172. provenance JSONB NOT NULL DEFAULT '{}'::jsonb,
  173. created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
  174. UNIQUE (dataflow_version_id, component_id),
  175. CHECK (
  176. (
  177. component_kind = 'standard.enforce'
  178. AND standard_version_id IS NOT NULL
  179. AND rule_version_id IS NULL
  180. )
  181. OR (
  182. component_kind IN ('rule.apply','quality.check')
  183. AND standard_version_id IS NULL
  184. AND rule_version_id IS NOT NULL
  185. )
  186. )
  187. );
  188. CREATE INDEX idx_dataflow_component_standard
  189. ON public.dataflow_component_bindings(standard_version_id)
  190. WHERE standard_version_id IS NOT NULL;
  191. CREATE INDEX idx_dataflow_component_rule
  192. ON public.dataflow_component_bindings(rule_version_id)
  193. WHERE rule_version_id IS NOT NULL;
  194. CREATE TABLE public.rule_execution_plans (
  195. id UUID PRIMARY KEY,
  196. component_binding_id UUID NOT NULL
  197. REFERENCES public.dataflow_component_bindings(id)
  198. ON DELETE RESTRICT,
  199. backend VARCHAR(30) NOT NULL CHECK (
  200. backend IN (
  201. 'sql_pushdown','polars_batch','quality_check',
  202. 'generated_python','external_adapter'
  203. )
  204. ),
  205. compiler_version VARCHAR(80) NOT NULL,
  206. plan JSONB NOT NULL,
  207. plan_hash CHAR(64) NOT NULL,
  208. schema_hashes JSONB NOT NULL,
  209. status VARCHAR(20) NOT NULL DEFAULT 'compiled'
  210. CHECK (status IN ('compiled','published','revoked')),
  211. created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
  212. UNIQUE (component_binding_id, plan_hash)
  213. );
  214. CREATE TABLE public.rule_artifacts (
  215. id UUID PRIMARY KEY,
  216. rule_version_id UUID NOT NULL
  217. REFERENCES public.data_rule_versions(id)
  218. ON DELETE RESTRICT,
  219. kind VARCHAR(30) NOT NULL CHECK (
  220. kind IN ('cel','sql','polars','python','test','manifest')
  221. ),
  222. uri VARCHAR(1000) NOT NULL,
  223. artifact_digest CHAR(64) NOT NULL,
  224. signature VARCHAR(2000),
  225. runtime_digest CHAR(64),
  226. dependency_manifest JSONB NOT NULL DEFAULT '{}'::jsonb,
  227. status VARCHAR(20) NOT NULL DEFAULT 'built'
  228. CHECK (status IN ('built','signed','published','revoked')),
  229. created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
  230. UNIQUE (rule_version_id, kind, artifact_digest)
  231. );
  232. CREATE TABLE public.dataflow_deployments (
  233. id UUID PRIMARY KEY,
  234. dataflow_version_id UUID NOT NULL
  235. REFERENCES public.dataflow_versions(id)
  236. ON DELETE RESTRICT,
  237. environment VARCHAR(20) NOT NULL CHECK (
  238. environment IN ('development','test','production')
  239. ),
  240. workflow_version_id UUID
  241. REFERENCES public.dataflow_workflow_versions(id)
  242. ON DELETE SET NULL,
  243. schedule_plan_id UUID
  244. REFERENCES public.workflow_schedules(id)
  245. ON DELETE SET NULL,
  246. deployment_config JSONB NOT NULL DEFAULT '{}'::jsonb,
  247. status VARCHAR(30) NOT NULL DEFAULT 'draft' CHECK (
  248. status IN (
  249. 'draft','disabled','canary','active','superseded',
  250. 'rolled_back','failed'
  251. )
  252. ),
  253. activated_by UUID REFERENCES public.users(id) ON DELETE SET NULL,
  254. activated_at TIMESTAMPTZ,
  255. created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
  256. updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
  257. UNIQUE (dataflow_version_id, environment)
  258. );
  259. CREATE UNIQUE INDEX uq_dataflow_deployment_active_environment
  260. ON public.dataflow_deployments(dataflow_version_id, environment)
  261. WHERE status = 'active';
  262. CREATE TABLE public.rule_runs (
  263. id UUID PRIMARY KEY,
  264. workflow_run_id UUID REFERENCES public.workflow_runs(id)
  265. ON DELETE SET NULL,
  266. deployment_id UUID NOT NULL
  267. REFERENCES public.dataflow_deployments(id)
  268. ON DELETE RESTRICT,
  269. component_binding_id UUID NOT NULL
  270. REFERENCES public.dataflow_component_bindings(id)
  271. ON DELETE RESTRICT,
  272. rule_version_id UUID NOT NULL
  273. REFERENCES public.data_rule_versions(id)
  274. ON DELETE RESTRICT,
  275. plan_hash CHAR(64) NOT NULL,
  276. rows_in BIGINT NOT NULL DEFAULT 0 CHECK (rows_in >= 0),
  277. rows_out BIGINT NOT NULL DEFAULT 0 CHECK (rows_out >= 0),
  278. rows_rejected BIGINT NOT NULL DEFAULT 0 CHECK (rows_rejected >= 0),
  279. rows_quarantined BIGINT NOT NULL DEFAULT 0 CHECK (
  280. rows_quarantined >= 0
  281. ),
  282. status VARCHAR(30) NOT NULL CHECK (
  283. status IN (
  284. 'queued','running','success','failed','unknown','cancelled'
  285. )
  286. ),
  287. timings JSONB NOT NULL DEFAULT '{}'::jsonb,
  288. correlation_id UUID NOT NULL,
  289. started_at TIMESTAMPTZ,
  290. finished_at TIMESTAMPTZ,
  291. created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
  292. );
  293. CREATE INDEX idx_rule_runs_correlation
  294. ON public.rule_runs(correlation_id, created_at);
  295. CREATE TABLE public.rule_violation_samples (
  296. id UUID PRIMARY KEY,
  297. rule_run_id UUID NOT NULL REFERENCES public.rule_runs(id)
  298. ON DELETE CASCADE,
  299. artifact_ref VARCHAR(1000) NOT NULL,
  300. sample_count INTEGER NOT NULL CHECK (
  301. sample_count >= 0 AND sample_count <= 1000
  302. ),
  303. redaction_policy VARCHAR(100) NOT NULL,
  304. expires_at TIMESTAMPTZ NOT NULL,
  305. created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
  306. );
  307. """
  308. )
  309. def downgrade() -> None:
  310. # Published standards, rules, production lines, deployments, and run evidence
  311. # are intentionally retained during application rollback.
  312. pass