# description
Two shapes in one file: (1) the per-plugin manifest that authors ship as `plugin.json` (see `$defs/PluginManifest`); (2) the aggregate registry the implementation produces on disk (e.g. `~/.skill-map/plugins.json`), which lists all discovered plugins with their compat status. Both shapes are normative. camelCase keys throughout.

# $defs.PluginManifest.properties.id.description
Kebab-case, globally unique plugin identifier.

# $defs.PluginManifest.properties.specCompat.description
Semver range this plugin is compatible with (e.g. `^1.0.0`, `>=0.3.0 <0.4.0`). Checked via `semver.satisfies(specVersion, this)` at load time.

# $defs.PluginManifest.properties.extensions.description
Relative paths to extension files, resolved from the plugin directory.

# $defs.PluginManifest.properties.granularity.description
Toggle granularity for this plugin. `bundle` (default) — the plugin id is the only enable/disable key; the whole set of extensions follows the toggle. `extension` — each extension is independently toggle-able under its qualified id `<plugin-id>/<extension-id>`. Built-in bundles use the same field: the `claude` bundle is `bundle` (the Provider and its kind-aware extractors form a coherent provider); the `core` bundle is `extension` (every kernel built-in is removable per the spec promise that no extension is privileged). Plugin authors should keep the default unless their plugin ships several orthogonal capabilities a user might reasonably want piecemeal.

# $defs.PluginManifest.properties.storage.description
Persistence mode for this plugin. Absent = plugin does not persist state.

# $defs.PluginManifest.properties.storage.oneOf[].properties.mode.description
Shared `state_plugin_kvs` table, scoped by plugin id.

# $defs.PluginManifest.properties.storage.oneOf[].properties.schema.description
Optional. JSON Schema (path relative to plugin root) that validates the value on every `ctx.store.set(key, value)`. Absent = permissive (no validation, status quo). The kernel AJV-compiles the schema at load time; a missing or unparseable schema file surfaces as `load-error`.

# $defs.PluginManifest.properties.storage.oneOf[].properties.migrations.description
Relative paths to `.sql` migration files.

# $defs.PluginManifest.properties.storage.oneOf[].properties.schemas.description
Optional. Maps each declared table to a JSON Schema (path relative to plugin root) that validates rows on write. Tables not present here accept any shape (permissive, status quo). The kernel AJV-validates `ctx.store.write(table, row)` against the declared schema and throws on shape violation. A missing or unparseable schema file surfaces as `load-error`.

# $defs.DiscoveredPlugin.properties.status.description
Resolved state after discovery. `disabled` = user-disabled via config; `id-collision` = two plugins (any combination of project / global / --plugin-dir) declared the same `id`, both blocked, no precedence; others = automatic.

# $defs.DiscoveredPlugin.properties.statusReason.description
Human-readable detail when status is anything other than `enabled`.