20260723_160_rule_execution_evidence.py 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. """Make governed rule execution evidence durable and idempotent."""
  2. from alembic import op
  3. revision = "20260723_160"
  4. down_revision = "20260723_150"
  5. branch_labels = None
  6. depends_on = None
  7. def upgrade() -> None:
  8. op.execute(
  9. """
  10. ALTER TABLE public.rule_runs
  11. ADD COLUMN evidence_key CHAR(64),
  12. ADD COLUMN commit_outcome VARCHAR(30) NOT NULL
  13. DEFAULT 'not_applicable'
  14. CHECK (commit_outcome IN (
  15. 'not_applicable','not_committed','committed','unknown'
  16. )),
  17. ADD COLUMN public_result JSONB,
  18. ADD COLUMN failure_code VARCHAR(100),
  19. ADD COLUMN updated_at TIMESTAMPTZ NOT NULL
  20. DEFAULT CURRENT_TIMESTAMP;
  21. ALTER TABLE public.rule_runs
  22. ADD CONSTRAINT rule_runs_evidence_key UNIQUE (evidence_key);
  23. ALTER TABLE public.rule_violation_samples
  24. ADD COLUMN artifact_digest CHAR(64),
  25. ADD COLUMN schema_hash CHAR(64),
  26. ADD COLUMN handoff_status VARCHAR(20) NOT NULL
  27. DEFAULT 'legacy'
  28. CHECK (handoff_status IN (
  29. 'legacy','pending','ready','failed'
  30. )),
  31. ADD COLUMN failure_code VARCHAR(100),
  32. ADD COLUMN updated_at TIMESTAMPTZ NOT NULL
  33. DEFAULT CURRENT_TIMESTAMP;
  34. DO $$
  35. BEGIN
  36. IF EXISTS (
  37. SELECT 1
  38. FROM public.rule_violation_samples
  39. GROUP BY rule_run_id
  40. HAVING COUNT(*) > 1
  41. ) THEN
  42. RAISE EXCEPTION
  43. 'violation sample migration found duplicate rule_run_id';
  44. END IF;
  45. IF EXISTS (
  46. SELECT 1
  47. FROM public.rule_violation_samples
  48. WHERE sample_count > 100
  49. ) THEN
  50. RAISE EXCEPTION
  51. 'violation sample migration found rows above 100';
  52. END IF;
  53. END
  54. $$;
  55. ALTER TABLE public.rule_violation_samples
  56. DROP CONSTRAINT IF EXISTS
  57. rule_violation_samples_sample_count_check,
  58. ADD CONSTRAINT rule_violation_samples_sample_count_check
  59. CHECK (sample_count >= 0 AND sample_count <= 100),
  60. ADD CONSTRAINT rule_violation_samples_rule_run_key
  61. UNIQUE (rule_run_id);
  62. CREATE INDEX idx_rule_violation_samples_retention
  63. ON public.rule_violation_samples
  64. (handoff_status, expires_at, id);
  65. """
  66. )
  67. def downgrade() -> None:
  68. raise RuntimeError(
  69. "rule execution evidence is forward-only and cannot be downgraded"
  70. )