Phase 3 Slice 1 Go-side of mig 078–080. Compat-mode reads: the
service selects BOTH the legacy shape (is_mandatory, is_optional,
condition_flag, condition_rule_id) and the new shape (priority,
condition_expr, is_court_set, trigger_event_id,
spawn_proceeding_type_id, combine_op, lifecycle_state, draft_of,
published_at). Existing callers stay on the legacy fields until
Slice 4 cuts the calculator over.
Adds:
- DeadlineRule field block for the nine Phase 3 columns. NULLable
jsonb (condition_expr) uses NullableJSON to dodge the
json.RawMessage NULL-scan trap (see Project.Metadata note from
t-paliad-138 dogfood).
- Project.InstanceLevel *string.
- DeadlineRuleAudit row struct (id, rule_id, changed_by,
changed_at, action, before_json, after_json, reason,
migration_exported).
- ruleColumns const extended to project every new column.
Test (TEST_DATABASE_URL-gated, mirrors audit_service_test.go):
1. ruleColumns SELECT scans cleanly — every new column populates
its Go field.
2. Migration defaults land: priority='mandatory',
is_court_set=false, lifecycle_state='published' on every
pre-Slice-1 row.
3. Audit trigger writes one row on UPDATE WITH paliad.audit_reason
set, captures before+after JSON + reason.
4. Audit trigger RAISES on UPDATE WITHOUT paliad.audit_reason —
Slice 2 backfills fail loudly if they forget to set it.
5. paliad.projects.instance_level accepts NULL + first/appeal/
cassation, rejects 'final'.
Build clean, full test suite green (live DB test skipped locally).