Phase 2 — Rollback Package
Rollback Package — Phase 2
What Phase 2 added (must be removed to roll back)
dot_config keys (+4)
queue.lease.reaper_enabledqueue.lease.reaper_dry_run_onlyqueue.dlq.replay_enabledqueue.runtime.phase
dot_config value flipped (1)
queue.heartbeat.enabled:false→true
queue_heartbeat rows (+1)
executor_name='iu_outbound_default'(passive marker)
Functions (+5)
fn_queue_heartbeat_register_passive(text, text, timestamptz, text, text)fn_job_reap_stale_leases_dry_run(integer)fn_job_reap_stale_leases_apply(text, integer)fn_job_dead_letter_requeue_dry_run(uuid)fn_job_dead_letter_triage_update(uuid, text, text, text)
Rollback SQL (single transaction)
BEGIN;
-- Drop new functions
DROP FUNCTION IF EXISTS public.fn_queue_heartbeat_register_passive(text, text, timestamptz, text, text);
DROP FUNCTION IF EXISTS public.fn_job_reap_stale_leases_dry_run(integer);
DROP FUNCTION IF EXISTS public.fn_job_reap_stale_leases_apply(text, integer);
DROP FUNCTION IF EXISTS public.fn_job_dead_letter_requeue_dry_run(uuid);
DROP FUNCTION IF EXISTS public.fn_job_dead_letter_triage_update(uuid, text, text, text);
-- Remove passive marker
DELETE FROM public.queue_heartbeat
WHERE executor_name = 'iu_outbound_default'
AND (metadata->>'marker') = 'legacy_silent_passive';
-- Remove new dot_config keys
DELETE FROM public.dot_config
WHERE key IN (
'queue.lease.reaper_enabled',
'queue.lease.reaper_dry_run_only',
'queue.dlq.replay_enabled',
'queue.runtime.phase'
);
-- Restore queue.heartbeat.enabled
UPDATE public.dot_config
SET value = 'false',
description = regexp_replace(description, ' \[Phase 2 enactment 2026-05-26:.*?\]', ''),
updated_at = now()
WHERE key = 'queue.heartbeat.enabled';
-- Verify rollback shape
DO $$
DECLARE v_q int; v_h int; v_fn int;
BEGIN
SELECT count(*) INTO v_q FROM public.dot_config WHERE key LIKE 'queue.%';
SELECT count(*) INTO v_h FROM public.queue_heartbeat;
SELECT count(*) INTO v_fn FROM information_schema.routines
WHERE routine_schema='public'
AND routine_name IN ('fn_queue_heartbeat_register_passive',
'fn_job_reap_stale_leases_dry_run',
'fn_job_reap_stale_leases_apply',
'fn_job_dead_letter_requeue_dry_run',
'fn_job_dead_letter_triage_update');
IF v_q <> 8 THEN RAISE EXCEPTION 'expected 8 queue.* keys after rollback, got %', v_q; END IF;
IF v_h <> 0 THEN RAISE EXCEPTION 'expected 0 queue_heartbeat rows after rollback, got %', v_h; END IF;
IF v_fn <> 0 THEN RAISE EXCEPTION 'expected 0 Phase 2 fns after rollback, got %', v_fn; END IF;
END $$;
COMMIT;
This restores the Phase-1-post state exactly.
Backup artifacts (custom format, pg_dump -Fc)
| Name | Path inside postgres container |
Size | Taken |
|---|---|---|---|
| Pre-Phase 2 | /tmp/dieu45_phase2_pre.dump |
82,970,890 B | 2026-05-26 11:43 UTC |
| Post-Phase 2 | /tmp/dieu45_phase2_post.dump |
83,187,232 B | 2026-05-26 13:56 UTC |
Restore command for emergency rollback:
docker exec postgres pg_restore -U workflow_admin -d directus --clean --if-exists /tmp/dieu45_phase2_pre.dump
(Strongly prefer the SQL rollback above for surgical rollback. Use pg_restore only if structural damage requires whole-DB restore — which would also clobber the +1,019 background event_outbox rows accumulated during the apply window.)
Migration source
The full migration script is at /tmp/dieu45_phase2_mig_051.sql on host and inside container (518 lines, ≈21.6 KB).
Effect of rollback on §15.5 silent gap
Rolling back re-hides the silent gap from v_queue_health and fn_queue_stale_check. The underlying iu_route_worker_cursor.last_run_at = 2026-05-22 11:31:41 does not change, but the heartbeat surface drops to empty. Rollback is not recommended unless the Phase 2 governance functions themselves are found defective. Otherwise the silent gap risk re-opens.