Drops the legacy paliad.deadline_rules table after 3 weeks of dual-write
shadowing (mig 136 → B.2 dual-write → B.3 read cutover via view). The
new tables — paliad.procedural_events, paliad.sequencing_rules,
paliad.legal_sources — are the sole source of truth from this commit
forward.
Pre-flip drift verified clean against prod:
deadline_rules=231 == sequencing_rules=231 == procedural_events=231
legal_sources=87
missing_sr=0, orphaned_sr=0, mismatched_lifecycle=0
* internal/db/migrations/140_drop_deadline_rules.up.sql (new) —
Single TX, audit-first:
1. CREATE TABLE paliad.deadline_rules_pre_140 AS TABLE paliad.deadline_rules
(precedent migs 091/093/095/098 — snapshot in same TX as destructive op).
2. Final reconciliation UPDATE on paliad.deadlines (no-op when
drift is already 0; defensive against last-minute writes).
3. DROP TRIGGER deadline_rules_audit_aiud.
4. Re-point FKs to sequencing_rules:
- paliad.appointments.deadline_rule_id → paliad.sequencing_rules(id)
- paliad.deadline_rule_backfill_orphans.resolved_rule_id → paliad.sequencing_rules(id)
(the id values are identical — sr.id inherited dr.id at mig 136.)
5. DROP COLUMN paliad.deadlines.rule_id.
6. DROP TABLE paliad.deadline_rules.
7. CREATE INSTEAD OF INSERT + INSTEAD OF UPDATE triggers on
paliad.deadline_rules_unified. Triggers route writes into the
three new tables in the same TX, preserving the legacy column
shape on the wire so RuleEditorService SQL only needs a
table-name swap, not a structural rewrite. Synthetic-code mint
expression is byte-identical to mig 136 + the B.2 dual-write
helper. POST assertions confirm the table is gone, the column
is gone, and the snapshot matches.
Trigger design notes (1:N caveat documented in-trigger):
- PE identity columns (code, name, name_en, description, event_kind,
primary_party_default, legal_source_id, concept_id) mirror from
the writing sequencing-rule.
- PE lifecycle columns (lifecycle_state, published_at, is_active)
deliberately do NOT mirror — a draft sequencing-rule cloned from
a published source shares the source's PE; we don't want the
clone's 'draft' lifecycle to leak back onto the source's PE.
Practical bound today (1:1 corpus); explicit comment in-trigger
for the eventual 1:N pattern.
* internal/db/migrations/140_drop_deadline_rules.down.sql (new) —
Best-effort restore from the snapshot. Triggers / indexes /
CHECK constraints from historical migrations are NOT replayed;
operator must reapply 078/079/091/095/098/122/128/134/135 to
bring the restored table to working shape. The down path is for
catastrophic recovery, not casual revert.
* internal/services/rule_editor_service.go —
Six syncDualWriteFromDeadlineRule(...) calls removed (the
INSTEAD OF triggers now do the fan-out). Five
INSERT/UPDATE paliad.deadline_rules statements (Create,
UpdateDraft, CloneAsDraft INSERT+SELECT, Publish, peer-archive,
flipLifecycle) renamed to paliad.deadline_rules_unified —
trigger handles the routing.
* internal/services/rule_editor_orphans.go — ResolveOrphan no
longer writes deadlines.rule_id (column dropped). Sets
sequencing_rule_id directly + derives procedural_event_id from
the matching sequencing_rules row in the same UPDATE statement.
* internal/services/deadline_service.go — deadlineColumns now
lists sequencing_rule_id (Deadline.RuleID still binds to it via
the db tag rename below). Update path's appendSet("rule_id",…)
flipped to appendSet("sequencing_rule_id",…) and post-write
derivation moved to the renamed syncDeadlineProceduralEventID
helper.
* internal/services/projection_service.go,
internal/services/submission_vars.go — `WHERE rule_id = $X`
reads on paliad.deadlines flipped to sequencing_rule_id.
* internal/models/models.go — Deadline.RuleID db tag changed from
"rule_id" to "sequencing_rule_id". Field name + JSON name kept
for backward compat with the frontend and existing Go callers;
semantic value is identical (same UUID).
* internal/services/dual_write.go — Massively trimmed.
Removed: syncDualWriteFromDeadlineRule, syncDeadlineDualLinks,
CheckDualWriteDrift, DualWriteDriftReport, HasDrift,
StartDualWriteDriftCheckLoop. All referenced
paliad.deadline_rules which no longer exists.
Kept (renamed): syncDeadlineProceduralEventID — derives
procedural_event_id from sequencing_rule_id after any
DeadlineService.Update that touched the back-link.
* cmd/server/main.go — Removed the StartDualWriteDriftCheckLoop
bootstrap call (and its `time` import that only that call
needed). Comment notes the retirement.
* internal/services/dual_write_test.go — Removed the final
CheckDualWriteDrift assertion in
TestDualWrite_RuleEditorLifecycle (function deleted). The
per-step asserts against procedural_events / sequencing_rules
/ legal_sources cover the same contract by direct query.
Hard rules followed:
- Audit-first: snapshot precedes destructive ops in the same TX.
- No silent data loss: pre-drop drift was zero; snapshot captures
the final state; FK re-points use identical UUIDs.
- INSTEAD OF triggers documented in mig 140 — single source of
truth for the legacy→new mapping.
- Down migration is honest about its scope (catastrophic recovery
only).
Build + vet clean. TestMigrations_NoDuplicateSlot passes. Live-DB
tests skipped (no TEST_DATABASE_URL in this env) — they'll exercise
the full mig 140 + INSTEAD OF triggers in CI.
48 lines
2.4 KiB
SQL
48 lines
2.4 KiB
SQL
-- 140_drop_deadline_rules (down) — Slice B.4, t-paliad-305
|
|
--
|
|
-- Best-effort recovery from the deadline_rules_pre_140 snapshot. The
|
|
-- original triggers (mig 079 audit), indexes, CHECK constraints (mig
|
|
-- 135 primary_party), and FK constraints on the new tables are NOT
|
|
-- recreated here — restoring the working state requires replaying
|
|
-- migrations 078/079/091/095/098/122/128/134/135 against the restored
|
|
-- table.
|
|
--
|
|
-- Use this only for catastrophic recovery. The normal revert path
|
|
-- for B.4 is to re-deploy the previous container image (which still
|
|
-- writes via the dual-write helper to a paliad.deadline_rules that no
|
|
-- longer exists) — that would crash on first write, so true revert
|
|
-- requires this down + a code revert + a snapshot restore.
|
|
|
|
-- Drop the INSTEAD OF triggers + functions
|
|
DROP TRIGGER IF EXISTS deadline_rules_unified_insert ON paliad.deadline_rules_unified;
|
|
DROP TRIGGER IF EXISTS deadline_rules_unified_update ON paliad.deadline_rules_unified;
|
|
DROP FUNCTION IF EXISTS paliad.deadline_rules_unified_insert_trigger();
|
|
DROP FUNCTION IF EXISTS paliad.deadline_rules_unified_update_trigger();
|
|
|
|
-- Recreate paliad.deadline_rules from snapshot.
|
|
CREATE TABLE paliad.deadline_rules AS TABLE paliad.deadline_rules_pre_140;
|
|
|
|
-- Re-add the PK constraint (CREATE TABLE AS doesn't carry constraints).
|
|
ALTER TABLE paliad.deadline_rules ADD PRIMARY KEY (id);
|
|
|
|
-- Re-point the FKs back to deadline_rules.
|
|
ALTER TABLE paliad.appointments
|
|
DROP CONSTRAINT IF EXISTS appointments_deadline_rule_id_fkey;
|
|
ALTER TABLE paliad.appointments
|
|
ADD CONSTRAINT appointments_deadline_rule_id_fkey
|
|
FOREIGN KEY (deadline_rule_id) REFERENCES paliad.deadline_rules(id);
|
|
|
|
ALTER TABLE paliad.deadline_rule_backfill_orphans
|
|
DROP CONSTRAINT IF EXISTS deadline_rule_backfill_orphans_resolved_rule_id_fkey;
|
|
ALTER TABLE paliad.deadline_rule_backfill_orphans
|
|
ADD CONSTRAINT deadline_rule_backfill_orphans_resolved_rule_id_fkey
|
|
FOREIGN KEY (resolved_rule_id) REFERENCES paliad.deadline_rules(id);
|
|
|
|
-- Re-add deadlines.rule_id from the snapshot's data (via sequencing_rule_id
|
|
-- which inherited deadline_rules.id during mig 136).
|
|
ALTER TABLE paliad.deadlines ADD COLUMN rule_id uuid;
|
|
UPDATE paliad.deadlines SET rule_id = sequencing_rule_id WHERE sequencing_rule_id IS NOT NULL;
|
|
ALTER TABLE paliad.deadlines
|
|
ADD CONSTRAINT fristen_rule_id_fkey
|
|
FOREIGN KEY (rule_id) REFERENCES paliad.deadline_rules(id);
|