# 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.

