Phase 2 — Bounded BEGIN/ROLLBACK Proof Results
Bounded BEGIN/ROLLBACK Proof Results
Setup
- Channel: SSH contabo →
docker exec postgres→psql workflow_admin@directus - File:
/tmp/dieu45_phase2_proof.sql - Strategy: full proof executed inside one
BEGIN; … ROLLBACK;block. Durable rows (passive marker, new dot_config keys, new fns) created by mig 051 already in place BEFORE the proof TX and survive ROLLBACK.
Pre-proof state (outside TX, already-applied Phase 2)
| Object | Rows |
|---|---|
job_queue |
0 |
job_dead_letter |
0 |
queue_heartbeat |
1 (passive marker) |
| dot_config key | Value |
|---|---|
queue.job_substrate.enabled |
false |
queue.lease.reaper_enabled |
false |
queue.lease.reaper_dry_run_only |
true |
queue.worker.enabled |
false |
Step-by-step proof outcomes (all PASS)
Step 0 — Bounded gate flip inside TX
UPDATE dot_config flips inside TX:
queue.job_substrate.enabled = truequeue.lease.reaper_enabled = truequeue.lease.reaper_dry_run_only = false
All three reverted by ROLLBACK.
Step 1 — Enqueue 3 jobs
Three fn_job_enqueue calls created phase2_proof_retryable, phase2_proof_dlq_bound, phase2_proof_ack — all state='queued', attempts=0, max_attempts=5, distinct idempotency keys.
Step 2 — Claim 3 jobs
fn_job_claim(p_lease_owner='phase2_proof_worker_A', p_limit=3) → all 3 transitioned queued → leased with lease_owner and lease_until = now() + 300s.
Step 3 — Simulate stale leases
UPDATE … SET lease_until = now() - interval '10 minutes' on the 2 jobs to be reaped. phase2_proof_dlq_bound also had attempts := 4 preloaded so attempts+1=5=max_attempts.
Step 4 — Dry-run reaper
fn_job_reap_stale_leases_dry_run() returned:
{"stale_lease_count":2,"limit":100,"mutation":false,"evaluated_at":"…",
"jobs":[
{"job_id":"5f2a…","attempts":0,"job_kind":"phase2_proof_retryable","overdue_seconds":600,"would_action":"reset_to_retry_waiting","max_attempts":5,…},
{"job_id":"4f1e…","attempts":4,"job_kind":"phase2_proof_dlq_bound","overdue_seconds":600,"would_action":"move_to_dead_letter","max_attempts":5,…}
]}
Step 5 — Apply reaper
fn_job_reap_stale_leases_apply(p_actor='phase2_proof_reaper') returned:
{"refused":false,"reset_count":1,"dead_letter_count":1,"actor":"phase2_proof_reaper",
"actions":[
{"action":"reset_to_retry_waiting","job_id":"5f2a…","attempts":1,"backoff_sec":10,"scheduled_at":"…"},
{"action":"moved_to_dead_letter","job_id":"4f1e…","attempts":5,
"dead_letter":{"dead_letter_id":"9a4f…","state":"dead_letter","refused":false,…}}
]}
job_queue after reap:
| job_kind | state | attempts | last_error |
|---|---|---|---|
| phase2_proof_ack | leased | 0 | (null) |
| phase2_proof_dlq_bound | dead_letter | 5 | lease_expired_reaped_at_max_attempts |
| phase2_proof_retryable | retry_waiting | 1 | lease_expired_reaped |
job_dead_letter after reap: 1 row, triage_status='pending', attempts=5.
Step 6 — Triage update
fn_job_dead_letter_triage_update(…, p_triage_status='acknowledged', p_triage_note='phase2 bounded proof triage', p_actor='phase2_proof_triage') succeeded, transitioning pending → acknowledged.
Step 7 — DLQ requeue dry-run (gate off)
fn_job_dead_letter_requeue_dry_run(…) returned with replay_gate_enabled=false and would_action="refused: queue.dlq.replay_enabled=false". idempotency_collision.blocked=false.
Step 8 — Ack non-stale leased job
fn_job_ack(job_id=<ack job>, lease_owner='phase2_proof_worker_A', summary='…') returned {state:'succeeded', finished_at:…}. Confirms non-stale path intact.
Step 9 — Heartbeat tick
fn_queue_heartbeat_tick('phase2_proof_executor','external_worker','ok','{"phase":"phase2_proof"}') returned {ticks_total:1, last_tick_status:'ok', refused:false, …}. queue_heartbeat now has 2 rows in TX.
Step 10 — Stale check classification
v_queue_health: executors_fresh=1, executors_warning=0, executors_stale=1. fn_queue_stale_check(p_threshold_seconds=300): stale_count=1 (the legacy iu_outbound_default marker), phase2_proof_executor not stale.
Step 11 — Refusal proofs (7 negative tests)
| # | Test | Refusal reason |
|---|---|---|
| 11a | fn_queue_heartbeat_register_passive(executor='iu_outbound_default'…) |
already_registered |
| 11b | fn_job_dead_letter_triage_update(…, 'bogus_status', …) |
invalid_triage_status (with allowed list returned) |
| 11c | fn_job_dead_letter_triage_update(…, 'pending', actor='') |
actor_required |
| 11d | apply reaper with reaper_dry_run_only=true toggled |
queue.lease.reaper_dry_run_only=true |
| 11e | apply reaper with reaper_enabled=false toggled |
queue.lease.reaper_enabled=false |
| 11f | requeue_dry_run('00000000-…') |
dead_letter_not_found |
| 11g | fn_job_enqueue with payload_json='{"vector":[…]}' |
CHECK violation job_queue_payload_safe_check (denylist key vector blocked) |
ROLLBACK
All proof TX work reverted. Post-ROLLBACK snapshot:
| Object | Rows |
|---|---|
job_queue |
0 |
job_dead_letter |
0 |
queue_heartbeat |
1 (passive marker survives) |
| dot_config key | Value |
|---|---|
queue.job_substrate.enabled |
false ✓ restored |
queue.lease.reaper_enabled |
false ✓ restored |
queue.lease.reaper_dry_run_only |
true ✓ restored |
queue.worker.enabled |
false |
Passive marker executor_name='iu_outbound_default', last_tick_at='2026-05-22 11:31:41.928657+00', last_tick_status='warn' — unchanged by ROLLBACK because it was committed as part of mig 051 BEFORE the proof TX opened.
Verdict
11/11 proof steps PASS. No durable mutation by the proof itself. All gate refusals correct. All payload-denylist CHECKs intact. Lease reaper exercise covers both branches (reset and DLQ). Triage workflow functional. Heartbeat protocol armed and functional. Silent gap surfaces deterministically.