# agent scheduled migration ran twice: double-applied

## TL;DR

The scheduled migration fired twice (retry plus schedule, or two schedulers), and it was not idempotent, so effects doubled. Make migrations idempotent first; then dedupe the trigger. Double-run is a matter of time; idempotency is the defense.

```text
agent scheduled migration ran twice: double-applied
```

## Steps

1. Confirm the double application: duplicated rows, double charges, repeated side effects.
   Expected: The doubling is proven.

2. Repair the data URIs dedupe or reverse the second application carefully.
   Expected: The state is correct.

3. Make the migration idempotent: re-running it must be a no-op.
   Expected: The class of bug is fixed.

4. Dedupe the trigger: one scheduler, retry-aware, with run locking.
   Expected: Double fires stop.

5. Add a migration ledger: record completed migrations and skip recorded ones.
   Expected: The ledger is the backstop.

## When to use

- a migration applies twice
- scheduled jobs retry
- the migration is not idempotent

## When not to use

- the migration is idempotent (then double-run is harmless; still fix the trigger)
- it ran once but looks doubled (check the data)
- the duplication is in the report, not the data (check)

## Tool compatibility

- any scheduled migration
- idempotency patterns

## Variant phrasings

- "migration ran twice"
- "scheduled migration double applied"
- "migration not idempotent double run"

## Why it happens

Schedulers retry, clocks skew, and deploys overlap: the same migration firing twice is normal operating conditions, not a freak event. A non-idempotent migration turns 'twice' into 'corrupted'. Idempotency is the primary fix; trigger dedup is secondary. The ledger makes both verifiable.

## Edge cases

- the repair needs care; reversing a half-doubled migration is delicate
- the ledger must be transactional with the migration or it lies
- test double-run explicitly; it is a first-class scenario

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_ERkzUk6M6ZoKq_61x59MuQ
