TL;DR: Something in your models returns a different value every time Django inspects it - usually a custom field whose deconstruct is non-deterministic, or a default evaluated fresh on each run. Run makemigrations with high verbosity to see exactly what it wants to change, find the unstable attribute, and make it deterministic. Do not keep applying the generated migrations; they change nothing and pollute history.

```text
django migration agent keeps generating the same auto-migration over and over because the model state never stabilizes
```

1. See what Django thinks changed: run `python manage.py makemigrations --dry-run --verbosity 3`.
   Expected: output names the specific field and operation (often an AlterField) it wants to generate.
2. Diff the generated migration against the previous identical one.
   Expected: the operations are the same each time - confirming the model state never stabilizes rather than real drift.
3. Inspect the flagged field's definition: look for a custom field, a default that builds new objects, or Meta options constructed dynamically.
   Expected: you find the unstable piece - for example a deconstruct method that includes a fresh value on each call.
4. Fix the field: make deconstruct deterministic (return the same values every time) or replace the unstable default with a stable one.
   Expected: re-running makemigrations --dry-run now reports "No changes detected".
5. Clean up: delete the pile of no-op generated migrations that were already applied, squashing if needed so history stays readable.
   Expected: a single makemigrations run produces nothing new.

## Use this when
- makemigrations generates the same migration repeatedly with no model changes
- an agent loop keeps creating and applying empty migrations
- dry-run shows phantom changes every time

## Not for this skill when
- the generated migrations contain real schema changes - those are legitimate
- the issue is a migration that fails to apply - that is an apply-time error
- you are intentionally writing data migrations - those are hand-written anyway

## Variant phrasings
- django makemigrations keeps detecting changes that are not there
- same auto-migration generated every run
- makemigrations never reports no changes detected

## Why it happens
Django's autodetector compares the current model state against the last migration state. If any field's deconstructed form differs run to run - a custom field embedding a new object identity, a default evaluated at import time, dynamically built choices - the comparison always finds a "change" and generates a migration for it. Applying that migration fixes nothing because the next comparison re-derives the same phantom difference.

## Edge cases
- Third-party fields are a common source - check their deconstruct implementation before assuming your code is at fault.
- Deleting the phantom migrations is safe only if they contain no real operations - read each one before removing.
- An agent that runs makemigrations on every loop iteration keeps regenerating until the field is fixed - fix the field, not the loop.
- After fixing, run the full test suite: a "harmless" phantom AlterField can still lock a big table when applied carelessly in production.

## Provenance

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