Generic Plugins
Generic plugins are reusable system components under praxist/plugins/** or another
explicit plugin root. A task project may also ship its own generic plugins under
<task_path>/.praxist/plugins/; those are discovered as source="task_project" only
when --task-path selects the task (see
Task Projects → Boundary Rules).
Plugin Manifest¶
Each plugin has a plugin.yaml manifest describing:
- plugin kind and name;
- version and stability;
- executable entrypoint or manifest-only contract;
- dependencies and compatibility;
- declared code/assets for replay hash coverage.
The plugin loader discovers candidates, resolves dependencies, checks source priority, and writes the selected plugin manifest into the run directory.
Stability as an interface contract
stability describes the interface contract a plugin promises — schema,
prompt shape, role-API backward compatibility — not whether the plugin is
trustworthy to execute. Trust comes from the plugin's source, gated via
TRUSTED_EXECUTION_SOURCES (bundled and task_project).
The two paths are gated differently:
- Bundled plugins must declare the kind's strict expected stability
(e.g.
v1_stableforpanel_topology,agent_runtime,workflow_stage). A bundled plugin affects every task project, so the contract there has to hold. - Task-local plugins under
<task>/.praxist/plugins/are scope-isolated to one task project and high-churn by design; they may declare anystabilityvalue (commonlyv0_experimental) without triggering the kind-mismatch check. Source trust is enough.
Minimal Executable Plugin¶
An executable plugin usually has this shape:
praxist/plugins/<kind_plural>/<name>/
plugin.yaml
adapter.py
README.md # optional, for complex plugins
plugin.yaml should declare the plugin ref, compatibility, entrypoint, and code
files that participate in source hashing. adapter.py should expose a small
factory or adapter object matching the kind-specific contract.
The plugin content hash covers the manifest and its declared code and assets. Imported modules or assets omitted from the manifest are outside that plugin content hash.
Do not name every implementation file plugin.py by habit. Use names that
describe the plugin's internal structure.
Plugin-Supplied Assets¶
Some plugin kinds accept assets shipped alongside the manifest, declared through dedicated manifest fields rather than ad-hoc paths.
panel_topology: a plugin may declaretopology.prompts_dirto ship its own Jinja prompt templates forBasePIandChairArbiter. The bundled prompts for the multi-agent Principal Investigator (PI) panel are used as a fallback for anything the plugin does not override. See Panel Topology Prompts.
When a plugin ships assets, declare the paths under code / assets
in plugin.yaml so they participate in replay source hashing.
What Belongs in a Plugin¶
Put code in a generic plugin when it is reusable across task projects:
- runtime adapters;
- API provider adapters;
- tool servers;
- workflow stages;
- graph maintainers;
- generic budget policies.
Do not put benchmark-specific research facts or task-local role contracts into a generic plugin. Those belong in the task project.
Decision Test¶
Before adding a plugin, ask whether two unrelated task projects could use it without copying task facts. If the answer is no, it probably belongs in a task project.
Before adding code to core, ask whether the behavior can be selected, replaced, or disabled through a plugin. If yes, it belongs in a plugin.
Plugin Tests¶
Plugin changes should add:
- plugin-local unit tests when the plugin has real code;
- kind-specific conformance tests under
tests/conformance/; - workflow smoke tests when the plugin participates in startup or run execution;
- replay/hash tests when plugin code or assets affect run reproducibility.
The default test path must not require real model keys, network, GPUs, or an external task checkout.