20260723_160_rule_execution_evidence.py 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172
  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. WHERE sample_count > 100
  40. ) THEN
  41. RAISE EXCEPTION
  42. 'violation sample migration found rows above 100';
  43. END IF;
  44. END
  45. $$;
  46. ALTER TABLE public.rule_violation_samples
  47. DROP CONSTRAINT IF EXISTS
  48. rule_violation_samples_sample_count_check,
  49. ADD CONSTRAINT rule_violation_samples_sample_count_check
  50. CHECK (sample_count >= 0 AND sample_count <= 100),
  51. ADD CONSTRAINT rule_violation_samples_rule_run_key
  52. UNIQUE (rule_run_id);
  53. CREATE INDEX idx_rule_violation_samples_retention
  54. ON public.rule_violation_samples
  55. (handoff_status, expires_at, id);
  56. """
  57. )
  58. def downgrade() -> None:
  59. raise RuntimeError(
  60. "rule execution evidence is forward-only and cannot be downgraded"
  61. )