# `MontyPlugin`
[🔗](https://github.com/d-led/ex.monty.plugin/blob/main/lib/monty_plugin.ex#L1)

What a plugin tells a cluster about itself, and what a cluster may rely on.

A Monty run is assembled from modules: the experiment that drives it, the
learning modules it steps, the motor policies that decide where to sense next.
Not all of them live in the same node — a policy that reads a part's B-rep has
to run where the part was opened — so a cluster has to be able to ask a module
what it is before it decides where to call it.

That answer is `describe/0`, and this module is its contract: the three kinds,
the two placements, and `conforms?/1`, which says what is wrong with a
description rather than only that something is.

## The description

    %{
      "name" => "follow_chamfers",
      "kind" => :motor_policy,
      "version" => "0.1.0",
      "placement" => :owner_only,
      "needs" => [:quiddity]
    }

* `name` — the name the cluster registers it under, and the name a run asks
  for. Two plugins with one name on one node is a mistake the registry reports.
* `kind` — one of `kinds/0`. The kind says how often it is called: an experiment
  and a policy once per step, a learning module thousands of times, which is why
  a learning module is advertised rather than routed.
* `version` — the plugin's own version string, so a run's record says which
  decision-maker produced it.
* `placement` — `:anywhere` when any node may answer a call, `:owner_only` when
  a call has to reach the node that holds the state. A B-rep handle cannot leave
  its node, so anything that reads one is `:owner_only`.
* `needs` — what the plugin must be able to reach to answer at all, as atoms:
  `:quiddity` for the geometry, `:cadmpeg` for the file, `:monty` for the
  framework.

`MontyPlugin.Registry` carries the answer between nodes; nothing here knows
about processes.

# `description`

```elixir
@type description() :: %{
  optional(String.t()) =&gt; term(),
  required(String.t()) =&gt; term()
}
```

The map `describe/0` answers.

# `kind`

```elixir
@type kind() :: :experiment | :learning_module | :motor_policy
```

# `placement`

```elixir
@type placement() :: :anywhere | :owner_only
```

# `describe`

```elixir
@callback describe() :: description()
```

What a plugin says about itself.

# `conforms?`

```elixir
@spec conforms?(description()) :: :ok | {:error, String.t()}
```

Whether a description says enough for a cluster to use it.

`:ok`, or a sentence naming the first thing that is wrong — which is what a
generated plugin's test asserts, and what a person reads when a hand-written one
is refused.

# `kinds`

```elixir
@spec kinds() :: [kind()]
```

The kinds of plugin a run is assembled from.

# `placements`

```elixir
@spec placements() :: [placement()]
```

Where a plugin may be called: anywhere, or only where its state is.

---

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