Codumentor logo Codumentor

Plugin Dependencies

A plugin can declare ordered dependencies on other plugins. The loader resolves them topologically before falling back to the existing priority integer for tie-breaks within each layer.

See also: Plugin Distribution, Backend Hooks.


Why

Hook order is observable: an onContextReady handler that reads branch_overrides only sees a useful value if branch_switching has already populated it. Before this field, ordering was coupled to a single global priority: int shared by every plugin — collisions were silent and forced magic numbers.

Declaring dependencies: ["branch_switching"] makes the constraint explicit, self-documenting, and verified at load time.

Where to Declare

Two equivalent surfaces:

1. Distributable Plugin (plugin.yaml)

name: my_plugin
version: 1.0.0
has_backend: true
entry_module: my_plugin.plugin
entry_class: MyPlugin
priority: 50
dependencies:
  - branch_switching
  - permission_bridge

The dependencies field is part of PluginPackageManifest (src/codumentor/plugins/package.py). Default is [] — existing manifests need no change.

2. Built-in or Inline (meta)

class MyPlugin:
    meta = {
        "name": "my_plugin",
        "version": "1.0.0",
        "priority": 50,
        "dependencies": ["branch_switching"],
    }

The loader reads meta["dependencies"] for in-tree plugins. Same semantics as plugin.yaml.

3. Per-deployment Override (codumentor.yaml)

plugins:
  - module: codumentor.plugins.my_plugin
    class: MyPlugin
    dependencies: ["branch_switching"]

A dependencies entry in codumentor.yaml overrides whatever the plugin's meta (or external manifest) declared. Use this sparingly — it lets a deployment fix a load-order bug without rebuilding the plugin, but it also drifts from upstream.

Resolution Rules

  1. Topological sort first. Each plugin appears after every plugin it depends on, transitively.
  2. Priority within a layer. Plugins whose dependencies are all satisfied at the same point sort by priority integer (higher runs first), preserving the pre-existing behaviour for plugins that don't declare dependencies.
  3. Names match meta["name"]. A dependency string "branch_switching" must match another loaded plugin's meta["name"] (or external manifest name).

Failure Modes

The loader fails fast at startup with a clear error:

SituationError
Declared dependency is not loadedPlugin 'my_plugin' declares dependency 'foo', which is not among the loaded plugins.
Dependency graph has a cyclePlugin dependency cycle detected: a -> b -> a

If the missing plugin is optional, use a soft check at runtime instead of declaring it as a dependency. For example, peek at ctx["scratchpad"] for a key the other plugin populates, and degrade gracefully when absent.

When Not to Use

file_links will eventually merge with the permalink rewrite pipeline so locally-modified or generated files get correct download links. That merger consumes branch_overrides from branch_switching, so the merged plugin will declare:

meta = {
    "name": "file_links",
    "dependencies": ["branch_switching"],
}

Today file_links runs alone with dependencies: [] because the consumer code isn't wired up yet — declaring it now would force every selfdev config (which often runs without branch_switching) to error at load. The loader treats missing dependencies as fatal, so add the entry only when the consumer code is in place.