# Trade Lifecycle Integrity - Migration Plan Date: 2026-02-15 Owner: Backend trade service ## Goal Enforce deterministic lifecycle tracing by `profile_id + trade_id` across `orders`, `trade_history`, and runtime reconciliation, without blocking production traffic. ## Null Handling Policy - `trade_id` may be `NULL` only for legacy/manual rows. - Empty or whitespace `trade_id` values are normalized to `NULL`. - Bot-originated lifecycle records must use deterministic `trade_id` (`TRD-*`). ## Rollout Plan ### Phase A - Prepare (No Risk) - [ ] Snapshot current row counts and distinct trade IDs: ```sql SELECT COUNT(*) AS orders_total, COUNT(*) FILTER (WHERE trade_id IS NULL OR btrim(trade_id) = '') AS orders_missing_trade_id FROM orders; SELECT COUNT(*) AS history_total, COUNT(*) FILTER (WHERE trade_id IS NULL OR btrim(trade_id) = '') AS history_missing_trade_id FROM trade_history; ``` - [ ] Run legacy backfill in dry-run mode. - [ ] Confirm no unacceptable duplicate lifecycle keys in reporting. ### Phase B - Deploy Non-Blocking Guardrails - [ ] Apply `schema/007_trade_lifecycle_integrity_constraints.sql`. - [ ] Verify indexes exist: ```sql SELECT indexname FROM pg_indexes WHERE tablename IN ('orders', 'trade_history') AND indexname LIKE 'idx_%trade_id%'; ``` - [ ] Confirm constraints are present and `NOT VALID`: ```sql SELECT conname, convalidated FROM pg_constraint WHERE conname IN ( 'chk_orders_action_lifecycle', 'chk_orders_trade_id_not_blank', 'chk_orders_trade_id_format', 'chk_trade_history_trade_id_not_blank', 'chk_trade_history_trade_id_format' ); ``` ### Phase C - Backfill + Reconcile - [ ] Run deterministic `trade_id` backfill in apply mode. - [ ] Run lifecycle reconciliation report (`orders -> positions -> history`) per profile. - [ ] Resolve every `missing_entry_order` / `orphan_exit` anomaly before validation. ### Phase D - Tighten Constraints - [ ] Validate constraints after reconciliation is clean: ```sql ALTER TABLE orders VALIDATE CONSTRAINT chk_orders_action_lifecycle; ALTER TABLE orders VALIDATE CONSTRAINT chk_orders_trade_id_not_blank; ALTER TABLE orders VALIDATE CONSTRAINT chk_orders_trade_id_format; ALTER TABLE trade_history VALIDATE CONSTRAINT chk_trade_history_trade_id_not_blank; ALTER TABLE trade_history VALIDATE CONSTRAINT chk_trade_history_trade_id_format; ``` ## Rollback Plan - Drop newly added constraints (if validation reveals regression): ```sql ALTER TABLE orders DROP CONSTRAINT IF EXISTS chk_orders_action_lifecycle; ALTER TABLE orders DROP CONSTRAINT IF EXISTS chk_orders_trade_id_not_blank; ALTER TABLE orders DROP CONSTRAINT IF EXISTS chk_orders_trade_id_format; ALTER TABLE trade_history DROP CONSTRAINT IF EXISTS chk_trade_history_trade_id_not_blank; ALTER TABLE trade_history DROP CONSTRAINT IF EXISTS chk_trade_history_trade_id_format; ``` - Keep indexes unless they materially impact write throughput. - Re-run reconciliation report and keep bot runtime lifecycle guards active.