# `MobDev.Plugin.Manifest`
[🔗](https://github.com/genericjam/mob_dev/blob/master/lib/mob_dev/plugin/manifest.ex#L1)

Reads, validates, and classifies a plugin's `priv/mob_plugin.exs` manifest.

The manifest is data, not code (see [`MOB_PLUGINS.md`](https://github.com/GenericJam/mob/blob/master/MOB_PLUGINS.md)): a plain Elixir map
describing what a plugin contributes. This module is the single place that
turns that map into a validated, classified description — tier, hot-push
status, activation — that `mix mob.plugins` reports and the compile-time
merge will later consume.

A tier-0 plugin has no manifest at all; `load/1` returns `{:ok, nil}` for
that case, and `tier(nil)` is `0`.

# `hot_pushable`

```elixir
@spec hot_pushable(map() | nil) :: true | false | :partial
```

Whether the plugin's contributions can be hot-pushed without a native rebuild.

Computed from populated sections, not from tier: `true` for pure-Elixir
plugins, `false` for native-only ones (NIFs / components), and `:partial`
when a plugin mixes native code with hot-pushable Elixir (screens, lifecycle).

# `load`

```elixir
@spec load(Path.t()) :: {:ok, map() | nil} | {:error, String.t()}
```

Loads the manifest for a plugin checked out at `plugin_dir`.

Returns `{:ok, nil}` when there is no `priv/mob_plugin.exs` (a tier-0
plugin), `{:ok, map}` when one is present and evaluates to a map, or
`{:error, reason}` when the file is unreadable or doesn't yield a map.
Does not validate field contents — call `validate/1` for that.

# `manifest_present?`

```elixir
@spec manifest_present?(Path.t()) :: boolean()
```

Returns `true` when `plugin_dir` has a `priv/mob_plugin.exs`, `false`
otherwise. Distinguishes tier-0 plugins (no manifest, no signature
needed) from tier-1+ plugins (manifest present, signature required)
without evaluating the manifest itself. See `Verify.load_verified/1`.

# `nif_for_platform?`

```elixir
@spec nif_for_platform?(map(), :android | :ios | :all) :: boolean()
```

Whether the native build compiles this `nifs:` entry for `platform`
(`:android`, `:ios`, or `:all`) — the one platform rule every NIF consumer
shares: the build args, the driver_tab, and the MOB-281 native-build record.

An entry with no `:platform` is compiled on every platform; one tagged
`:ios`/`:android` only on that platform. `lang: :objc` is implicitly
Apple-only: Objective-C has no Android runtime, so an objc NIF authored
without an explicit `platform: :ios` must still be excluded from the Android
build args + driver_tab (otherwise zig tries to compile a `.m` source Android
cannot build).

# `nif_lang`

```elixir
@spec nif_lang(map()) :: atom()
```

The language of a `nifs:` entry. Defaults to `:c` so existing (haptic)
plugins are unaffected; `lang: :zig` etc. opt into other compile paths.

# `tier`

```elixir
@spec tier(map() | nil) :: 0..4
```

Classifies the plugin tier (0–4) from which capability sections are present.

Highest matching section wins. `nil` (no manifest) is tier 0. A manifest
with required fields but no capability sections is the tier-1 floor (the
"minimum viable manifest").

# `validate`

```elixir
@spec validate(map() | nil) :: {:ok, map() | nil} | {:error, [String.t()]}
```

Validates a manifest map against the spec's required top-level fields.

Returns `{:ok, manifest}` or `{:error, reasons}` with a list of every
problem found (validation never stops at the first error). `nil` (no
manifest) is valid — a tier-0 plugin has nothing to validate.

---

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