# `Milvex.Migration.Operation`

Single unit of work exchanged between `Milvex.Migration.Plan`,
`Milvex.Migration.Runner`, and `Milvex.Migration.Reporter`.

An `Operation` describes one logical schema mutation (add/alter/drop a field,
create/alter/recreate/drop an index, add/alter/drop a function, alter
collection KV, create collection) plus the metadata needed to display, plan
releases for, and serialise it.

Categories:

  * `:additive`     — safe forward-compatible change (adds capability)
  * `:destructive`  — removes data or capability; may need release on older
                      Milvus versions
  * `:impossible`   — diff cannot be reconciled in-place; report only
  * `:descriptive`  — comment / description update; no schema effect

`requires_release` is derived from `(kind, milvus_version)` by
`requires_release?/2`. Modern Milvus (>= 2.6.0) is permissive: drops do not
require release. Older versions need release for field/function drops. Index
drops and recreations always require release.

# `category`

```elixir
@type category() :: :additive | :destructive | :impossible | :descriptive
```

# `kind`

```elixir
@type kind() ::
  :create_collection
  | :add_field
  | :alter_field
  | :drop_field
  | :description_change
  | :create_index
  | :drop_index
  | :recreate_index
  | :add_function
  | :alter_function
  | :drop_function
```

# `t`

```elixir
@type t() :: %Milvex.Migration.Operation{
  category: category(),
  collection_name: String.t(),
  kind: kind(),
  payload: map(),
  reason: String.t() | nil,
  requires_release: boolean()
}
```

# `build`

```elixir
@spec build(kind(), category(), String.t(), map(), String.t(), keyword()) :: t()
```

Builds an operation, deriving `requires_release` from `(kind, version)`.

Pass `reason: "..."` in `opts` to attach a human explanation, used for
`:impossible` ops in the reporter.

# `requires_release?`

```elixir
@spec requires_release?(kind(), String.t()) :: boolean()
```

Returns whether the given operation kind requires the collection to be
released before it can be applied on the given Milvus version.

  * `:drop_index`, `:recreate_index` — always true
  * `:drop_field`, `:drop_function`  — true only on Milvus < 2.6.0
  * everything else                  — false

# `to_line`

```elixir
@spec to_line(t()) :: iodata()
```

Renders the operation as one line of iodata for the text reporter.

The first character is a sigil derived from the category:

  * `:additive`    → `+`
  * `:destructive` → `-`
  * `:descriptive` → `~`
  * `:impossible`  → `!`

# `to_map`

```elixir
@spec to_map(t()) :: map()
```

Converts the operation to a JSON-serialisable map.

Each kind has a clause normalising its payload to a canonical shape:
field/index/function structs are projected to small maps containing only
identifying keys.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
