Skip to content

Plugin Lifecycle

Every plugin declares a lifecycle stage in its manifest.json. The stage tells users how stable to expect the plugin to be, and gives authors a sanctioned way to evolve or retire a plugin without breaking downstream tasks overnight.

The four stages, borrowed from the R tidyverse model, are a closed enum — free-text values like "beta" or "pre-release" are rejected at install time.

The four stages

StageWhat it promises
experimentalThe plugin is new or still being shaped. Interfaces and output schemas may change between versions without a migration path. Use at your own risk.
stableInterfaces and output schemas will not break within the same major version. Bug fixes and backwards-compatible additions are expected.
supersededA newer plugin does the job better. The superseded plugin still works, but new tasks should use the replacement.
deprecatedThe plugin is scheduled for removal. Still installable for now, but expect it to disappear in a future release.

Default: If a manifest omits lifecycle, the host treats the plugin as experimental. Declare a stage explicitly to make the promise deliberate.

Declaring a stage

Add one line to your manifest.json, alongside risk_profile:

{
"schema_version": "1",
"plugin_id": "com.example.my-plugin",
"version": "1.0.0",
"lifecycle": "stable"
}

For superseded or deprecated, name the replacement so users can act on the hint:

{
"lifecycle": "superseded",
"lifecycle_replacement": "com.example.my-plugin-v2"
}

lifecycle_replacement accepts a plugin_id, or a qualified plugin_id::endpoint_id if only one endpoint has been replaced.

How the UI shows it

  • Install dialog — a badge appears next to the version. For deprecated plugins a warning row is added. For superseded, the dialog shows "Superseded by {replacement}" with a link to the replacement’s registry entry.
  • Installed plugins panel — every row carries the badge and can be filtered by stage.
  • Registry browse view — the latest version’s stage is shown in the list; deprecated entries are hidden by default (toggleable).
  • Help Explorer — the per-endpoint column that used to be labelled “Stability” is now “Lifecycle” and uses the same badge.

Endpoint-level lifecycle

A plugin can be stable overall while one experimental endpoint is added ahead of the rest. In that case the endpoint declares its own stage in the help JSON it emits for --help <endpoint>:

{
"summary": "Generate predictions from a fitted model.",
"lifecycle": "experimental",
"arguments": [ ... ]
}

Absence of lifecycle in an endpoint’s help means the endpoint inherits the plugin-level stage. Unknown values (e.g. "beta", "Stable") are stripped at ingest — they behave the same as omission, so a typo falls back to the plugin default rather than rejecting the whole help payload.

This keeps the plugin stage honest: don’t promote a plugin to stable just because most endpoints are — mark the exception instead.

Where to put it

How you declare endpoint lifecycle depends on how your plugin produces help:

  • Static help files (e.g. static/help/<endpoint>.json, the pattern used by the bundled plugins) — add a top-level "lifecycle" key to the JSON file for that endpoint. Only the endpoints you want to override need it; omit the field everywhere else and they inherit the plugin stage.

  • Dynamic --help <endpoint> (SDK-based plugins using ocp_types::ExplainResponse) — set the lifecycle: Option<String> field on the response struct. The field is optional and skipped when None, so the default wire shape is unchanged.

Ingest only reads this field from the endpoint’s help response. You do not need to list endpoint stages anywhere in manifest.json.

Picking a stage

A quick heuristic:

  • Just shipped it, still iterating on the shape? experimental.
  • Signature frozen, breaking changes would require a major version bump? stable.
  • Replaced by another plugin but still in use? superseded, and point to the replacement.
  • About to remove it? deprecated, one release before removal.

A plugin typically moves experimental → stable once, and only later to superseded or deprecated. Moving backwards (e.g. stable → experimental) is allowed but should trigger a major version bump — downstream tasks relied on the previous promise.

Why the enum is closed

Free-text lifecycle fields drift immediately — "beta", "Beta", "wip", "pre-release" all mean roughly the same thing and break any UI that wants to render a consistent badge or filter. Adding a new stage means a schema bump and a host update; that’s deliberate — the ecosystem should agree on what a stage means before tools render it.