Orchestrata Ultimate / Specs / 015-plugin-sdk
015-plugin-sdk: Plugin SDK
The Plugin SDK is the extensibility framework for Orchestrata. It defines a public API surface and runtime for third-party and first-party plugins that extend the system with new Volk types, tools, LLM providers, channel adapters, governance rules, and knowledge graph extensions — all installable and loadable at runtime without modifying the monorepo or rebuilding the system.
At a glance
| Item | Value |
|---|---|
| Spec directory | specs/015-plugin-sdk/ |
| Status line in the spec | Implemented |
| Requirement IDs defined | 210 |
With a @traces citation in src/ |
95 (45.2%) |
With a @traces citation in tests/ |
0 (0.0%) |
| Tracked files | 6 |
| RTM page | 015-plugin-sdk |
Reading this spec
The Plugin SDK is the extensibility framework of Orchestrata. It defines a public API and runtime for first-party and third-party plugins that add new Volk types, tools, LLM providers, channel adapters, governance rules and knowledge graph extensions, installable and loadable at runtime without modifying the monorepo. The problem it solves is that the system's packages are tightly coupled workspace dependencies, all components are imported statically at startup, and the existing Volk types are hardcoded.
The scope is twelve user stories: installing and loading a plugin, developing Volk, tool and provider plugins, discovery and listing, configuration, isolation and security, and lifecycle management (priority 1), then governance rule plugins, knowledge graph extension plugins, event hooks and channel adapter plugins (priority 2). Requirement IDs use the REQ-015- prefix.
The key decisions are an IPluginManager interface registered as ComponentType.PLUGIN_MANAGER; plugins as Python packages with a plugin.yaml manifest declaring capabilities, dependencies and entry points; discovery by a local directory scan plus pip-installed packages in the orchestrata_plugin entry point group; and a lifecycle of discover, validate, load, initialize, start, stop and unload. The SDK exposes IPlugin, PluginContext and registration of tools, Volks, providers and governance rules. PAM++ governance controls which plugins are allowed, what they can access and their resource limits, and all plugin operations are audit logged.
In the code, the package is orchestrata_ultimate/plugin. The largest tracers are orchestrata_ultimate/plugin/interfaces, orchestrata_ultimate/plugin/manager, orchestrata_ultimate/plugin/sdk, orchestrata_ultimate/plugin/context and orchestrata_ultimate/plugin/discovery. The per-requirement view is on the RTM page.
The spec's status line reads Implemented. It tracks 6 files and defines 210 requirement IDs. The full text follows below.
Measured coverage is 95 of 210 requirements cited by at least one @traces in src/ (45.2%), spread over 29 modules; the module citing the most of them is orchestrata_ultimate/plugin/interfaces (35 requirements). In tests/, 0 requirements are cited by a test-side @traces.
Coverage gaps: 115 requirements have no citing symbol. The largest untraced groups are REQ-015-* (115). Untraced means no @traces names the ID; the behaviour may still exist in code that was never annotated. Test-side citations are thinner than code-side ones (0 against 95).
Requirements
Checkbox is the state recorded in the requirements checklist; Traced by is the count of src/ symbols whose @traces names the ID.
| ID | Done | Requirement | Traced by |
|---|---|---|---|
REQ-015-001 |
no | Install plugin from pip package spec (orchestrata plugins install orchestrata-plugin-browser) |
1 |
REQ-015-002 |
no | Install plugin from local directory path (orchestrata plugins install ./my-plugin) |
4 |
REQ-015-003 |
no | Install plugin from git URL (orchestrata plugins install git+https://...) |
4 |
REQ-015-004 |
no | Installation validates manifest before completing | 3 |
REQ-015-005 |
no | Installation checks Python dependency compatibility | none |
REQ-015-006 |
no | Discover plugins from configurable local directories | 5 |
REQ-015-007 |
no | Discover plugins via importlib.metadata entry point group orchestrata_plugin |
2 |
REQ-015-008 |
no | Discover plugins from pip packages with orchestrata-plugin- prefix |
1 |
REQ-015-009 |
no | Deduplicate discovered plugins by name (local takes precedence) | 2 |
REQ-015-010 |
no | Log all discovered plugins with source and version | none |
REQ-015-011 |
no | Load plugin Python module via importlib | 3 |
REQ-015-012 |
no | Instantiate plugin entry point class from manifest | none |
REQ-015-013 |
no | Pass PluginContext to plugin on instantiation | none |
REQ-015-014 |
no | Load plugins in dependency order (topological sort) | 1 |
REQ-015-015 |
no | Detect and reject circular plugin dependencies | 1 |
REQ-015-016 |
no | Plugin calls context.register_volk(alias, capabilities, handler) |
4 |
REQ-015-017 |
no | Registered Volk appears in kernel Volk registry | 11 |
REQ-015-018 |
no | Registered Volk is routable via CXP packets | 1 |
REQ-015-019 |
no | Volk alias validated for uniqueness (conflict detection) | 1 |
REQ-015-020 |
no | Volk alias cannot override core Volks (U:Ox, U:PS, U:KR reserved) | 1 |
REQ-015-021 |
no | Plugin Volk receives CXP packets via handler function | 6 |
REQ-015-022 |
no | Plugin Volk responds via standard CXP protocol | 1 |
REQ-015-023 |
no | Plugin Volk participates in PAM++ governance | none |
REQ-015-024 |
no | Plugin Volk has access to PluginContext for DI resolution | none |
REQ-015-025 |
no | Plugin Volk can declare capabilities (reasoning, coding, etc.) | 1 |
REQ-015-026 |
no | Plugin Volks auto-deregistered on plugin stop | none |
REQ-015-027 |
no | In-flight CXP packets to plugin Volk handled gracefully on stop | none |
REQ-015-028 |
no | Plugin Volk health monitored by kernel health system | none |
REQ-015-029 |
no | Plugin Volk errors isolated from other Volks | none |
REQ-015-030 |
no | Plugin Volk registration logged to audit trail | none |
REQ-015-031 |
no | Plugin calls context.register_tool(name, description, schema, handler) |
5 |
REQ-015-032 |
no | Tool name namespaced: plugin.<plugin_name>.<tool_name> |
none |
REQ-015-033 |
no | Tool parameters validated against JSON Schema on invocation | none |
REQ-015-034 |
no | Tool registered in central tool registry accessible to all Volks | 2 |
REQ-015-035 |
no | Tool name validated for uniqueness | none |
REQ-015-036 |
no | Tool handler invoked asynchronously with validated parameters | none |
REQ-015-037 |
no | Tool results returned to calling Volk via CXP | none |
REQ-015-038 |
no | Tool invocation authorized by PAM++ before execution | none |
REQ-015-039 |
no | Tool execution timeout enforced (configurable per tool) | none |
REQ-015-040 |
no | Tool errors returned as structured error responses | none |
REQ-015-041 |
no | Plugin tools auto-deregistered on plugin stop | none |
REQ-015-042 |
no | In-flight tool invocations completed or cancelled on plugin stop | none |
REQ-015-043 |
no | Tool invocation count tracked per plugin | none |
REQ-015-044 |
no | Tool execution time tracked per invocation | none |
REQ-015-045 |
no | Tool registration logged to audit trail | none |
REQ-015-046 |
no | Plugin calls context.register_provider(name, provider, config) |
3 |
REQ-015-047 |
no | Provider implements IProvider protocol (SPEC-011) | none |
REQ-015-048 |
no | Provider registered with IProviderManager via DI | 1 |
REQ-015-049 |
no | Provider participates in fallback chains | none |
REQ-015-050 |
no | Provider participates in cost tracking and rate limiting | none |
REQ-015-051 |
no | Provider config loaded from plugin config section | none |
REQ-015-052 |
no | Provider API keys loaded from environment variables | none |
REQ-015-053 |
no | Provider model metadata declared (capabilities, pricing, context window) | none |
REQ-015-054 |
no | Provider rate limits declared in config | none |
REQ-015-055 |
no | Provider health monitored by ProviderManager | 2 |
REQ-015-056 |
no | Plugin providers auto-deregistered on plugin stop | none |
REQ-015-057 |
no | In-flight provider requests completed or cancelled on stop | 2 |
REQ-015-058 |
no | Provider deregistration triggers fallback chain update | none |
REQ-015-059 |
no | Provider registration logged to audit trail | none |
REQ-015-060 |
no | Provider errors isolated from other providers | none |
REQ-015-061 |
no | Plugin calls context.register_rules(rule_files) |
2 |
REQ-015-062 |
no | Rule files are relative paths within plugin directory | none |
REQ-015-063 |
no | Rules loaded into Prolog knowledge base on initialize | none |
REQ-015-064 |
no | Rules unloaded from Prolog knowledge base on stop | none |
REQ-015-065 |
no | Rule predicate conflicts detected and reported | none |
REQ-015-066 |
no | Plugin rules cannot override core PAM++ predicates | none |
REQ-015-067 |
no | Plugin rules validated against constitutional articles | none |
REQ-015-068 |
no | Plugin rules namespaced to prevent collisions | none |
REQ-015-069 |
no | Rule loading failures do not crash Prolog engine | none |
REQ-015-070 |
no | Rule audit trail records loaded/unloaded predicates | none |
REQ-015-071 |
no | Plugin calls context.register_kg_schema(node_types, rel_types, indexes) |
3 |
REQ-015-072 |
no | Node types namespaced with plugin prefix: Plugin_<name>_<NodeType> |
none |
REQ-015-073 |
no | Schema applied to Neo4j on plugin initialize | none |
REQ-015-074 |
no | Schema migrations versioned per plugin | 1 |
REQ-015-075 |
no | Schema conflicts with core or other plugins detected | none |
REQ-015-076 |
no | Plugin can create nodes of its declared types | none |
REQ-015-077 |
no | Plugin can create relationships of its declared types | none |
REQ-015-078 |
no | Plugin KG operations scoped to its namespace | none |
REQ-015-079 |
no | Core KG nodes not modifiable by plugins | none |
REQ-015-080 |
no | KG schema extension logged to audit trail | none |
REQ-015-081 |
no | orchestrata plugins list shows all plugins with status |
none |
REQ-015-082 |
no | List output includes: name, version, status, capabilities, source | 1 |
REQ-015-083 |
no | orchestrata plugins info <name> shows full manifest details |
none |
REQ-015-084 |
no | Info output includes: registered components, config schema, resource usage | 1 |
REQ-015-085 |
no | orchestrata plugins enable <name> activates a disabled plugin |
4 |
REQ-015-086 |
no | orchestrata plugins disable <name> deactivates without uninstalling |
4 |
REQ-015-087 |
no | Disable stops the plugin and deregisters its components | 1 |
REQ-015-088 |
no | orchestrata plugins uninstall <name> removes completely |
5 |
REQ-015-089 |
no | Uninstall cleans up Redis state, Neo4j audit, and files | none |
REQ-015-090 |
no | Plugin status visible via Gateway RPC plugins.list (SPEC-014) |
none |
REQ-015-091 |
no | orchestrata plugins doctor <name> runs health check on plugin |
none |
REQ-015-092 |
no | Doctor checks: manifest validity, dependencies, config, capability access | none |
REQ-015-093 |
no | Doctor reports errors with remediation suggestions | none |
REQ-015-094 |
no | Error plugins include error message and stack trace in listing | 1 |
REQ-015-095 |
no | Plugin logs accessible via orchestrata plugins logs <name> |
none |
REQ-015-096 |
no | Plugin config loaded from configs/monolithic.yaml under plugins.<name> |
1 |
REQ-015-097 |
no | Config validated against schema declared in manifest (JSON Schema) | none |
REQ-015-098 |
no | Environment variable interpolation supported in plugin config | none |
REQ-015-099 |
no | Config accessible via PluginContext.config |
1 |
REQ-015-100 |
no | Invalid config prevents plugin load with validation error | 1 |
REQ-015-101 |
no | Default config values from manifest applied when keys missing | none |
REQ-015-102 |
no | Config changes detectable for hot-reload notification | none |
REQ-015-103 |
no | Plugin notified of config changes via on_config_changed() callback |
none |
REQ-015-104 |
no | New config validated before applying (rollback on rejection) | none |
REQ-015-105 |
no | Config changes logged to audit trail | none |
REQ-015-106 |
no | Plugins declare required capabilities in manifest | none |
REQ-015-107 |
no | DI container scoped to declared capabilities only | 1 |
REQ-015-108 |
no | Undeclared capability access raises CapabilityDeniedError | 2 |
REQ-015-109 |
no | Capability access logged for security audit | none |
REQ-015-110 |
no | Capability types: di_access, redis, neo4j, network, filesystem, prolog, event_bus, provider, gateway | 1 |
REQ-015-111 |
no | Prolog queried for plugin_allowed(name) before loading |
none |
REQ-015-112 |
no | Prolog queried for plugin_capability_granted(name, cap) per capability |
none |
REQ-015-113 |
no | Plugin allowlist configurable in monolithic.yaml | 1 |
REQ-015-114 |
no | Plugin blocklist overrides allowlist | 1 |
REQ-015-115 |
no | Governance decisions logged to audit trail | 1 |
REQ-015-116 |
no | Memory usage tracked per plugin | 2 |
REQ-015-117 |
no | API call count tracked per time window | 2 |
REQ-015-118 |
no | Events emitted tracked per time window | 2 |
REQ-015-119 |
no | Resource limits configurable per plugin in monolithic.yaml | 2 |
REQ-015-120 |
no | Plugin throttled or disabled on resource limit breach | 1 |
REQ-015-121 |
no | Plugin exceptions caught and logged (never crash core) | 1 |
REQ-015-122 |
no | Plugin marked "error" after consecutive failures (configurable threshold) | 2 |
REQ-015-123 |
no | Auto-disable on persistent error state (configurable) | 1 |
REQ-015-124 |
no | Other plugins unaffected by one plugin's failure | none |
REQ-015-125 |
no | Plugin errors include full traceback and plugin context | none |
REQ-015-126 |
no | Lifecycle order: discover → validate → load → initialize → start | 2 |
REQ-015-127 |
no | Shutdown order: stop → unload (reverse dependency order) | 2 |
REQ-015-128 |
no | initialize() called after load, before any other method | 2 |
REQ-015-129 |
no | start() called after all plugins initialized | 3 |
REQ-015-130 |
no | stop() called on shutdown or disable | 5 |
REQ-015-131 |
no | Plugin declares dependencies on other plugins in manifest | none |
REQ-015-132 |
no | Load order computed via topological sort on dependency graph | 1 |
REQ-015-133 |
no | Circular dependencies detected and rejected with clear error | 1 |
REQ-015-134 |
no | Missing dependency prevents plugin load with clear error | 1 |
REQ-015-135 |
no | Dependency version constraints validated | none |
REQ-015-136 |
no | Hot-reload executes: stop → unload → load → initialize → start | 1 |
REQ-015-137 |
no | Hot-reload preserves other plugins' state | none |
REQ-015-138 |
no | Hot-reload re-registers all plugin components | none |
REQ-015-139 |
no | Hot-reload validates manifest changes before applying | none |
REQ-015-140 |
no | Hot-reload logged to audit trail | none |
REQ-015-141 |
no | All lifecycle transitions emit events to EventBus | none |
REQ-015-142 |
no | Events: plugin.discovered, plugin.loaded, plugin.initialized, plugin.started, plugin.stopped, plugin.unloaded, plugin.error | none |
REQ-015-143 |
no | Events include plugin name, version, and transition details | none |
REQ-015-144 |
no | All lifecycle events logged to Neo4j audit trail | none |
REQ-015-145 |
no | Gateway subscribers receive plugin lifecycle events (SPEC-014) | none |
REQ-015-146 |
no | Plugin calls context.subscribe_event(topic, handler) |
3 |
REQ-015-147 |
no | Subscription supports wildcard topics (cxp.*, session.*) |
1 |
REQ-015-148 |
no | Subscriptions auto-unsubscribed on plugin stop | none |
REQ-015-149 |
no | Subscription returns subscription_id for manual unsubscribe | 2 |
REQ-015-150 |
no | Event handler receives full event payload | 1 |
REQ-015-151 |
no | Plugin calls context.emit_event(event_type, payload) |
3 |
REQ-015-152 |
no | Custom events namespaced: plugin.<name>.<event_type> |
1 |
REQ-015-153 |
no | Custom events visible to other plugins and gateway subscribers | 1 |
REQ-015-154 |
no | Event emission rate tracked per plugin (resource limit) | 1 |
REQ-015-155 |
no | Event handler errors do not crash EventBus or other subscribers | 2 |
REQ-015-156 |
no | Plugin calls context.register_channel(channel_id, inbound, outbound) |
3 |
REQ-015-157 |
no | Channel registered with kernel channel registry | 2 |
REQ-015-158 |
no | Channel_id validated for uniqueness | 1 |
REQ-015-159 |
no | Inbound handler converts external messages to CXP packets | none |
REQ-015-160 |
no | Outbound handler converts CXP packets to channel-native messages | 1 |
REQ-015-161 |
no | Inbound messages routed through kernel like any CXP packet | 1 |
REQ-015-162 |
no | Outbound messages dispatched to channel via outbound handler | none |
REQ-015-163 |
no | Channel configuration loaded from plugin config (tokens, webhooks) | none |
REQ-015-164 |
no | Channel health monitored by kernel health system | none |
REQ-015-165 |
no | Channel errors isolated from other channels | none |
REQ-015-166 |
no | Plugin channels auto-deregistered on plugin stop | none |
REQ-015-167 |
no | In-flight channel messages handled gracefully on stop | none |
REQ-015-168 |
no | Channel registration logged to audit trail | 1 |
REQ-015-169 |
no | Channel message throughput tracked per plugin | 1 |
REQ-015-170 |
no | Channel reconnection handled by plugin (not core) | 1 |
REQ-015-171 |
no | SDK published as orchestrata-plugin-sdk pip package |
none |
REQ-015-172 |
no | SDK includes: IPlugin, PluginContext, PluginManifest, PluginCapability | 2 |
REQ-015-173 |
no | SDK includes type stubs for all public interfaces | 1 |
REQ-015-174 |
no | SDK is version-pinned to Orchestrata compatibility range | none |
REQ-015-175 |
no | SDK has zero runtime dependencies on Orchestrata core (interface-only) | 1 |
REQ-015-176 |
no | orchestrata plugins scaffold <name> generates plugin template |
none |
REQ-015-177 |
no | Template includes: plugin.yaml, pyproject.toml, src/, tests/, README.md | none |
REQ-015-178 |
no | Template includes working example (minimal Volk or tool) | 1 |
REQ-015-179 |
no | SDK documentation with examples for each registration type | none |
REQ-015-180 |
no | Plugin testing utilities provided (test harness, mock context) | none |
REQ-015-181 |
no | SDK API follows semantic versioning | 1 |
REQ-015-182 |
no | Breaking changes require major version bump | 1 |
REQ-015-183 |
no | Deprecation warnings for API changes | 1 |
REQ-015-184 |
no | SDK changelog maintained | 1 |
REQ-015-185 |
no | Compatibility matrix published (SDK version ↔ Orchestrata version) | 1 |
REQ-015-186 |
no | IPluginManager registered with ComponentType.PLUGIN_MANAGER | none |
REQ-015-187 |
no | IPluginManager resolvable from DI container | none |
REQ-015-188 |
no | PluginManager implements IComponent lifecycle | 1 |
REQ-015-189 |
no | PluginManager initializes after kernel and before Volks | none |
REQ-015-190 |
no | PluginManager stops before kernel on shutdown | none |
REQ-015-191 |
no | list_plugins() returns all plugins with status |
4 |
REQ-015-192 |
no | get_plugin(name) returns plugin info or None |
4 |
REQ-015-193 |
no | load_plugin(name) loads and initializes a discovered plugin |
5 |
REQ-015-194 |
no | unload_plugin(name) stops and unloads a plugin |
4 |
REQ-015-195 |
no | reload_plugin(name) hot-reloads a plugin |
4 |
REQ-015-196 |
no | Query plugins by status (active, disabled, error) | 1 |
REQ-015-197 |
no | Query plugins by capability (which plugins provide network access?) | none |
REQ-015-198 |
no | Query registered components by plugin (which Volks does plugin X provide?) | none |
REQ-015-199 |
no | Query plugin resource usage | none |
REQ-015-200 |
no | Query plugin health | 1 |
REQ-015-201 |
no | Plugin install/uninstall events recorded | none |
REQ-015-202 |
no | Plugin lifecycle events recorded (load, init, start, stop, unload) | none |
REQ-015-203 |
no | Plugin registration events recorded (volk, tool, provider, channel, rules) | none |
REQ-015-204 |
no | Plugin governance decisions recorded (allow, deny, capability grant) | none |
REQ-015-205 |
no | Plugin errors recorded with traceback | none |
REQ-015-206 |
no | Audit records stored as Neo4j (:PluginAudit) nodes | none |
REQ-015-207 |
no | Audit records linked to (:Plugin) nodes via relationships | none |
REQ-015-208 |
no | Audit records include timestamp, plugin_name, event_type, details | none |
REQ-015-209 |
no | Audit records immutable after write | none |
REQ-015-210 |
no | Audit queryable by time range, plugin name, event type | none |
Code that traces this spec
Checklists
checklists/README.md
Plugin SDK Checklists
Project: Orchestrata Plugin SDK (Spec 015) Component: Plugin SDK Status: Planning Phase Owner: Architecture Team
Overview
The Plugin SDK is the extensibility framework for Orchestrata. It defines the public API surface and runtime for third-party and first-party plugins that extend the system with new Volk types, tools, LLM providers, channel adapters, governance rules, and knowledge graph extensions — all installable and loadable at runtime without modifying the monorepo.
Checklist Summary
| Checklist | Total Items | Completed | Progress |
|---|---|---|---|
| Requirements | 220+ | 0 | 0% |
| Constitutional Compliance | 110+ | 0 | 0% |
| Testing Plan | 380+ | 0 | 0% |
| TOTAL | 710+ | 0 | 0% |
Key Responsibilities
1. Plugin Discovery and Loading
- Discover plugins from local directories and pip-installed packages
- Validate plugin manifests against schema
- Load plugins in dependency order with topological sort
- Handle circular dependencies and missing dependencies
2. Plugin SDK (Public API)
- IPlugin interface for plugin entry points
- PluginContext for DI access, configuration, and registration
- Registration methods for: Volks, tools, providers, channels, rules, KG extensions
- Event subscription and emission
3. Plugin Lifecycle Management
- Full lifecycle: discover → validate → load → initialize → start → stop → unload
- Hot-reload support (stop → unload → load → initialize → start)
- Enable/disable without uninstalling
- Dependency-aware ordering
4. Isolation and Security
- Capability-based access control (DI scoping)
- PAM++ governance for plugin authorization
- Resource limit enforcement (memory, API calls, events)
- Error isolation (one plugin crash does not affect others)
5. Plugin Management CLI
- Install from pip, local path, or git URL
- Uninstall with cleanup
- List, info, enable, disable commands
- Status reporting and diagnostics
Performance Targets
| Metric | Target | Critical Threshold |
|---|---|---|
| Plugin Discovery | <500ms for 50 plugins | <1s |
| Plugin Validation | <100ms per plugin | <200ms |
| Plugin Initialization | <2s per plugin | <5s |
| Full Startup (50 plugins) | <10s | <20s |
| Tool Invocation Overhead | <5ms p95 | <10ms |
| Volk Routing Overhead | <10ms p95 | <20ms |
| Capability Check | <1ms p95 | <5ms |
Checklist Files
1. requirements.md
Functional requirements organized by user stories:
- US-001: Install and Load a Plugin
- US-002: Develop a Custom Volk Plugin
- US-003: Develop a Tool Plugin
- US-004: Develop a Provider Plugin
- US-005: Develop a Governance Rule Plugin
- US-006: Develop a Knowledge Graph Extension Plugin
- US-007: Plugin Discovery and Listing
- US-008: Plugin Configuration
- US-009: Plugin Isolation and Security
- US-010: Plugin Lifecycle Management
- US-011: Plugin Event Hooks
- US-012: Channel Adapter Plugin
2. constitutional.md
Compliance with all 11 constitutional articles:
- Article I: Agent Autonomy
- Article II: Prolog as Single Source of Truth
- Article III: Session Isolation
- Article IV: Hooks for Extensibility
- Article V: Minimal Core, Maximal Extension
- Article VI: Python 3.14t Implementation
- Article VII: Testing Without Mocks
- Article VIII: Metrics and Observability
- Article IX: Graceful Degradation
- Article X: Version Compatibility
- Article XI: Documentation First
3. testing.md
Comprehensive testing strategy:
- Unit Tests (140+ tests): Plugin loading, SDK API, lifecycle, config, capability enforcement
- Integration Tests (100+ tests): Volk registration, tool invocation, provider registration, Prolog rules
- Contract Tests (40 tests): IPlugin compliance, manifest schema, PluginContext API
- Performance Tests (50+ tests): Discovery, loading, runtime overhead
- Compliance Tests (11 tests): Constitutional compliance
- Failure Scenario Tests (40+ tests): Plugin errors, dependency failures, resource exhaustion
Integration Points
Upstream Dependencies
- Kernel: Volk registry, EventBus
- DI Container: Scoped access for plugins
- Prolog: PAM++ plugin governance predicates
- Redis: Plugin registry, resource tracking
- Neo4j: Audit trail, KG schema extensions
Downstream Consumers
- Plugin Developers: Use SDK to build extensions
- System Operators: Use CLI to manage plugins
- Gateway: Exposes plugin management RPC methods (SPEC-014)
- Volks: Discover and invoke plugin-provided tools
References
- SPEC-001: Orchestration Kernel
- SPEC-004: CXP Protocol
- SPEC-007: Prolog Governance
- SPEC-008: Knowledge Graph
- SPEC-011: Provider Manager
- SPEC-014: WebSocket Gateway
Last Updated: 2026-03-27 Version: 1.0.0 Maintained By: Architecture Team
checklists/constitutional.md
Plugin SDK - Constitutional Compliance
Component: Plugin SDK Spec: 015-plugin-sdk Status: Compliance Review Total Compliance Items: 120+
Overview
This document tracks compliance of the Plugin SDK with all 11 articles of the Orchestrata Constitution. The plugin system is the primary extensibility mechanism — it must enforce constitutional boundaries rigorously to prevent third-party code from violating system invariants.
Article I: Agent Autonomy
Principle: Volks (agents) retain autonomy; plugins extend but do not override agent behavior.
Compliance Requirements
Autonomy Preservation
- [ ] CONST-I-001: Plugin Volks are autonomous agents with their own decision logic
- [ ] CONST-I-002: Plugin tools do not force Volk behavior — Volks choose when to invoke tools
- [ ] CONST-I-003: Plugin providers do not override Volk model preferences
- [ ] CONST-I-004: Plugin governance rules cannot remove agent autonomy rights
- [ ] CONST-I-005: Plugin channels relay messages without modifying agent decisions
- [ ] CONST-I-006: Core Volk aliases (U:Ox, U:PS, U:KR) cannot be overridden by plugins
- [ ] CONST-I-007: Plugin Volks can refuse work assignments like core Volks
- [ ] CONST-I-008: Plugin Volks control their own internal state
- [ ] CONST-I-009: Plugin tools provide capabilities, not mandates
- [ ] CONST-I-010: Plugin system enhances agent ecosystem without constraining it
Priority: Critical Verification: Integration tests + manual review
Article II: Prolog as Single Source of Truth
Principle: All governance rules are defined in Prolog; plugin governance queries Prolog.
Compliance Requirements
Prolog Integration
- [ ] CONST-II-001: Plugin load authorization queries Prolog (plugin_allowed)
- [ ] CONST-II-002: Plugin capability grants query Prolog (plugin_capability_granted)
- [ ] CONST-II-003: Plugin Volk registration queries Prolog (plugin_can_register_volk)
- [ ] CONST-II-004: Plugin tool registration queries Prolog (plugin_can_register_tool)
- [ ] CONST-II-005: Plugin resource limits queried from Prolog (plugin_resource_limit)
- [ ] CONST-II-006: Plugin SDK does not hardcode authorization logic
- [ ] CONST-II-007: Plugin governance rules loaded into Prolog, not interpreted by Python
- [ ] CONST-II-008: Plugin Prolog rules cannot override core PAM++ predicates
- [ ] CONST-II-009: All plugin governance queries logged for audit
- [ ] CONST-II-010: Plugin rule changes take effect without system restart
Priority: Critical Verification: Prolog integration tests + policy compliance tests
Article III: Session Isolation
Principle: Sessions are isolated; plugin state does not leak between sessions.
Compliance Requirements
State Isolation
- [ ] CONST-III-001: Plugin Volks respect session boundaries in CXP routing
- [ ] CONST-III-002: Plugin tools operate within the calling session's context
- [ ] CONST-III-003: Plugin EventBus subscriptions do not leak cross-session events
- [ ] CONST-III-004: Plugin KG extensions namespaced to prevent cross-session pollution
- [ ] CONST-III-005: Plugin configuration is global (not session-specific) — this is explicit
- [ ] CONST-III-006: Plugin resource tracking is global (not session-specific) — this is explicit
- [ ] CONST-III-007: Plugin channels route inbound messages through session-aware kernel
- [ ] CONST-III-008: Plugin audit records include session_id when applicable
- [ ] CONST-III-009: Plugin state cleanup does not affect other sessions
- [ ] CONST-III-010: Plugin Prolog rules respect session namespace
Priority: Critical Verification: Session isolation tests + state leak detection
Article IV: Hooks for Extensibility
Principle: The system provides extension points for customization.
Compliance Requirements
Extension Points
- [ ] CONST-IV-001: Plugin system IS the primary extension mechanism (Article IV fulfilled)
- [ ] CONST-IV-002: Plugins register Volks via hook (register_volk)
- [ ] CONST-IV-003: Plugins register tools via hook (register_tool)
- [ ] CONST-IV-004: Plugins register providers via hook (register_provider)
- [ ] CONST-IV-005: Plugins register channels via hook (register_channel)
- [ ] CONST-IV-006: Plugins register governance rules via hook (register_rules)
- [ ] CONST-IV-007: Plugins register KG extensions via hook (register_kg_schema)
- [ ] CONST-IV-008: Plugins subscribe to events via hook (subscribe_event)
- [ ] CONST-IV-009: Plugin hooks are registered dynamically at runtime
- [ ] CONST-IV-010: Plugin hook failures do not crash the system
Priority: High Verification: Hook registration tests + extension tests
Article V: Minimal Core, Maximal Extension
Principle: Core is minimal; the plugin system is how features are added.
Compliance Requirements
Minimal Core
- [ ] CONST-V-001: Plugin SDK enables external feature development without core changes
- [ ] CONST-V-002: New Volk types added via plugins, not new packages
- [ ] CONST-V-003: New tools added via plugins, not core modifications
- [ ] CONST-V-004: New providers added via plugins, not core modifications
- [ ] CONST-V-005: New channels added via plugins, not core modifications
- [ ] CONST-V-006: New governance rules added via plugins, not core rule files
- [ ] CONST-V-007: Core plugin framework code is < 5000 lines
- [ ] CONST-V-008: Core plugin dependencies are minimal
- [ ] CONST-V-009: Plugin SDK API is stable and versioned
- [ ] CONST-V-010: Existing specialized Volks (CodeDirector, QA, Architect, PM, UX, Concierge) could migrate to plugins
Priority: High Verification: Core size metrics + extension loading tests
Article VI: Python 3.14t Implementation
Principle: Plugin SDK is implemented in Python 3.14t with modern async patterns.
Compliance Requirements
Python Version
- [ ] CONST-VI-001: Code requires Python 3.14t or newer
- [ ] CONST-VI-002: Type hints on all public interfaces (IPlugin, PluginContext, etc.)
- [ ] CONST-VI-003: Async/await for all I/O operations
- [ ] CONST-VI-004: No blocking calls in async functions
- [ ] CONST-VI-005: asyncio.gather for parallel plugin initialization
- [ ] CONST-VI-006: importlib for dynamic module loading
- [ ] CONST-VI-007: importlib.metadata for entry point discovery
- [ ] CONST-VI-008: Protocol classes for plugin interfaces
- [ ] CONST-VI-009: Dataclasses for manifest and config models
- [ ] CONST-VI-010: No global mutable state in plugin framework
Code Quality
- [ ] CONST-VI-011: Black formatting enforced
- [ ] CONST-VI-012: Flake8 linting passes
- [ ] CONST-VI-013: Mypy strict mode passes
- [ ] CONST-VI-014: Isort for import sorting
- [ ] CONST-VI-015: Google-style docstrings for all public APIs
- [ ] CONST-VI-016: Test coverage > 90%
- [ ] CONST-VI-017: Cyclomatic complexity < 10 per function
- [ ] CONST-VI-018: Functions < 40 lines
- [ ] CONST-VI-019: No
# type: ignorewithout justification - [ ] CONST-VI-020: Code reviewed before merge
Priority: High Verification: CI/CD checks + static analysis
Article VII: Testing Without Mocks
Principle: Tests use real implementations, not mocks.
Compliance Requirements
Real Systems Testing
- [ ] CONST-VII-001: Tests use real plugin loading (importlib)
- [ ] CONST-VII-002: Tests use real Redis for plugin registry and resource tracking
- [ ] CONST-VII-003: Tests use real Neo4j for audit trail and KG extensions
- [ ] CONST-VII-004: Tests use real Prolog for plugin governance
- [ ] CONST-VII-005: No unittest.mock, MagicMock, Mock, AsyncMock, or patch
- [ ] CONST-VII-006: Test plugins are real Python packages (not mocked)
- [ ] CONST-VII-007: Test fixtures create real plugin directories with real manifests
- [ ] CONST-VII-008: Integration tests use real kernel with real Volk registry
- [ ] CONST-VII-009: Tests validate real dependency resolution
- [ ] CONST-VII-010: Tests validate real capability enforcement
Priority: Critical Verification: Test code review + mock detector
Article VIII: Metrics and Observability
Principle: All operations are observable and measurable.
Compliance Requirements
Metrics Collection
- [ ] CONST-VIII-001: Plugin count metric (active, disabled, error)
- [ ] CONST-VIII-002: Plugin load time metric (histogram)
- [ ] CONST-VIII-003: Plugin error count metric (counter by plugin)
- [ ] CONST-VIII-004: Plugin resource usage metrics (memory, API calls, events)
- [ ] CONST-VIII-005: Plugin tool invocation count metric (counter by tool)
- [ ] CONST-VIII-006: Plugin Volk message count metric (counter by Volk)
- [ ] CONST-VIII-007: Plugin capability denial count metric (counter)
- [ ] CONST-VIII-008: Plugin governance decision count metric (counter by result)
- [ ] CONST-VIII-009: Plugin hot-reload count metric (counter)
- [ ] CONST-VIII-010: Plugin discovery time metric (histogram)
Observability
- [ ] CONST-VIII-011: Structured logging for all plugin operations
- [ ] CONST-VIII-012: Plugin-scoped loggers with plugin name prefix
- [ ] CONST-VIII-013: Audit trail for all plugin lifecycle and registration events
- [ ] CONST-VIII-014: Plugin status queryable via CLI and Gateway RPC
- [ ] CONST-VIII-015: Plugin resource usage queryable via PluginInfo
- [ ] CONST-VIII-016: Plugin health included in system health report
- [ ] CONST-VIII-017: Plugin errors include full context (plugin name, operation, traceback)
- [ ] CONST-VIII-018: Plugin EventBus activity observable
- [ ] CONST-VIII-019: Plugin KG extensions visible in graph schema
- [ ] CONST-VIII-020: Plugin Prolog rules visible in governance rule listing
Priority: High Verification: Metrics tests + observability review
Article IX: Graceful Degradation
Principle: System degrades gracefully under failure, does not crash.
Compliance Requirements
Failure Handling
- [ ] CONST-IX-001: Plugin load failure does not crash system
- [ ] CONST-IX-002: Plugin initialize failure does not crash system
- [ ] CONST-IX-003: Plugin start failure does not crash system
- [ ] CONST-IX-004: Plugin runtime exception does not crash system
- [ ] CONST-IX-005: Plugin resource exhaustion does not crash system
- [ ] CONST-IX-006: One plugin failure does not affect other plugins
- [ ] CONST-IX-007: Redis unavailable: plugin manager uses in-memory fallback
- [ ] CONST-IX-008: Neo4j unavailable: audit deferred, KG extensions skipped
- [ ] CONST-IX-009: Prolog unavailable: plugin governance defaults to deny (safe)
- [ ] CONST-IX-010: Plugin auto-disabled after configurable error threshold
Recovery
- [ ] CONST-IX-011: Plugin hot-reload recovers from transient failures
- [ ] CONST-IX-012: Plugin re-enable after manual disable works
- [ ] CONST-IX-013: Redis reconnection restores plugin state
- [ ] CONST-IX-014: Neo4j reconnection processes deferred audit
- [ ] CONST-IX-015: Prolog reconnection re-evaluates plugin governance
- [ ] CONST-IX-016: System startup succeeds even if all plugins fail to load
- [ ] CONST-IX-017: Plugin error logged with remediation suggestion
- [ ] CONST-IX-018: Plugin doctor command diagnoses and suggests fixes
- [ ] CONST-IX-019: Plugin cleanup on failure is complete (no leaked state)
- [ ] CONST-IX-020: Recovery events logged for debugging
Priority: High Verification: Failure injection tests + chaos tests
Article X: Version Compatibility
Principle: APIs are versioned; breaking changes are avoided.
Compliance Requirements
API Versioning
- [ ] CONST-X-001: Plugin SDK published with semantic version
- [ ] CONST-X-002: Plugin manifest declares min_orchestrata_version
- [ ] CONST-X-003: Version compatibility checked on plugin load
- [ ] CONST-X-004: Incompatible plugins rejected with clear message
- [ ] CONST-X-005: SDK API changes follow deprecation cycle
- [ ] CONST-X-006: Backward-compatible changes do not break existing plugins
- [ ] CONST-X-007: Breaking changes require major version bump
- [ ] CONST-X-008: Compatibility matrix published (SDK ↔ Orchestrata)
- [ ] CONST-X-009: Plugin manifest version validated as semver
- [ ] CONST-X-010: Old plugins (N-1) work with new Orchestrata
Priority: Medium Verification: Compatibility tests + version migration tests
Article XI: Documentation First
Principle: Documentation precedes implementation.
Compliance Requirements
Design Documentation
- [ ] CONST-XI-001: Plugin SDK architecture documented before implementation
- [ ] CONST-XI-002: IPlugin interface documented with examples
- [ ] CONST-XI-003: PluginContext API documented with examples
- [ ] CONST-XI-004: Plugin manifest format documented
- [ ] CONST-XI-005: Capability types documented with access scope
- [ ] CONST-XI-006: Plugin lifecycle documented with diagram
- [ ] CONST-XI-007: Plugin configuration schema documented
- [ ] CONST-XI-008: Plugin governance predicates documented with examples
- [ ] CONST-XI-009: Plugin development tutorial provided
- [ ] CONST-XI-010: Plugin testing guide provided
Priority: High Verification: Documentation review + doc tests
Summary Statistics
| Article | Total Items | Completed | Percentage |
|---|---|---|---|
| Article I (Autonomy) | 10 | 0 | 0% |
| Article II (Prolog) | 10 | 0 | 0% |
| Article III (Isolation) | 10 | 0 | 0% |
| Article IV (Hooks) | 10 | 0 | 0% |
| Article V (Minimal Core) | 10 | 0 | 0% |
| Article VI (Python 3.14t) | 20 | 0 | 0% |
| Article VII (No Mocks) | 10 | 0 | 0% |
| Article VIII (Observability) | 20 | 0 | 0% |
| Article IX (Degradation) | 20 | 0 | 0% |
| Article X (Versioning) | 10 | 0 | 0% |
| Article XI (Documentation) | 10 | 0 | 0% |
| TOTAL | 140 | 0 | 0% |
Last Updated: 2026-03-27 Version: 1.0.0 Maintained By: Architecture Team
checklists/requirements.md
The requirement list above comes from this file; 0 of 210 checkbox items are ticked.
checklists/testing.md
Plugin SDK - Testing Plan
Component: Plugin SDK Spec: 015-plugin-sdk Status: Test Planning Phase Total Test Cases: 381+
Overview
This document defines the comprehensive testing strategy for the Plugin SDK. All tests follow the strict "no mocking" policy and use real implementations including real plugin loading, real Redis, real Neo4j, real Prolog for governance, and real kernel integration.
Test Categories Summary
| Category | Test Count | Purpose | Priority |
|---|---|---|---|
| Unit Tests | 140+ | Core logic validation | Critical |
| Integration Tests | 100+ | System integration | Critical |
| Contract Tests | 40 | Interface compliance | High |
| Performance Tests | 50+ | Loading & runtime overhead | High |
| Compliance Tests | 11 | Constitutional compliance | Critical |
| Failure Scenario Tests | 40+ | Error handling & recovery | High |
| TOTAL | 381+ |
Unit Tests (140+)
Plugin Discovery Tests (15 tests)
- [ ] TEST-UNIT-DISC-001: Discover plugins from local directory
- [ ] TEST-UNIT-DISC-002: Discover plugins from importlib.metadata entry points
- [ ] TEST-UNIT-DISC-003: Discover plugins from pip packages with prefix
- [ ] TEST-UNIT-DISC-004: Deduplicate by name (local takes precedence)
- [ ] TEST-UNIT-DISC-005: Ignore directories without plugin.yaml
- [ ] TEST-UNIT-DISC-006: Handle empty plugin directories gracefully
- [ ] TEST-UNIT-DISC-007: Handle missing plugin directories gracefully
- [ ] TEST-UNIT-DISC-008: Discover from multiple configured directories
- [ ] TEST-UNIT-DISC-009: Log all discovered plugins with source
- [ ] TEST-UNIT-DISC-010: Discovery returns PluginManifest objects
- [ ] TEST-UNIT-DISC-011: Discovery handles malformed plugin.yaml gracefully
- [ ] TEST-UNIT-DISC-012: Discovery handles missing entry_point gracefully
- [ ] TEST-UNIT-DISC-013: Discovery respects disabled plugin list
- [ ] TEST-UNIT-DISC-014: Discovery handles concurrent calls safely
- [ ] TEST-UNIT-DISC-015: Discovery performance < 500ms for 50 plugins
Plugin Validation Tests (15 tests)
- [ ] TEST-UNIT-VAL-001: Validate valid manifest passes
- [ ] TEST-UNIT-VAL-002: Reject manifest missing required field (name)
- [ ] TEST-UNIT-VAL-003: Reject manifest missing required field (version)
- [ ] TEST-UNIT-VAL-004: Reject manifest missing required field (entry_point)
- [ ] TEST-UNIT-VAL-005: Reject manifest with invalid version format
- [ ] TEST-UNIT-VAL-006: Reject manifest with unknown capability type
- [ ] TEST-UNIT-VAL-007: Validate manifest with optional fields missing
- [ ] TEST-UNIT-VAL-008: Validate manifest config_schema is valid JSON Schema
- [ ] TEST-UNIT-VAL-009: Validate manifest dependencies are list of strings
- [ ] TEST-UNIT-VAL-010: Validate manifest python_dependencies are valid pip specs
- [ ] TEST-UNIT-VAL-011: Validate min_orchestrata_version compatibility
- [ ] TEST-UNIT-VAL-012: Reject manifest with duplicate capability declarations
- [ ] TEST-UNIT-VAL-013: Validate prolog_rules paths exist in plugin directory
- [ ] TEST-UNIT-VAL-014: Validate kg_schema structure when present
- [ ] TEST-UNIT-VAL-015: Validation error messages include field path and reason
Plugin Loading Tests (20 tests)
- [ ] TEST-UNIT-LOAD-001: Load plugin module via importlib
- [ ] TEST-UNIT-LOAD-002: Instantiate entry point class from manifest
- [ ] TEST-UNIT-LOAD-003: Pass PluginContext to plugin on instantiation
- [ ] TEST-UNIT-LOAD-004: Load plugins in dependency order (A depends on B → B first)
- [ ] TEST-UNIT-LOAD-005: Detect circular dependency (A → B → A)
- [ ] TEST-UNIT-LOAD-006: Detect transitive circular dependency (A → B → C → A)
- [ ] TEST-UNIT-LOAD-007: Reject load when dependency not found
- [ ] TEST-UNIT-LOAD-008: Reject load when Python dependency not installed
- [ ] TEST-UNIT-LOAD-009: Reject load when entry_point class not found
- [ ] TEST-UNIT-LOAD-010: Reject load when entry_point class does not implement IPlugin
- [ ] TEST-UNIT-LOAD-011: Load multiple independent plugins in parallel
- [ ] TEST-UNIT-LOAD-012: Load plugin with no dependencies
- [ ] TEST-UNIT-LOAD-013: Load plugin with multiple dependencies
- [ ] TEST-UNIT-LOAD-014: Diamond dependency resolved correctly (A → B,C; B → D; C → D)
- [ ] TEST-UNIT-LOAD-015: Plugin module reloaded correctly on hot-reload
- [ ] TEST-UNIT-LOAD-016: Load failure does not affect other plugins
- [ ] TEST-UNIT-LOAD-017: Load failure logs error with full traceback
- [ ] TEST-UNIT-LOAD-018: PAM++ queried before loading (plugin_allowed)
- [ ] TEST-UNIT-LOAD-019: Denied plugin not loaded (PAM++ block)
- [ ] TEST-UNIT-LOAD-020: Load order deterministic for same dependency graph
Plugin Lifecycle Tests (15 tests)
- [ ] TEST-UNIT-LC-001: initialize() called after load
- [ ] TEST-UNIT-LC-002: start() called after all plugins initialized
- [ ] TEST-UNIT-LC-003: stop() called on shutdown
- [ ] TEST-UNIT-LC-004: Lifecycle order: initialize → start → stop
- [ ] TEST-UNIT-LC-005: Start not called if initialize fails
- [ ] TEST-UNIT-LC-006: Stop called even if start failed (cleanup)
- [ ] TEST-UNIT-LC-007: Hot-reload: stop → unload → load → initialize → start
- [ ] TEST-UNIT-LC-008: Hot-reload preserves other plugins
- [ ] TEST-UNIT-LC-009: Enable calls: load → initialize → start
- [ ] TEST-UNIT-LC-010: Disable calls: stop → unload
- [ ] TEST-UNIT-LC-011: Lifecycle events emitted to EventBus
- [ ] TEST-UNIT-LC-012: Lifecycle events logged to audit trail
- [ ] TEST-UNIT-LC-013: Double start is idempotent
- [ ] TEST-UNIT-LC-014: Double stop is idempotent
- [ ] TEST-UNIT-LC-015: Shutdown order is reverse of start order
PluginContext Tests (20 tests)
- [ ] TEST-UNIT-CTX-001: PluginContext provides plugin config
- [ ] TEST-UNIT-CTX-002: PluginContext provides scoped logger
- [ ] TEST-UNIT-CTX-003: PluginContext provides plugin directory path
- [ ] TEST-UNIT-CTX-004: PluginContext provides manifest
- [ ] TEST-UNIT-CTX-005: register_volk() registers Volk in kernel registry
- [ ] TEST-UNIT-CTX-006: register_volk() rejects reserved aliases
- [ ] TEST-UNIT-CTX-007: register_volk() rejects duplicate aliases
- [ ] TEST-UNIT-CTX-008: register_tool() registers tool in tool registry
- [ ] TEST-UNIT-CTX-009: register_tool() namespaces tool name
- [ ] TEST-UNIT-CTX-010: register_tool() rejects duplicate tool names
- [ ] TEST-UNIT-CTX-011: register_provider() registers with ProviderManager
- [ ] TEST-UNIT-CTX-012: register_provider() rejects duplicate provider names
- [ ] TEST-UNIT-CTX-013: register_channel() registers channel in kernel
- [ ] TEST-UNIT-CTX-014: register_rules() loads Prolog rules
- [ ] TEST-UNIT-CTX-015: register_kg_schema() applies Neo4j schema
- [ ] TEST-UNIT-CTX-016: subscribe_event() subscribes to EventBus topic
- [ ] TEST-UNIT-CTX-017: emit_event() emits namespaced event
- [ ] TEST-UNIT-CTX-018: resolve() returns DI component within capabilities
- [ ] TEST-UNIT-CTX-019: resolve() raises CapabilityDeniedError for undeclared capability
- [ ] TEST-UNIT-CTX-020: All registrations auto-deregistered on plugin stop
Capability Enforcement Tests (15 tests)
- [ ] TEST-UNIT-CAP-001: Scoped DI allows declared capabilities
- [ ] TEST-UNIT-CAP-002: Scoped DI blocks undeclared capabilities
- [ ] TEST-UNIT-CAP-003: di_access capability grants DI resolution
- [ ] TEST-UNIT-CAP-004: redis capability grants Redis access
- [ ] TEST-UNIT-CAP-005: neo4j capability grants Neo4j access
- [ ] TEST-UNIT-CAP-006: network capability grants network access
- [ ] TEST-UNIT-CAP-007: filesystem capability grants filesystem access
- [ ] TEST-UNIT-CAP-008: prolog capability grants Prolog access
- [ ] TEST-UNIT-CAP-009: event_bus capability grants EventBus access
- [ ] TEST-UNIT-CAP-010: provider capability grants ProviderManager access
- [ ] TEST-UNIT-CAP-011: gateway capability grants GatewayServer access
- [ ] TEST-UNIT-CAP-012: Capability denial logged with plugin name and attempted capability
- [ ] TEST-UNIT-CAP-013: PAM++ queried for capability grants
- [ ] TEST-UNIT-CAP-014: Capability check cached for performance (< 1ms)
- [ ] TEST-UNIT-CAP-015: Multiple capabilities can be declared and enforced
Plugin Configuration Tests (15 tests)
- [ ] TEST-UNIT-CFG-001: Config loaded from monolithic.yaml under plugins.
- [ ] TEST-UNIT-CFG-002: Config validated against manifest JSON Schema
- [ ] TEST-UNIT-CFG-003: Invalid config prevents plugin load
- [ ] TEST-UNIT-CFG-004: Validation error includes field and reason
- [ ] TEST-UNIT-CFG-005: Environment variable interpolation works
- [ ] TEST-UNIT-CFG-006: Default values from schema applied
- [ ] TEST-UNIT-CFG-007: Missing config section treated as empty dict
- [ ] TEST-UNIT-CFG-008: Config accessible via PluginContext.config
- [ ] TEST-UNIT-CFG-009: Config is read-only (plugin cannot modify)
- [ ] TEST-UNIT-CFG-010: Config hot-reload notifies plugin
- [ ] TEST-UNIT-CFG-011: Hot-reload validates new config before applying
- [ ] TEST-UNIT-CFG-012: Hot-reload rollbacks on plugin rejection
- [ ] TEST-UNIT-CFG-013: Config changes logged to audit trail
- [ ] TEST-UNIT-CFG-014: Nested config keys work correctly
- [ ] TEST-UNIT-CFG-015: Array and object config values work correctly
Resource Limit Tests (10 tests)
- [ ] TEST-UNIT-RES-001: Memory usage tracked per plugin
- [ ] TEST-UNIT-RES-002: API call count tracked per time window
- [ ] TEST-UNIT-RES-003: Event emission count tracked per time window
- [ ] TEST-UNIT-RES-004: Error count tracked per time window
- [ ] TEST-UNIT-RES-005: Warning emitted at configurable threshold
- [ ] TEST-UNIT-RES-006: Plugin throttled at limit
- [ ] TEST-UNIT-RES-007: Plugin auto-disabled after persistent error threshold
- [ ] TEST-UNIT-RES-008: Resource limits configurable per plugin
- [ ] TEST-UNIT-RES-009: Default resource limits applied when not configured
- [ ] TEST-UNIT-RES-010: Resource usage queryable via PluginInfo
CLI Command Tests (15 tests)
- [ ] TEST-UNIT-CLI-001:
plugins listshows all plugins - [ ] TEST-UNIT-CLI-002:
plugins listincludes status column - [ ] TEST-UNIT-CLI-003:
plugins info <name>shows manifest details - [ ] TEST-UNIT-CLI-004:
plugins info <name>shows registered components - [ ] TEST-UNIT-CLI-005:
plugins enable <name>activates disabled plugin - [ ] TEST-UNIT-CLI-006:
plugins disable <name>deactivates plugin - [ ] TEST-UNIT-CLI-007:
plugins install <source>installs from pip - [ ] TEST-UNIT-CLI-008:
plugins install <path>installs from local directory - [ ] TEST-UNIT-CLI-009:
plugins uninstall <name>removes plugin - [ ] TEST-UNIT-CLI-010:
plugins doctor <name>runs health check - [ ] TEST-UNIT-CLI-011:
plugins scaffold <name>generates template - [ ] TEST-UNIT-CLI-012: Unknown plugin name returns clear error
- [ ] TEST-UNIT-CLI-013: Install with invalid manifest returns validation error
- [ ] TEST-UNIT-CLI-014: Uninstall stops plugin before removing
- [ ] TEST-UNIT-CLI-015: CLI output formatted consistently
Integration Tests (100+)
Kernel Integration (20 tests)
- [ ] TEST-INTEG-KRN-001: Plugin Volk registered in real kernel Volk registry
- [ ] TEST-INTEG-KRN-002: Plugin Volk receives CXP packets through kernel routing
- [ ] TEST-INTEG-KRN-003: Plugin Volk sends CXP responses through kernel
- [ ] TEST-INTEG-KRN-004: Plugin Volk deregistration removes from kernel registry
- [ ] TEST-INTEG-KRN-005: Plugin tool registered in kernel tool registry
- [ ] TEST-INTEG-KRN-006: Plugin tool invocable by kernel Volks
- [ ] TEST-INTEG-KRN-007: Plugin channel registered in kernel channel registry
- [ ] TEST-INTEG-KRN-008: Plugin EventBus subscription receives real kernel events
- [ ] TEST-INTEG-KRN-009: Plugin custom events visible on kernel EventBus
- [ ] TEST-INTEG-KRN-010: Plugin lifecycle events emitted on kernel EventBus
- [ ] TEST-INTEG-KRN-011: PluginManager initializes after kernel
- [ ] TEST-INTEG-KRN-012: PluginManager stops before kernel on shutdown
- [ ] TEST-INTEG-KRN-013: Multiple plugin Volks coexist with core Volks
- [ ] TEST-INTEG-KRN-014: Plugin Volk participates in workflow orchestration
- [ ] TEST-INTEG-KRN-015: Plugin tool appears in Volk capability discovery
- [ ] TEST-INTEG-KRN-016: Plugin Volk health reported in kernel health
- [ ] TEST-INTEG-KRN-017: DI container resolves IPluginManager
- [ ] TEST-INTEG-KRN-018: Kernel restart does not crash PluginManager
- [ ] TEST-INTEG-KRN-019: Plugin Volk escalation handled by kernel
- [ ] TEST-INTEG-KRN-020: Plugin tool timeout handled by kernel
Prolog Integration (15 tests)
- [ ] TEST-INTEG-PL-001: plugin_allowed() queried on load
- [ ] TEST-INTEG-PL-002: Denied plugin not loaded
- [ ] TEST-INTEG-PL-003: plugin_capability_granted() queried per capability
- [ ] TEST-INTEG-PL-004: Denied capability raises CapabilityDeniedError
- [ ] TEST-INTEG-PL-005: plugin_resource_limit() returns configured limits
- [ ] TEST-INTEG-PL-006: plugin_can_register_volk() blocks reserved aliases
- [ ] TEST-INTEG-PL-007: plugin_can_register_tool() blocks reserved names
- [ ] TEST-INTEG-PL-008: Plugin Prolog rules loaded into knowledge base
- [ ] TEST-INTEG-PL-009: Plugin Prolog rules unloaded on stop
- [ ] TEST-INTEG-PL-010: Plugin rules do not override core predicates
- [ ] TEST-INTEG-PL-011: Rule conflicts detected and reported
- [ ] TEST-INTEG-PL-012: Prolog health check reflects plugin rule state
- [ ] TEST-INTEG-PL-013: Multiple plugin rule sets coexist
- [ ] TEST-INTEG-PL-014: Prolog timeout falls back to deny for plugin operations
- [ ] TEST-INTEG-PL-015: Plugin governance decisions logged to audit trail
Redis Integration (15 tests)
- [ ] TEST-INTEG-REDIS-001: Plugin registry stored in Redis
- [ ] TEST-INTEG-REDIS-002: Active plugin set maintained in Redis
- [ ] TEST-INTEG-REDIS-003: Resource counters tracked in Redis
- [ ] TEST-INTEG-REDIS-004: Resource counters auto-expire (hourly)
- [ ] TEST-INTEG-REDIS-005: Plugin status updates reflected in Redis
- [ ] TEST-INTEG-REDIS-006: Redis unavailable: plugin manager continues with in-memory state
- [ ] TEST-INTEG-REDIS-007: Redis reconnection restores plugin state
- [ ] TEST-INTEG-REDIS-008: Capability cache stored in Redis
- [ ] TEST-INTEG-REDIS-009: Capability cache invalidated on rule change
- [ ] TEST-INTEG-REDIS-010: Plugin uninstall cleans up Redis keys
- [ ] TEST-INTEG-REDIS-011: Redis key namespace: orchestrata:plugin:*
- [ ] TEST-INTEG-REDIS-012: Multiple PluginManager instances share Redis state
- [ ] TEST-INTEG-REDIS-013: Redis health check reflects plugin storage state
- [ ] TEST-INTEG-REDIS-014: Redis operations use configurable DB number
- [ ] TEST-INTEG-REDIS-015: Concurrent plugin operations safe with Redis
Neo4j Integration (15 tests)
- [ ] TEST-INTEG-NEO4J-001: Plugin audit records stored as (:PluginAudit) nodes
- [ ] TEST-INTEG-NEO4J-002: Plugin nodes created as (:Plugin) in graph
- [ ] TEST-INTEG-NEO4J-003: Registration events linked to (:Plugin) nodes
- [ ] TEST-INTEG-NEO4J-004: KG schema extensions applied to Neo4j
- [ ] TEST-INTEG-NEO4J-005: KG node types namespaced with plugin prefix
- [ ] TEST-INTEG-NEO4J-006: KG schema migrations versioned per plugin
- [ ] TEST-INTEG-NEO4J-007: Plugin uninstall does not delete KG data (safety)
- [ ] TEST-INTEG-NEO4J-008: Neo4j unavailable: audit deferred, KG extensions skipped
- [ ] TEST-INTEG-NEO4J-009: Neo4j reconnection processes deferred audit
- [ ] TEST-INTEG-NEO4J-010: Audit records immutable after write
- [ ] TEST-INTEG-NEO4J-011: Audit queryable by plugin name and event type
- [ ] TEST-INTEG-NEO4J-012: Multiple plugins extend KG without conflicts
- [ ] TEST-INTEG-NEO4J-013: KG constraints enforced for plugin nodes
- [ ] TEST-INTEG-NEO4J-014: Neo4j health check reflects plugin KG state
- [ ] TEST-INTEG-NEO4J-015: Batch audit writes for performance
End-to-End Tests (35 tests)
- [ ] TEST-INTEG-E2E-001: Full plugin lifecycle: install → discover → load → init → start → stop → unload → uninstall
- [ ] TEST-INTEG-E2E-002: Plugin Volk receives and responds to CXP packet
- [ ] TEST-INTEG-E2E-003: Plugin tool invoked by core Volk via CXP
- [ ] TEST-INTEG-E2E-004: Plugin provider used by ProviderManager for LLM request
- [ ] TEST-INTEG-E2E-005: Plugin channel adapter receives external message and routes to kernel
- [ ] TEST-INTEG-E2E-006: Plugin Prolog rules enforced in governance decision
- [ ] TEST-INTEG-E2E-007: Plugin KG extension creates nodes in Neo4j
- [ ] TEST-INTEG-E2E-008: Plugin event subscription receives kernel events
- [ ] TEST-INTEG-E2E-009: Plugin custom event received by another plugin
- [ ] TEST-INTEG-E2E-010: Multiple plugins loaded with dependencies
- [ ] TEST-INTEG-E2E-011: Plugin hot-reload preserves other plugins
- [ ] TEST-INTEG-E2E-012: Plugin disable/enable cycle preserves configuration
- [ ] TEST-INTEG-E2E-013: PAM++ blocks unauthorized plugin load
- [ ] TEST-INTEG-E2E-014: PAM++ blocks unauthorized capability access
- [ ] TEST-INTEG-E2E-015: Resource limit breach throttles plugin
- [ ] TEST-INTEG-E2E-016: Plugin error does not crash other plugins
- [ ] TEST-INTEG-E2E-017: Plugin error does not crash core system
- [ ] TEST-INTEG-E2E-018: CLI list shows real-time plugin status
- [ ] TEST-INTEG-E2E-019: CLI install from local path works end-to-end
- [ ] TEST-INTEG-E2E-020: CLI uninstall cleans up all state
- [ ] TEST-INTEG-E2E-021: Plugin config loaded and accessible
- [ ] TEST-INTEG-E2E-022: Plugin config hot-reload works
- [ ] TEST-INTEG-E2E-023: Gateway exposes plugin list via RPC (SPEC-014)
- [ ] TEST-INTEG-E2E-024: Audit trail complete for full plugin lifecycle
- [ ] TEST-INTEG-E2E-025: Plugin scaffold generates working template
- [ ] TEST-INTEG-E2E-026: Scaffolded plugin loads and initializes
- [ ] TEST-INTEG-E2E-027: Plugin with no capabilities has minimal access
- [ ] TEST-INTEG-E2E-028: Plugin with all capabilities has full access
- [ ] TEST-INTEG-E2E-029: 10 plugins loaded concurrently without issues
- [ ] TEST-INTEG-E2E-030: Plugin dependency chain (A → B → C) loads correctly
- [ ] TEST-INTEG-E2E-031: Plugin Volk visible in gateway Volk list (SPEC-014)
- [ ] TEST-INTEG-E2E-032: Plugin tool visible in Volk tool discovery
- [ ] TEST-INTEG-E2E-033: Plugin provider visible in provider health dashboard
- [ ] TEST-INTEG-E2E-034: Plugin doctor diagnoses real configuration issues
- [ ] TEST-INTEG-E2E-035: Plugin version compatibility check works
Contract Tests (40)
IPlugin Protocol Compliance (10 tests)
- [ ] TEST-CONTRACT-PLUG-001: Plugin implements initialize(context)
- [ ] TEST-CONTRACT-PLUG-002: Plugin implements start()
- [ ] TEST-CONTRACT-PLUG-003: Plugin implements stop()
- [ ] TEST-CONTRACT-PLUG-004: Plugin implements get_manifest()
- [ ] TEST-CONTRACT-PLUG-005: get_manifest() returns valid PluginManifest
- [ ] TEST-CONTRACT-PLUG-006: initialize() accepts PluginContext parameter
- [ ] TEST-CONTRACT-PLUG-007: All lifecycle methods are async
- [ ] TEST-CONTRACT-PLUG-008: Plugin does not raise during normal lifecycle
- [ ] TEST-CONTRACT-PLUG-009: Plugin stop() releases all resources
- [ ] TEST-CONTRACT-PLUG-010: Plugin is re-initializable after stop
Manifest Schema Compliance (10 tests)
- [ ] TEST-CONTRACT-MAN-001: Manifest has required field: name (string)
- [ ] TEST-CONTRACT-MAN-002: Manifest has required field: version (semver string)
- [ ] TEST-CONTRACT-MAN-003: Manifest has required field: entry_point (dotted path)
- [ ] TEST-CONTRACT-MAN-004: Manifest has optional field: description (string)
- [ ] TEST-CONTRACT-MAN-005: Manifest has optional field: capabilities (list of enum)
- [ ] TEST-CONTRACT-MAN-006: Manifest has optional field: dependencies (list of string)
- [ ] TEST-CONTRACT-MAN-007: Manifest has optional field: config_schema (JSON Schema)
- [ ] TEST-CONTRACT-MAN-008: Manifest has optional field: prolog_rules (list of paths)
- [ ] TEST-CONTRACT-MAN-009: Manifest has optional field: kg_schema (KGSchemaExtension)
- [ ] TEST-CONTRACT-MAN-010: Manifest has optional field: min_orchestrata_version (string)
PluginContext API Compliance (10 tests)
- [ ] TEST-CONTRACT-CTX-001: PluginContext exposes config (dict)
- [ ] TEST-CONTRACT-CTX-002: PluginContext exposes logger (Logger)
- [ ] TEST-CONTRACT-CTX-003: PluginContext exposes plugin_dir (Path)
- [ ] TEST-CONTRACT-CTX-004: PluginContext exposes manifest (PluginManifest)
- [ ] TEST-CONTRACT-CTX-005: register_volk() is async and accepts alias, capabilities, handler
- [ ] TEST-CONTRACT-CTX-006: register_tool() is async and accepts name, description, schema, handler
- [ ] TEST-CONTRACT-CTX-007: register_provider() is async and accepts name, provider, config
- [ ] TEST-CONTRACT-CTX-008: subscribe_event() is async and returns subscription_id
- [ ] TEST-CONTRACT-CTX-009: emit_event() is async and accepts event_type, payload
- [ ] TEST-CONTRACT-CTX-010: resolve() accepts type and returns instance or raises CapabilityDeniedError
IComponent Compliance (10 tests)
- [ ] TEST-CONTRACT-COMP-001: IPluginManager implements initialize()
- [ ] TEST-CONTRACT-COMP-002: IPluginManager implements start()
- [ ] TEST-CONTRACT-COMP-003: IPluginManager implements stop()
- [ ] TEST-CONTRACT-COMP-004: IPluginManager implements get_health()
- [ ] TEST-CONTRACT-COMP-005: get_health() returns HealthStatus with correct fields
- [ ] TEST-CONTRACT-COMP-006: Component registered with ComponentType.PLUGIN_MANAGER
- [ ] TEST-CONTRACT-COMP-007: DI container resolves IPluginManager
- [ ] TEST-CONTRACT-COMP-008: Lifecycle follows: initialize → start → stop
- [ ] TEST-CONTRACT-COMP-009: Double start is idempotent
- [ ] TEST-CONTRACT-COMP-010: Stop after stop is idempotent
Performance Tests (50+)
Discovery and Loading Performance (20 tests)
- [ ] TEST-PERF-LOAD-001: Discovery < 500ms for 50 plugins
- [ ] TEST-PERF-LOAD-002: Manifest validation < 100ms per plugin
- [ ] TEST-PERF-LOAD-003: Module import < 500ms per plugin
- [ ] TEST-PERF-LOAD-004: Plugin initialization < 2s per plugin
- [ ] TEST-PERF-LOAD-005: Full startup < 10s for 50 plugins
- [ ] TEST-PERF-LOAD-006: Dependency sort < 10ms for 50 plugins
- [ ] TEST-PERF-LOAD-007: PAM++ check < 5ms per plugin (cached)
- [ ] TEST-PERF-LOAD-008: Config validation < 10ms per plugin
- [ ] TEST-PERF-LOAD-009: DI scope creation < 5ms per plugin
- [ ] TEST-PERF-LOAD-010: Hot-reload < 3s per plugin
- [ ] TEST-PERF-LOAD-011: Enable < 3s per plugin
- [ ] TEST-PERF-LOAD-012: Disable < 1s per plugin
- [ ] TEST-PERF-LOAD-013: Uninstall < 2s per plugin
- [ ] TEST-PERF-LOAD-014: Plugin list query < 10ms
- [ ] TEST-PERF-LOAD-015: Plugin info query < 10ms
- [ ] TEST-PERF-LOAD-016: No memory leak after 100 load/unload cycles
- [ ] TEST-PERF-LOAD-017: No file descriptor leak after 100 load/unload cycles
- [ ] TEST-PERF-LOAD-018: Parallel plugin loading scales linearly
- [ ] TEST-PERF-LOAD-019: Large manifest (100 dependencies) validates < 200ms
- [ ] TEST-PERF-LOAD-020: Deep dependency chain (10 levels) resolves < 100ms
Runtime Performance (20 tests)
- [ ] TEST-PERF-RT-001: Tool invocation overhead < 5ms p95
- [ ] TEST-PERF-RT-002: Volk message routing overhead < 10ms p95
- [ ] TEST-PERF-RT-003: Event delivery to plugin subscriber < 50ms p95
- [ ] TEST-PERF-RT-004: Capability check < 1ms p95 (cached)
- [ ] TEST-PERF-RT-005: Custom event emission < 5ms p95
- [ ] TEST-PERF-RT-006: DI resolution from scoped container < 1ms p95
- [ ] TEST-PERF-RT-007: Resource tracking overhead < 0.5ms per operation
- [ ] TEST-PERF-RT-008: Plugin config access < 0.1ms
- [ ] TEST-PERF-RT-009: Audit logging overhead < 2ms per record
- [ ] TEST-PERF-RT-010: 50 active plugins do not degrade kernel performance
- [ ] TEST-PERF-RT-011: Plugin memory usage < 50MB per plugin (average)
- [ ] TEST-PERF-RT-012: Plugin CPU usage stable under sustained load
- [ ] TEST-PERF-RT-013: Concurrent tool invocations across plugins scale linearly
- [ ] TEST-PERF-RT-014: EventBus not bottlenecked by plugin subscribers
- [ ] TEST-PERF-RT-015: Provider plugin adds < 5ms to request routing
- [ ] TEST-PERF-RT-016: Channel plugin adds < 10ms to message routing
- [ ] TEST-PERF-RT-017: Prolog rule evaluation not degraded by plugin rules
- [ ] TEST-PERF-RT-018: Neo4j operations not degraded by plugin KG extensions
- [ ] TEST-PERF-RT-019: Redis operations not degraded by plugin state
- [ ] TEST-PERF-RT-020: System stable with 50 plugins for 1 hour sustained
CLI Performance (10 tests)
- [ ] TEST-PERF-CLI-001:
plugins list< 500ms - [ ] TEST-PERF-CLI-002:
plugins info< 200ms - [ ] TEST-PERF-CLI-003:
plugins install(local) < 5s - [ ] TEST-PERF-CLI-004:
plugins uninstall< 3s - [ ] TEST-PERF-CLI-005:
plugins enable< 3s - [ ] TEST-PERF-CLI-006:
plugins disable< 1s - [ ] TEST-PERF-CLI-007:
plugins doctor< 5s - [ ] TEST-PERF-CLI-008:
plugins scaffold< 2s - [ ] TEST-PERF-CLI-009: CLI handles 50+ plugins without timeout
- [ ] TEST-PERF-CLI-010: CLI output streaming for large plugin lists
Compliance Tests (11)
- [ ] TEST-COMPLIANCE-001: Article I — Plugins do not override agent autonomy
- [ ] TEST-COMPLIANCE-002: Article II — Plugin governance queries Prolog
- [ ] TEST-COMPLIANCE-003: Article III — Plugin operations respect session isolation
- [ ] TEST-COMPLIANCE-004: Article IV — Plugin system provides extension hooks
- [ ] TEST-COMPLIANCE-005: Article V — Plugin SDK is the primary extension mechanism
- [ ] TEST-COMPLIANCE-006: Article VI — Python 3.14t async throughout
- [ ] TEST-COMPLIANCE-007: Article VII — No mocks in plugin tests
- [ ] TEST-COMPLIANCE-008: Article VIII — Plugin operations observable and measurable
- [ ] TEST-COMPLIANCE-009: Article IX — Plugin failures degrade gracefully
- [ ] TEST-COMPLIANCE-010: Article X — Plugin SDK API versioned
- [ ] TEST-COMPLIANCE-011: Article XI — Plugin SDK documented before implementation
Failure Scenario Tests (40+)
Plugin Load Failures (10 tests)
- [ ] TEST-FAIL-LOAD-001: Missing plugin.yaml handled gracefully
- [ ] TEST-FAIL-LOAD-002: Invalid YAML syntax in manifest handled
- [ ] TEST-FAIL-LOAD-003: Missing entry_point module handled
- [ ] TEST-FAIL-LOAD-004: Entry point class import error handled
- [ ] TEST-FAIL-LOAD-005: Entry point class missing IPlugin methods handled
- [ ] TEST-FAIL-LOAD-006: initialize() exception handled
- [ ] TEST-FAIL-LOAD-007: start() exception handled
- [ ] TEST-FAIL-LOAD-008: Dependency cycle handled with clear error
- [ ] TEST-FAIL-LOAD-009: Missing dependency handled with clear error
- [ ] TEST-FAIL-LOAD-010: PAM++ denial handled gracefully
Plugin Runtime Failures (15 tests)
- [ ] TEST-FAIL-RT-001: Plugin Volk handler exception isolated
- [ ] TEST-FAIL-RT-002: Plugin tool handler exception returns structured error
- [ ] TEST-FAIL-RT-003: Plugin event handler exception logged and isolated
- [ ] TEST-FAIL-RT-004: Plugin provider request exception handled by ProviderManager
- [ ] TEST-FAIL-RT-005: Plugin channel handler exception isolated
- [ ] TEST-FAIL-RT-006: Repeated plugin errors trigger auto-disable
- [ ] TEST-FAIL-RT-007: Auto-disabled plugin logged with reason
- [ ] TEST-FAIL-RT-008: Plugin memory limit breach triggers throttle
- [ ] TEST-FAIL-RT-009: Plugin API call limit breach triggers throttle
- [ ] TEST-FAIL-RT-010: Plugin event emission limit breach triggers throttle
- [ ] TEST-FAIL-RT-011: One plugin crash does not affect other plugins
- [ ] TEST-FAIL-RT-012: One plugin crash does not affect core system
- [ ] TEST-FAIL-RT-013: Plugin error count tracked accurately
- [ ] TEST-FAIL-RT-014: Plugin recovery after transient error
- [ ] TEST-FAIL-RT-015: Plugin stop() exception handled (cleanup continues)
Dependency Failures (10 tests)
- [ ] TEST-FAIL-DEP-001: Redis unavailable: plugin manager uses in-memory fallback
- [ ] TEST-FAIL-DEP-002: Neo4j unavailable: audit deferred, KG extensions skipped
- [ ] TEST-FAIL-DEP-003: Prolog unavailable: plugin governance defaults to deny
- [ ] TEST-FAIL-DEP-004: Kernel unavailable: plugin Volk/tool registration deferred
- [ ] TEST-FAIL-DEP-005: DI container unavailable: plugin load fails gracefully
- [ ] TEST-FAIL-DEP-006: Redis reconnection restores plugin registry
- [ ] TEST-FAIL-DEP-007: Neo4j reconnection processes deferred audit
- [ ] TEST-FAIL-DEP-008: Prolog reconnection re-evaluates plugin governance
- [ ] TEST-FAIL-DEP-009: All dependencies down: plugin manager reports unhealthy
- [ ] TEST-FAIL-DEP-010: Partial recovery reflected in plugin manager health
Hot-Reload Failures (5 tests)
- [ ] TEST-FAIL-HR-001: Hot-reload with invalid new manifest rolls back
- [ ] TEST-FAIL-HR-002: Hot-reload with broken code restores previous version
- [ ] TEST-FAIL-HR-003: Hot-reload preserves other plugins during failure
- [ ] TEST-FAIL-HR-004: Hot-reload timeout kills stuck plugin
- [ ] TEST-FAIL-HR-005: Concurrent hot-reload requests serialized
Summary Statistics
| Category | Test Count | Completed | Percentage |
|---|---|---|---|
| Unit Tests | 140 | 0 | 0% |
| Integration Tests | 100 | 0 | 0% |
| Contract Tests | 40 | 0 | 0% |
| Performance Tests | 50 | 0 | 0% |
| Compliance Tests | 11 | 0 | 0% |
| Failure Scenario Tests | 40 | 0 | 0% |
| TOTAL | 381 | 0 | 0% |
Last Updated: 2026-03-27 Version: 1.0.0 Maintained By: Architecture Team
Other tracked files
specs/015-plugin-sdk/compliance-audit.md
Full spec text
Verbatim from specs/015-plugin-sdk/spec.md; headings are pushed down two levels.
SPEC-015: Plugin SDK
Metadata
- Spec ID: 015-plugin-sdk
- Title: Plugin SDK
- Author: Mohamed
- Status: Implemented
- Created: 2026-03-27
- Last Updated: 2026-03-28
- Priority: P1
- Version: 1.0.0
Overview
What
The Plugin SDK is the extensibility framework for Orchestrata. It defines a public API surface and runtime for third-party and first-party plugins that extend the system with new Volk types, tools, LLM providers, channel adapters, governance rules, and knowledge graph extensions — all installable and loadable at runtime without modifying the monorepo or rebuilding the system.
Why
- Orchestrata's 21 packages are tightly coupled workspace dependencies — adding new capabilities requires monorepo modifications
- No mechanism for external developers to contribute Volks, tools, or providers
- No runtime extension loading — all components are statically imported at startup
- The existing Volk types (CodeDirector, QA, Architect, PM, UX, Concierge) are hardcoded; adding new specialized agents requires new packages
- OpenClaw's plugin architecture (87 extensions, runtime install, SDK with 20+ subpaths) demonstrates the value of a mature extension system for multi-agent platforms
- Moving specialized Volks and providers to plugins reduces core complexity and enables independent versioning
How
- IPluginManager interface registered in DI container as ComponentType.PLUGIN_MANAGER
- Plugins are Python packages with a
plugin.yamlmanifest declaring capabilities, dependencies, and entry points - Plugin discovery: local directory scan + pip-installed packages with
orchestrata_pluginentry point group - Plugin lifecycle: discover → validate → load → initialize → start → stop → unload
- Plugin SDK defines public interfaces: IPlugin, PluginContext, tool registration, Volk registration, provider registration, governance rule registration
- PAM++ governance controls which plugins are allowed, what capabilities they can access, and resource limits
- All plugin operations logged to audit trail
User Scenarios
US-001: Install and Load a Plugin (P1)
As a system operator I want to install a plugin from a package or local directory So that I can extend Orchestrata with new capabilities without modifying core code
Acceptance Criteria:
- [ ] Plugin installed from pip package:
orchestrata plugins install orchestrata-plugin-browser - [ ] Plugin installed from local path:
orchestrata plugins install ./my-plugin - [ ] Plugin discovered via
orchestrata_pluginentry point group or local scan - [ ] Plugin manifest (
plugin.yaml) validated before loading - [ ] Plugin dependencies checked against installed packages
- [ ] Plugin initialized and started as part of system lifecycle
- [ ] Failed plugin load does not crash the system
US-002: Develop a Custom Volk Plugin (P1)
As a plugin developer I want to create a new specialized Volk (agent) as a plugin So that I can add domain-specific agent capabilities to Orchestrata
Acceptance Criteria:
- [ ] Plugin SDK provides
IPlugininterface withregister_volks()method - [ ] Plugin registers Volk with alias, capabilities, and handler function
- [ ] Registered Volk appears in kernel Volk registry and is routable via CXP
- [ ] Volk receives CXP packets and responds through standard CXP protocol
- [ ] Volk participates in PAM++ governance (ownership, permissions, escalation)
- [ ] Volk has access to PluginContext for DI resolution and configuration
US-003: Develop a Tool Plugin (P1)
As a plugin developer I want to create new tools that Volks can invoke So that agents gain new capabilities (web search, browser automation, code execution, etc.)
Acceptance Criteria:
- [ ] Plugin SDK provides
register_tool()method on PluginContext - [ ] Tool definition includes: name, description, parameters_schema, handler function
- [ ] Registered tools discoverable by Volks via tool registry
- [ ] Tool invocation goes through PAM++ authorization
- [ ] Tool execution results returned to calling Volk via CXP
- [ ] Tool errors handled gracefully with structured error responses
US-004: Develop a Provider Plugin (P1)
As a plugin developer I want to add a new LLM provider as a plugin So that Orchestrata can use additional model backends without core changes
Acceptance Criteria:
- [ ] Plugin SDK provides
register_provider()method on PluginContext - [ ] Provider implements IProvider protocol (SPEC-011)
- [ ] Provider registered with ProviderManager via DI-resolved IProviderManager
- [ ] Provider participates in fallback chains, cost tracking, and rate limiting
- [ ] Provider configuration loaded from plugin config section
- [ ] Provider health monitored by ProviderManager health system
US-005: Develop a Governance Rule Plugin (P2)
As a plugin developer I want to add custom Prolog governance rules as a plugin So that I can extend PAM++ policies without modifying core rule files
Acceptance Criteria:
- [ ] Plugin SDK provides
register_rules()method on PluginContext - [ ] Plugin declares Prolog rule files in manifest
- [ ] Rules loaded into Prolog knowledge base on plugin initialize
- [ ] Rules unloaded from Prolog knowledge base on plugin unload
- [ ] Rule conflicts detected and reported (overlapping predicates)
- [ ] PAM++ validates plugin rules against constitutional articles
US-006: Develop a Knowledge Graph Extension Plugin (P2)
As a plugin developer I want to extend the knowledge graph with new node types and relationships So that the graph captures domain-specific knowledge
Acceptance Criteria:
- [ ] Plugin SDK provides
register_kg_schema()method on PluginContext - [ ] Plugin declares new node types, relationship types, and indexes
- [ ] Schema extensions applied to Neo4j on plugin initialize
- [ ] Schema migrations versioned per plugin
- [ ] Schema conflicts with core or other plugins detected and reported
- [ ] Plugin KG data namespaced to prevent collisions
US-007: Plugin Discovery and Listing (P1)
As a system operator I want to list, inspect, enable, and disable plugins So that I can manage the extension ecosystem
Acceptance Criteria:
- [ ] CLI command
orchestrata plugins listshows all discovered plugins with status - [ ] CLI command
orchestrata plugins info <name>shows plugin manifest details - [ ] CLI command
orchestrata plugins enable <name>activates a disabled plugin - [ ] CLI command
orchestrata plugins disable <name>deactivates a plugin without uninstalling - [ ] CLI command
orchestrata plugins uninstall <name>removes a plugin - [ ] Plugin status visible via Gateway RPC method
plugins.list(SPEC-014)
US-008: Plugin Configuration (P1)
As a system operator I want to configure plugins via the standard configuration system So that plugin behavior is customizable without code changes
Acceptance Criteria:
- [ ] Plugin declares configuration schema in manifest (JSON Schema)
- [ ] Plugin config loaded from
configs/monolithic.yamlunderplugins.<name>key - [ ] Plugin config supports environment variable interpolation
- [ ] Plugin config validated against declared schema on load
- [ ] Invalid config prevents plugin from loading with clear error message
- [ ] Plugin accesses its config via
PluginContext.config
US-009: Plugin Isolation and Security (P1)
As a security administrator I want to control what plugins can access and do So that untrusted plugins cannot compromise the system
Acceptance Criteria:
- [ ] PAM++ controls which plugins are allowed to load (plugin allowlist)
- [ ] Plugins declare required capabilities in manifest (DI access, Redis, Neo4j, network, filesystem)
- [ ] Capability requests validated against PAM++ policy before granting
- [ ] Plugins cannot access DI components beyond declared capabilities
- [ ] Plugin resource usage tracked (memory, CPU, API calls)
- [ ] Plugin resource limits configurable and enforced
- [ ] Plugin errors isolated — one plugin crash does not affect others or core
US-010: Plugin Lifecycle Management (P1)
As the system I want to manage plugin lifecycle through well-defined phases So that plugins are loaded, initialized, started, and stopped cleanly
Acceptance Criteria:
- [ ] Plugin lifecycle: discover → validate → load → initialize → start → stop → unload
- [ ] Plugin can declare dependencies on other plugins
- [ ] Plugin load order respects dependency graph (topological sort)
- [ ] Circular plugin dependencies detected and rejected
- [ ] Plugin can be hot-reloaded (stop → unload → load → initialize → start)
- [ ] All lifecycle transitions logged to audit trail
- [ ] Lifecycle errors do not cascade to other plugins
US-011: Plugin Event Hooks (P2)
As a plugin developer I want to hook into system events So that my plugin can react to kernel, session, and CXP events
Acceptance Criteria:
- [ ] Plugin SDK provides
subscribe_event()method on PluginContext - [ ] Plugin can subscribe to kernel EventBus topics
- [ ] Event subscriptions scoped to plugin lifetime (auto-unsubscribed on stop)
- [ ] Plugin can emit custom events to EventBus
- [ ] Custom events namespaced to plugin (e.g.,
plugin.<name>.custom_event) - [ ] Event handling errors do not crash the EventBus
US-012: Channel Adapter Plugin (P2)
As a plugin developer I want to add a new messaging channel (Slack, Telegram, Matrix) as a plugin So that Orchestrata can communicate through additional external surfaces
Acceptance Criteria:
- [ ] Plugin SDK provides
register_channel()method on PluginContext - [ ] Channel implements IInboundChannel (receive) and IOutboundChannel (send) protocols
- [ ] Channel registered with kernel channel registry
- [ ] Inbound messages converted to CXP packets and routed through kernel
- [ ] Outbound CXP packets converted to channel-native messages
- [ ] Channel configuration (tokens, webhooks) loaded from plugin config
Terminology
- Plugin: A self-contained, installable extension that adds capabilities to Orchestrata without modifying the core monorepo
- Plugin Manifest: A
plugin.yamlfile declaring the plugin's metadata, capabilities, dependencies, and entry point - Capability: A declared permission (e.g.,
redis,neo4j,network) that a plugin requires and must be granted - Entry Point: The Python import path to the class implementing the
IPluginprotocol - Plugin Lifecycle: The ordered sequence of states: discovered, loading, active, error, disabled
- Scoped DI Container: A dependency injection container filtered to only expose services matching a plugin's declared capabilities
- Hot Reload: The ability to stop, unload, reload, and restart a plugin at runtime without restarting the system
- Allowlist: An explicit list of plugin names permitted to load (when
allowlist_modeis "explicit") - Blocklist: A list of plugin names that are always rejected regardless of allowlist mode
- Plugin Source: The origin of a plugin — local directory path, pip package spec, or git URL
- KG Schema Extension: A plugin's declaration of new Neo4j node types, relationship types, and indexes
Data Models
PluginManifest Schema (plugin.yaml)
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "PluginManifest",
"type": "object",
"required": ["name", "version", "description", "author", "license", "entry_point", "min_orchestrata_version"],
"properties": {
"name": {
"type": "string",
"pattern": "^[a-z][a-z0-9-]*$",
"description": "Unique plugin name in kebab-case."
},
"version": {
"type": "string",
"pattern": "^\\d+\\.\\d+\\.\\d+$",
"description": "Semantic version of the plugin."
},
"description": {
"type": "string",
"maxLength": 500
},
"author": {
"type": "string"
},
"license": {
"type": "string",
"description": "SPDX license identifier."
},
"entry_point": {
"type": "string",
"pattern": "^[a-z_][a-z0-9_.]*:[A-Z][a-zA-Z0-9]*$",
"description": "Python import path in module:Class format."
},
"capabilities": {
"type": "array",
"items": {
"type": "string",
"enum": ["di_access", "redis", "neo4j", "network", "filesystem", "prolog", "event_bus", "provider", "gateway"]
},
"default": []
},
"dependencies": {
"type": "array",
"items": {"type": "string"},
"description": "Other plugin names this plugin depends on."
},
"python_dependencies": {
"type": "array",
"items": {"type": "string"},
"description": "Pip package specifications."
},
"config_schema": {
"oneOf": [
{"type": "object"},
{"type": "null"}
],
"description": "JSON Schema for plugin-specific configuration."
},
"min_orchestrata_version": {
"type": "string",
"pattern": "^\\d+\\.\\d+\\.\\d+$"
},
"prolog_rules": {
"type": "array",
"items": {"type": "string"},
"description": "Relative paths to Prolog rule files."
},
"kg_schema": {
"oneOf": [
{
"type": "object",
"properties": {
"node_types": {"type": "array", "items": {"type": "string"}},
"relationship_types": {"type": "array", "items": {"type": "string"}},
"indexes": {"type": "array", "items": {"type": "string"}},
"constraints": {"type": "array", "items": {"type": "string"}},
"migration_version": {"type": "integer", "minimum": 1}
}
},
{"type": "null"}
]
}
},
"additionalProperties": false
}
PluginContext Interface Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "PluginContext",
"description": "Runtime context provided to plugins during initialization.",
"type": "object",
"required": ["config", "logger", "plugin_dir", "manifest"],
"properties": {
"config": {
"type": "object",
"description": "Plugin-specific configuration validated against manifest config_schema."
},
"logger": {
"type": "object",
"description": "Plugin-scoped logging.Logger instance."
},
"plugin_dir": {
"type": "string",
"description": "Absolute path to the plugin's directory on the filesystem."
},
"manifest": {
"$ref": "#/definitions/PluginManifest"
}
}
}
PluginInfo Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "PluginInfo",
"type": "object",
"required": ["manifest", "status", "source"],
"properties": {
"manifest": {
"$ref": "#/definitions/PluginManifest"
},
"status": {
"type": "string",
"enum": ["discovered", "loading", "active", "error", "disabled"]
},
"source": {
"type": "string",
"pattern": "^(local|pip|git):.*$",
"description": "Plugin origin (e.g., local:/path, pip:package-name, git:url)."
},
"loaded_at": {
"type": ["string", "null"],
"format": "date-time"
},
"error": {
"type": ["string", "null"],
"description": "Error message if status is 'error'."
},
"registered_volks": {
"type": "array",
"items": {"type": "string"}
},
"registered_tools": {
"type": "array",
"items": {"type": "string"}
},
"registered_providers": {
"type": "array",
"items": {"type": "string"}
},
"registered_channels": {
"type": "array",
"items": {"type": "string"}
},
"resource_usage": {
"type": "object",
"properties": {
"memory_bytes": {"type": "integer"},
"api_calls_last_hour": {"type": "integer"},
"events_emitted_last_hour": {"type": "integer"},
"errors_last_hour": {"type": "integer"}
}
}
}
}
Plugin Registration Event Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "PluginRegistrationEvent",
"type": "object",
"required": ["plugin_name", "event_type", "timestamp"],
"properties": {
"plugin_name": {"type": "string"},
"event_type": {
"type": "string",
"enum": ["discovered", "loaded", "started", "stopped", "unloaded", "error", "enabled", "disabled"]
},
"timestamp": {"type": "string", "format": "date-time"},
"details": {"type": "object"}
}
}
Functional Requirements
Core Plugin Framework
FR-001: IPluginManager Interface (P1)
- System SHALL define IPluginManager as an abstract base class in orchestrata-core interfaces
- System SHALL register IPluginManager with ComponentType.PLUGIN_MANAGER in DI container
- System SHALL implement IComponent lifecycle (initialize, start, stop, get_health)
- System SHALL accept PluginManagerConfig with: plugin_dirs, enabled_plugins, disabled_plugins, resource_limits
FR-002: Plugin Discovery (P1)
- System SHALL discover plugins from configurable local directories (default:
~/.orchestrata/plugins/) - System SHALL discover plugins via Python
importlib.metadataentry point grouporchestrata_plugin - System SHALL discover plugins from pip-installed packages with
orchestrata-plugin-prefix - System SHALL deduplicate plugins by name (local takes precedence over installed)
- System SHALL log all discovered plugins with source and version
FR-003: Plugin Validation (P1)
- System SHALL validate plugin manifest (
plugin.yaml) against schema before loading - System SHALL validate plugin dependencies (other plugins and Python packages)
- System SHALL validate plugin capability declarations against known capabilities
- System SHALL reject plugins with invalid manifests with clear error messages
- System SHALL reject plugins with unsatisfied dependencies
FR-004: Plugin Loading (P1)
- System SHALL load plugin Python modules via importlib
- System SHALL instantiate plugin entry point class (declared in manifest)
- System SHALL pass PluginContext to plugin on instantiation
- System SHALL load plugins in dependency order (topological sort)
- System SHALL detect and reject circular plugin dependencies
FR-005: Plugin Lifecycle (P1)
- System SHALL call plugin lifecycle methods in order: initialize → start → stop → unload
- System SHALL call initialize() after loading, before any other method
- System SHALL call start() after all plugins initialized
- System SHALL call stop() on system shutdown or plugin disable
- System SHALL support hot-reload: stop → unload → load → initialize → start
Plugin SDK
FR-006: IPlugin Interface (P1)
- System SHALL define IPlugin protocol with: initialize(), start(), stop(), get_manifest()
- System SHALL define PluginManifest dataclass with: name, version, description, author, capabilities, dependencies, entry_point, config_schema
- System SHALL define PluginContext with: config, di_container (scoped), logger, event_bus
- System SHALL provide typed accessors on PluginContext for common DI components
- System SHALL document IPlugin with examples and inline documentation
FR-007: Volk Registration (P1)
- System SHALL provide
PluginContext.register_volk()method - System SHALL accept: alias, capabilities, protocols, handler (async callable)
- System SHALL register the Volk with the kernel's Volk registry
- System SHALL validate alias uniqueness (conflict with core or other plugins)
- System SHALL auto-deregister plugin Volks on plugin stop
FR-008: Tool Registration (P1)
- System SHALL provide
PluginContext.register_tool()method - System SHALL accept: name, description, parameters_schema (JSON Schema), handler (async callable)
- System SHALL register tools with a central tool registry accessible to all Volks
- System SHALL validate tool name uniqueness (namespaced:
plugin.<plugin_name>.<tool_name>) - System SHALL auto-deregister plugin tools on plugin stop
FR-009: Provider Registration (P1)
- System SHALL provide
PluginContext.register_provider()method - System SHALL accept: name, provider (IProvider), config (ProviderConfig)
- System SHALL register provider with IProviderManager (SPEC-011)
- System SHALL validate provider name uniqueness
- System SHALL auto-deregister plugin providers on plugin stop
FR-010: Governance Rule Registration (P2)
- System SHALL provide
PluginContext.register_rules()method - System SHALL accept: list of Prolog rule file paths relative to plugin directory
- System SHALL load rules into Prolog knowledge base during initialize
- System SHALL unload rules from Prolog knowledge base on plugin stop
- System SHALL detect and report conflicting predicates
FR-011: Knowledge Graph Extension (P2)
- System SHALL provide
PluginContext.register_kg_schema()method - System SHALL accept: node_types (list), relationship_types (list), indexes (list), constraints (list)
- System SHALL apply schema to Neo4j on plugin initialize
- System SHALL namespace plugin KG entities (label prefix:
Plugin_<name>_) - System SHALL version schema per plugin for migration support
FR-012: Channel Registration (P2)
- System SHALL provide
PluginContext.register_channel()method - System SHALL accept: channel_id, inbound_handler (async callable), outbound_handler (async callable)
- System SHALL register channel with kernel channel registry
- System SHALL validate channel_id uniqueness
- System SHALL auto-deregister plugin channels on plugin stop
FR-013: Event Subscription (P2)
- System SHALL provide
PluginContext.subscribe_event()method - System SHALL accept: topic (string with wildcard support), handler (async callable)
- System SHALL scope subscriptions to plugin lifetime (auto-unsubscribe on stop)
- System SHALL provide
PluginContext.emit_event()for custom events - System SHALL namespace custom events:
plugin.<name>.<event_type>
Plugin Management CLI
FR-014: Plugin Install (P1)
- System SHALL implement
orchestrata plugins install <source>CLI command - System SHALL support sources: pip package spec, local directory path, git URL
- System SHALL install pip packages to plugin virtualenv or system packages
- System SHALL validate manifest after installation
- System SHALL run plugin initialization check (import + validate)
FR-015: Plugin Uninstall (P1)
- System SHALL implement
orchestrata plugins uninstall <name>CLI command - System SHALL stop and unload plugin before removal
- System SHALL remove plugin files or pip package
- System SHALL clean up plugin state from Redis and Neo4j
- System SHALL log uninstall to audit trail
FR-016: Plugin List and Info (P1)
- System SHALL implement
orchestrata plugins listCLI command - System SHALL show: name, version, status (enabled/disabled/error), capabilities, source
- System SHALL implement
orchestrata plugins info <name>CLI command - System SHALL show full manifest, config schema, registered components, health
- System SHALL implement
orchestrata plugins enable/disable <name>commands
Plugin Configuration
FR-017: Plugin Config Schema (P1)
- System SHALL load plugin configuration from
configs/monolithic.yamlunderplugins.<name> - System SHALL validate plugin config against schema declared in manifest (JSON Schema)
- System SHALL support environment variable interpolation in plugin config
- System SHALL provide config to plugin via
PluginContext.config - System SHALL reject plugin load on invalid config with clear validation errors
FR-018: Plugin Config Hot-Reload (P2)
- System SHALL detect plugin config changes in configuration file
- System SHALL notify plugin of config change via
on_config_changed(new_config)callback - System SHALL validate new config before notifying plugin
- System SHALL rollback to previous config if plugin rejects the change
- System SHALL log all config changes to audit trail
Isolation and Security
FR-019: Capability-Based Access Control (P1)
- System SHALL define capability types: di_access, redis, neo4j, network, filesystem, prolog, event_bus
- System SHALL enforce that plugins only access DI components matching declared capabilities
- System SHALL wrap DI container in a scoped proxy that filters by plugin capabilities
- System SHALL reject undeclared capability access with CapabilityDeniedError
- System SHALL log all capability access violations
FR-020: PAM++ Plugin Governance (P1)
- System SHALL extend PAM++ with plugin-specific Prolog predicates:
plugin_allowed(PluginName)— Plugin load authorizationplugin_capability_granted(PluginName, Capability)— Per-capability authorizationplugin_resource_limit(PluginName, Resource, Limit)— Resource limit lookupplugin_can_register_volk(PluginName, VolkAlias)— Volk registration authorizationplugin_can_register_tool(PluginName, ToolName)— Tool registration authorization
- System SHALL query Prolog before granting any plugin capability
- System SHALL log all plugin governance decisions to audit trail
FR-021: Plugin Resource Limits (P1)
- System SHALL enforce configurable resource limits per plugin
- System SHALL track plugin memory usage
- System SHALL track plugin API call count (per time window)
- System SHALL throttle or disable plugins exceeding limits
- System SHALL emit resource warning events at configurable thresholds
FR-022: Plugin Error Isolation (P1)
- System SHALL catch all exceptions from plugin code (lifecycle methods, handlers, event callbacks)
- System SHALL log plugin exceptions with full traceback and plugin context
- System SHALL continue system operation when a plugin fails
- System SHALL mark plugin status as "error" after repeated failures (configurable threshold, default: 5)
- System SHALL optionally auto-disable plugins in persistent error state
Audit Trail
FR-023: Plugin Audit (P1)
- System SHALL record all plugin lifecycle events: install, load, initialize, start, stop, unload, uninstall
- System SHALL record all plugin registration events: volk, tool, provider, channel, rules
- System SHALL record all plugin governance decisions: capability grants, denials, resource limit hits
- System SHALL record all plugin errors with context
- System SHALL store audit records in Neo4j (:PluginAudit) nodes
Error Conditions
| ID | Condition | Handling |
|---|---|---|
| ERR-001 | Plugin manifest fails JSON Schema validation | Plugin remains in discovered status; PluginInfo.error contains validation error message |
| ERR-002 | Plugin declares dependency on another plugin that is not installed | Load fails with PluginDependencyError; dependent plugin remains in discovered status |
| ERR-003 | Plugin declares min_orchestrata_version higher than running version |
Load fails with PluginVersionError |
| ERR-004 | Plugin entry_point import fails (module not found or class not found) |
Plugin transitions to error status with ImportError details |
| ERR-005 | Plugin initialize() method raises an exception |
Plugin transitions to error status; partially registered components are rolled back |
| ERR-006 | Plugin attempts to register a Volk with a reserved alias (U:Ox, U:PS, U:KR) | Registration rejected with PluginAuthorizationError |
| ERR-007 | Plugin attempts to access DI service outside declared capabilities | Scoped container raises CapabilityDeniedError |
| ERR-008 | Plugin exceeds configured resource limit (memory, API calls, events) | Plugin is throttled and warning event is emitted |
| ERR-009 | Plugin reaches error_threshold (default: 5) consecutive errors |
System auto-disables plugin and emits plugin.auto_disabled event |
| ERR-010 | Two plugins declare conflicting KG schema extensions (same node type or relationship type) | Second plugin load fails with PluginConflictError |
| ERR-011 | Plugin Prolog rule file contains syntax errors | Rule loading fails; plugin transitions to error status without affecting existing Prolog knowledge base |
| ERR-012 | PAM++ denies plugin load authorization (plugin_allowed(Name) fails) |
Plugin remains in discovered status with error set to "governance_denied" |
| ERR-013 | Plugin is unloaded while it has active EventBus subscriptions | All subscriptions are automatically unsubscribed before unload completes |
| ERR-014 | Plugin hot-reload fails during reload cycle | System attempts to restore previous version; if restoration fails, plugin enters error status |
| ERR-015 | Circular dependencies detected between plugins | All plugins in the cycle fail to load with PluginCircularDependencyError |
API Contracts
Contract: Plugin Manifest Schema (contracts/plugins/manifest-v1.0.0.yaml)
info:
title: Orchestrata Plugin Manifest Schema
version: 1.0.0
spec_id: 015-plugin-sdk
spec_version: 1.0.0
maturity: experimental
schemas:
PluginManifest:
type: object
required: [name, version, description, author, license, entry_point, min_orchestrata_version]
properties:
name:
type: string
pattern: "^[a-z][a-z0-9-]*$"
version:
type: string
pattern: "^\\d+\\.\\d+\\.\\d+$"
description:
type: string
maxLength: 500
author:
type: string
license:
type: string
entry_point:
type: string
pattern: "^[a-z_][a-z0-9_.]*:[A-Z][a-zA-Z0-9]*$"
capabilities:
type: array
items:
type: string
enum: [di_access, redis, neo4j, network, filesystem, prolog, event_bus, provider, gateway]
dependencies:
type: array
items:
type: string
python_dependencies:
type: array
items:
type: string
config_schema:
oneOf:
- type: object
- type: "null"
min_orchestrata_version:
type: string
prolog_rules:
type: array
items:
type: string
kg_schema:
oneOf:
- type: object
- type: "null"
PluginInfo:
type: object
required: [manifest, status, source]
properties:
manifest:
$ref: "#/schemas/PluginManifest"
status:
type: string
enum: [discovered, loading, active, error, disabled]
source:
type: string
loaded_at:
type: string
format: date-time
nullable: true
error:
type: string
nullable: true
registered_volks:
type: array
items:
type: string
registered_tools:
type: array
items:
type: string
registered_providers:
type: array
items:
type: string
registered_channels:
type: array
items:
type: string
resource_usage:
type: object
properties:
memory_bytes:
type: integer
api_calls_last_hour:
type: integer
events_emitted_last_hour:
type: integer
errors_last_hour:
type: integer
PluginLifecycleEvent:
type: object
required: [plugin_name, event_type, timestamp]
properties:
plugin_name:
type: string
event_type:
type: string
enum: [discovered, loaded, started, stopped, unloaded, error, enabled, disabled]
timestamp:
type: string
format: date-time
details:
type: object
rpc_methods:
plugin.list:
description: List all discovered plugins with status
scope_required: read_only
result:
type: array
items:
$ref: "#/schemas/PluginInfo"
plugin.install:
description: Install a plugin from source
scope_required: admin
params:
type: object
required: [source]
properties:
source:
type: string
result:
$ref: "#/schemas/PluginManifest"
plugin.uninstall:
description: Uninstall a plugin
scope_required: admin
params:
type: object
required: [name]
properties:
name:
type: string
result:
type: object
required: [uninstalled]
properties:
uninstalled:
type: boolean
plugin.enable:
description: Enable a disabled plugin
scope_required: admin
params:
type: object
required: [name]
properties:
name:
type: string
result:
type: object
required: [status]
properties:
status:
type: string
plugin.disable:
description: Disable an active plugin
scope_required: admin
params:
type: object
required: [name]
properties:
name:
type: string
result:
type: object
required: [status]
properties:
status:
type: string
performance:
discovery_p95_ms: 500
validation_p95_ms: 100
initialization_p95_ms: 2000
full_startup_50_plugins_p95_s: 10
tool_invocation_overhead_p95_ms: 5
capability_check_p95_ms: 1
security:
capability_enforcement: true
governance_authorization: true
resource_limits: true
audit_trail: true
observability:
emits_metrics: true
metric_names:
- orchestrata_plugins_active
- orchestrata_plugin_load_duration_seconds
- orchestrata_plugin_errors_total
- orchestrata_plugin_tool_invocations_total
- orchestrata_plugin_events_emitted_total
spec_traceability:
spec_id: "015-plugin-sdk"
spec_version: "1.0.0"
user_stories:
- "US-001"
- "US-002"
- "US-003"
- "US-004"
- "US-005"
- "US-006"
- "US-007"
- "US-008"
- "US-009"
- "US-010"
Non-Functional Requirements
Performance
NFR-001: Plugin Loading
- Plugin discovery: < 500ms for 50 plugins (P95)
- Plugin validation: < 100ms per plugin (P95)
- Plugin initialization: < 2s per plugin (P95)
- Full plugin system startup: < 10s for 50 plugins (P95)
NFR-002: Plugin Runtime
- Tool invocation overhead (plugin SDK layer): < 5ms (P95)
- Volk message routing to plugin Volk: < 10ms overhead (P95)
- Event delivery to plugin subscribers: < 50ms overhead (P95)
- Capability check: < 1ms (P95, cached)
PR-001: Plugin discovery - SHALL complete within 500ms for up to 50 plugins at the 95th percentile.
PR-002: Plugin validation - Individual plugin manifest validation SHALL complete within 100ms at the 95th percentile.
PR-003: Plugin initialization - Individual plugin initialization (import + instantiate + initialize()) SHALL complete within 2 seconds at the 95th percentile.
PR-004: Full startup - Full plugin system startup for 50 plugins SHALL complete within 10 seconds at the 95th percentile.
PR-005: Tool invocation - Tool invocation overhead introduced by the plugin SDK layer SHALL be < 5ms at the 95th percentile.
PR-006: CXP routing - CXP message routing to a plugin-registered Volk SHALL add < 10ms overhead at the 95th percentile compared to core Volk routing.
PR-007: Event delivery - Event delivery to plugin event subscribers SHALL add < 50ms overhead at the 95th percentile.
PR-008: Capability checks - Capability permission checks SHALL complete within 1ms at the 95th percentile (cached).
PR-009: Plugin unload - SHALL complete within 5 seconds, including deregistration of all components and EventBus unsubscription.
PR-010: Hot-reload - Plugin hot-reload SHALL complete within 10 seconds (stop + unload + load + initialize + start).
PR-011: Memory overhead - The plugin system SHALL not increase core system memory usage by more than 50MB when no plugins are loaded.
Reliability
NFR-003: Isolation
- Plugin failure does not crash core system
- Plugin failure does not affect other plugins
- Plugin resource exhaustion does not degrade core performance
- Plugin unload cleans up all registered components
NFR-004: Data Integrity
- Plugin KG extensions do not corrupt core graph
- Plugin Prolog rules do not override core governance
- Plugin config validation prevents invalid state
- Plugin audit trail immutable after write
Security
- Plugin capability declarations validated against allowlist before loading
- Plugin-scoped DI container enforces capability-based access control
- Plugin resource limits enforced (memory, API calls, events)
- Plugin errors isolated to prevent cascade failures
- All plugin operations logged to audit trail
Supply Chain
NFR-006: Supply Chain
- Plugin manifest signature verification (future, P3)
- Plugin source tracking in audit trail
- Plugin version pinning in configuration
- Plugin dependency vulnerability scanning (future, P3)
Technical Design
Architecture
┌──────────────────────────────────────────────────────────────┐
│ Plugin SDK System │
├──────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Plugin │ │ Plugin │ │ Plugin │ │
│ │ Manager │──│ Loader │──│ Registry │ │
│ │ │ │ │ │ │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Plugin │ │ Capability │ │ Scoped DI │ │
│ │ Lifecycle │ │ Enforcer │ │ Container │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Plugin SDK (Public API) │ │
│ │ IPlugin · PluginContext · register_volk() │ │
│ │ register_tool() · register_provider() │ │
│ │ register_channel() · register_rules() │ │
│ │ subscribe_event() · emit_event() │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Kernel │ │ Prolog │ │ Neo4j │ │
│ │ (Volk Reg, │ │ (PAM++ │ │ (Audit, │ │
│ │ EventBus) │ │ Plugin │ │ KG Ext) │ │
│ │ │ │ Rules) │ │ │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└──────────────────────────────────────────────────────────────┘
Key Interfaces
class IPluginManager(IComponent, ABC):
"""Plugin lifecycle and registry management."""
@abstractmethod
async def discover_plugins(self) -> list[PluginManifest]:
"""Discover available plugins from configured sources."""
@abstractmethod
async def load_plugin(self, name: str) -> None:
"""Load and initialize a plugin by name."""
@abstractmethod
async def unload_plugin(self, name: str) -> None:
"""Stop and unload a plugin by name."""
@abstractmethod
async def reload_plugin(self, name: str) -> None:
"""Hot-reload a plugin (stop → unload → load → initialize → start)."""
@abstractmethod
def get_plugin(self, name: str) -> PluginInfo | None:
"""Get plugin info by name. Returns None if not found."""
@abstractmethod
def list_plugins(self) -> list[PluginInfo]:
"""List all discovered plugins with status."""
@abstractmethod
async def enable_plugin(self, name: str) -> None:
"""Enable a disabled plugin."""
@abstractmethod
async def disable_plugin(self, name: str) -> None:
"""Disable an enabled plugin without uninstalling."""
@abstractmethod
async def install_plugin(self, source: str) -> PluginManifest:
"""Install a plugin from source (pip spec, local path, git URL)."""
@abstractmethod
async def uninstall_plugin(self, name: str) -> None:
"""Uninstall a plugin completely (stop, unload, remove)."""
class IPlugin(Protocol):
"""Protocol that all plugins must implement."""
async def initialize(self, context: PluginContext) -> None:
"""Initialize the plugin. Register components here."""
async def start(self) -> None:
"""Start the plugin. Called after all plugins initialized."""
async def stop(self) -> None:
"""Stop the plugin. Clean up resources here."""
def get_manifest(self) -> PluginManifest:
"""Return the plugin manifest."""
class PluginManifest:
name: str # Unique plugin name (kebab-case)
version: str # Semantic version
description: str # Human-readable description
author: str # Author name or organization
license: str # License identifier (SPDX)
entry_point: str # Python import path to IPlugin class
capabilities: list[PluginCapability] # Required capabilities
dependencies: list[str] # Other plugin names (load order)
python_dependencies: list[str] # pip package specs
config_schema: dict | None # JSON Schema for plugin config
min_orchestrata_version: str # Minimum compatible version
prolog_rules: list[str] # Relative paths to Prolog rule files
kg_schema: KGSchemaExtension | None # Knowledge graph schema extension
class PluginCapability(Enum):
DI_ACCESS = "di_access" # Access to DI container
REDIS = "redis" # Direct Redis access
NEO4J = "neo4j" # Direct Neo4j access
NETWORK = "network" # Outbound network access
FILESYSTEM = "filesystem" # Filesystem read/write
PROLOG = "prolog" # Prolog knowledge base access
EVENT_BUS = "event_bus" # Kernel EventBus access
PROVIDER = "provider" # IProviderManager access
GATEWAY = "gateway" # IGatewayServer access
class PluginContext:
config: dict # Plugin-specific configuration
logger: Logger # Plugin-scoped logger
plugin_dir: Path # Plugin directory on filesystem
manifest: PluginManifest # Plugin manifest
async def register_volk(
self,
alias: str,
capabilities: list[str],
handler: Callable,
) -> None:
"""Register a Volk with the kernel."""
async def register_tool(
self,
name: str,
description: str,
parameters_schema: dict,
handler: Callable,
) -> None:
"""Register a tool in the tool registry."""
async def register_provider(
self,
name: str,
provider: IProvider,
config: ProviderConfig,
) -> None:
"""Register an LLM provider with the ProviderManager."""
async def register_channel(
self,
channel_id: str,
inbound_handler: Callable,
outbound_handler: Callable,
) -> None:
"""Register a messaging channel adapter."""
async def register_rules(
self,
rule_files: list[str],
) -> None:
"""Load Prolog rule files into the knowledge base."""
async def register_kg_schema(
self,
node_types: list[str],
relationship_types: list[str],
indexes: list[str],
) -> None:
"""Extend the knowledge graph schema."""
async def subscribe_event(
self,
topic: str,
handler: Callable,
) -> str:
"""Subscribe to kernel EventBus topic. Returns subscription_id."""
async def emit_event(
self,
event_type: str,
payload: dict,
) -> None:
"""Emit a custom event (namespaced to plugin)."""
def resolve(self, interface: type) -> Any:
"""Resolve a component from the scoped DI container."""
class PluginInfo:
manifest: PluginManifest
status: PluginStatus # discovered | loading | active | error | disabled
source: str # "local:<path>" | "pip:<package>" | "git:<url>"
loaded_at: datetime | None
error: str | None
registered_volks: list[str]
registered_tools: list[str]
registered_providers: list[str]
registered_channels: list[str]
resource_usage: PluginResourceUsage
class PluginStatus(Enum):
DISCOVERED = "discovered"
LOADING = "loading"
ACTIVE = "active"
ERROR = "error"
DISABLED = "disabled"
class PluginResourceUsage:
memory_bytes: int
api_calls_last_hour: int
events_emitted_last_hour: int
errors_last_hour: int
class KGSchemaExtension:
node_types: list[str]
relationship_types: list[str]
indexes: list[str]
constraints: list[str]
migration_version: int
Plugin Manifest Format (plugin.yaml)
name: orchestrata-plugin-browser
version: 1.0.0
description: Browser automation tool for Orchestrata Volks
author: Mohamed
license: MIT
entry_point: orchestrata_plugin_browser.plugin:BrowserPlugin
min_orchestrata_version: "0.7.0"
capabilities:
- network
- di_access
- event_bus
dependencies: []
python_dependencies:
- playwright>=1.40.0
config_schema:
type: object
properties:
headless:
type: boolean
default: true
timeout_seconds:
type: integer
default: 30
max_concurrent_pages:
type: integer
default: 5
required: []
prolog_rules: []
kg_schema: null
Plugin Directory Structure
orchestrata-plugin-browser/
├── plugin.yaml # Plugin manifest
├── pyproject.toml # Python package metadata
├── src/
│ └── orchestrata_plugin_browser/
│ ├── __init__.py
│ ├── plugin.py # IPlugin implementation (entry point)
│ ├── browser_tool.py # Tool implementation
│ └── config.py # Plugin config model
├── prolog/ # Optional: Prolog governance rules
│ └── browser_rules.pl
├── tests/
│ ├── test_plugin.py
│ └── test_browser_tool.py
└── README.md
Plugin Loading Sequence
1. PluginManager.discover_plugins()
├── Scan plugin_dirs for plugin.yaml files
├── Query importlib.metadata for orchestrata_plugin entry points
└── Deduplicate by name (local > installed)
2. PluginManager.load_plugin(name)
├── Validate manifest schema
├── Check dependencies (plugins + python packages)
├── Query PAM++: plugin_allowed(name) → allow/deny
├── Import plugin module via importlib
├── Instantiate entry point class
├── Create scoped DI container (filtered by capabilities)
├── Create PluginContext
└── Call plugin.initialize(context)
├── Plugin registers volks, tools, providers, channels
├── Plugin registers Prolog rules
└── Plugin registers KG schema extensions
3. PluginManager.start_all()
└── For each loaded plugin (in dependency order):
└── Call plugin.start()
4. PluginManager.stop_all()
└── For each active plugin (reverse dependency order):
├── Call plugin.stop()
├── Auto-deregister all registered components
├── Unsubscribe from all EventBus topics
└── Unload Prolog rules
Redis State Persistence
Plugin registry:
orchestrata:plugin:registry:{name} — HASH {version, status, loaded_at, source, capabilities}
orchestrata:plugin:registry:active — SET of active plugin names
TTL: None (persistent). Cleaned on uninstall.
Plugin resource tracking:
orchestrata:plugin:resources:{name}:api — STRING (int, API calls this hour)
orchestrata:plugin:resources:{name}:evt — STRING (int, events emitted this hour)
orchestrata:plugin:resources:{name}:err — STRING (int, errors this hour)
TTL: 3600s (auto-reset hourly).
Neo4j Audit Schema
Nodes:
(:PluginAudit {audit_id, plugin_name, event_type, details, timestamp})(:Plugin {name, version, author, status, loaded_at})
Relationships:
(:PluginAudit)-[:FOR_PLUGIN]->(:Plugin)(:Plugin)-[:REGISTERS]->(:Volk)(:Plugin)-[:PROVIDES]->(:Tool)(:Plugin)-[:EXTENDS]->(:KGSchema)
Configuration
# configs/monolithic.yaml
plugin_manager:
enabled: true
plugin_dirs:
- "~/.orchestrata/plugins"
- "./plugins"
auto_discover: true
allowlist_mode: "explicit" # "explicit" | "open"
allowlist:
- orchestrata-plugin-browser
- orchestrata-plugin-slack
blocklist: []
resource_limits:
default_memory_bytes: 536870912 # 512MB
default_api_calls_per_hour: 10000
default_events_per_hour: 5000
error_threshold: 5 # Auto-disable after N consecutive errors
plugins:
orchestrata-plugin-browser:
headless: true
timeout_seconds: 30
max_concurrent_pages: 5
orchestrata-plugin-slack:
bot_token: «redacted»
signing_secret: «redacted»
channels:
- general
- engineering
Integration Points
| Component | Integration |
|---|---|
| orchestrata-core | IPluginManager interface, ComponentType.PLUGIN_MANAGER, IPlugin protocol |
| orchestrata-kernel | Volk registry for plugin Volks, EventBus for plugin events |
| orchestrata-di | Scoped DI container for capability-based access control |
| orchestrata-governance | PAM++ plugin authorization predicates |
| orchestrata-cxp | CXP routing for plugin-registered Volks |
| orchestrata-provider | Provider registration for plugin providers (SPEC-011) |
| orchestrata-knowledge | KG schema extension for plugin node/relationship types |
| orchestrata-session | Session-scoped plugin state (future) |
| orchestrata-gateway | Plugin management RPC methods (SPEC-014) |
| configs/monolithic.yaml | Plugin manager and per-plugin configuration |
Examples
Example: Minimal Plugin Implementation
plugin.yaml:
name: orchestrata-plugin-hello
version: 1.0.0
description: A minimal example plugin that registers a greeting tool
author: Example Author
license: MIT
entry_point: orchestrata_plugin_hello.plugin:HelloPlugin
min_orchestrata_version: "0.7.0"
capabilities:
- di_access
- event_bus
dependencies: []
python_dependencies: []
config_schema:
type: object
properties:
greeting:
type: string
default: "Hello from plugin!"
prolog_rules: []
kg_schema: null
plugin.py:
from orchestrata.plugins.sdk import IPlugin, PluginContext, PluginManifest
class HelloPlugin:
"""Minimal plugin demonstrating the IPlugin protocol."""
def __init__(self) -> None:
self._context: PluginContext | None = None
async def initialize(self, context: PluginContext) -> None:
self._context = context
greeting = context.config.get("greeting", "Hello from plugin!")
await context.register_tool(
name="hello_greet",
description="Returns a greeting message",
parameters_schema={
"type": "object",
"properties": {
"name": {"type": "string"}
},
"required": ["name"],
},
handler=self._greet,
)
context.logger.info("HelloPlugin initialized with greeting: %s", greeting)
async def start(self) -> None:
await self._context.emit_event("hello.started", {"status": "ready"})
async def stop(self) -> None:
self._context.logger.info("HelloPlugin stopping")
def get_manifest(self) -> PluginManifest:
return self._context.manifest
async def _greet(self, name: str) -> dict:
greeting = self._context.config.get("greeting", "Hello")
return {"message": f"{greeting}, {name}!"}
Example: Plugin Discovery and Loading
# System startup sequence
plugin_manager = container.resolve(IPluginManager)
# Discover all available plugins
manifests = await plugin_manager.discover_plugins()
# Returns: [PluginManifest(name="orchestrata-plugin-hello", ...), ...]
# Load a specific plugin
await plugin_manager.load_plugin("orchestrata-plugin-hello")
# Internally: validate → governance check → import → instantiate → initialize
# Query plugin status
info = plugin_manager.get_plugin("orchestrata-plugin-hello")
# Returns: PluginInfo(status=PluginStatus.ACTIVE, registered_tools=["hello_greet"], ...)
# List all plugins
all_plugins = plugin_manager.list_plugins()
# Returns: [PluginInfo(...), ...]
Example: Plugin Error (Capability Denied)
# Plugin declares capabilities: [di_access, event_bus]
# Plugin attempts to access Redis (not declared):
try:
redis = context.resolve(IRedisClient)
except CapabilityDeniedError as e:
# e.message: "Plugin 'orchestrata-plugin-hello' does not declare 'redis' capability"
# e.error_code: "CAPABILITY_DENIED"
# e.details: {"plugin": "orchestrata-plugin-hello", "capability": "redis"}
pass
Example: Plugin Volk Registration
async def initialize(self, context: PluginContext) -> None:
await context.register_volk(
alias="U:Br", # Browser agent
capabilities=["web-scraping", "screenshot", "form-fill"],
handler=self._handle_browser_task,
)
# Volk is now routable via CXP: {"target": "U:Br", ...}
Security
The Plugin SDK implements the following security controls:
Authentication and Authorization
- Plugin load authorization enforced via PAM++
plugin_allowed/1predicate - Plugin capability declarations validated against allowlist before loading
- Reserved Volk aliases protected from plugin override
Capability-Based Access Control
- Plugins declare required capabilities in manifest (di_access, redis, neo4j, network, filesystem, prolog, event_bus, provider, gateway)
- Scoped DI container filters services by declared capabilities
- Undeclared capability access raises
CapabilityDeniedError
Resource Limits
- Configurable memory limits per plugin (default: 512MB)
- Rate limiting for API calls and events per plugin
- Resource usage tracked in Redis with hourly reset
- Auto-disable plugins exceeding error threshold (default: 5 consecutive errors)
Audit Logging
- All plugin lifecycle events recorded to Neo4j (:PluginAudit nodes)
- Plugin governance decisions logged (capability grants/denials)
- Plugin errors logged with full traceback and plugin context
Error Isolation
- Plugin exceptions caught and isolated to prevent cascade failures
- Plugin errors do not affect core system or other plugins
- Failed plugins marked with "error" status
Governance
The Plugin SDK integrates with PAM++ (SPEC-007) for governance control:
Plugin Authorization Predicates
%% Plugin load authorization
plugin_allowed(PluginName) :-
plugin_in_allowlist(PluginName),
\+ plugin_in_blocklist(PluginName).
plugin_allowed(PluginName) :-
plugin_allowlist_mode(open),
\+ plugin_in_blocklist(PluginName).
%% Capability authorization
plugin_capability_granted(PluginName, Capability) :-
plugin_allowed(PluginName),
plugin_declares_capability(PluginName, Capability),
\+ capability_restricted(PluginName, Capability).
%% Resource limits
plugin_resource_limit(PluginName, memory_bytes, Limit) :-
plugin_memory_limit(PluginName, Limit).
plugin_resource_limit(_, memory_bytes, 536870912). % Default: 512MB
plugin_resource_limit(PluginName, api_calls_per_hour, Limit) :-
plugin_api_limit(PluginName, Limit).
plugin_resource_limit(_, api_calls_per_hour, 10000). % Default: 10K/hour
%% Registration authorization
plugin_can_register_volk(PluginName, VolkAlias) :-
plugin_capability_granted(PluginName, di_access),
\+ volk_alias_reserved(VolkAlias).
plugin_can_register_tool(PluginName, ToolName) :-
plugin_capability_granted(PluginName, di_access),
\+ tool_name_reserved(ToolName).
%% Reserved aliases (core Volks cannot be overridden)
volk_alias_reserved('U:Ox').
volk_alias_reserved('U:PS').
volk_alias_reserved('U:KR').
Governance Rule Registration
- Plugins can register custom Prolog governance rules via
register_rules()method - Rules loaded into Prolog knowledge base on plugin initialize
- Rules unloaded from Prolog knowledge base on plugin unload
- Rule conflicts detected and reported (overlapping predicates)
- PAM++ validates plugin rules against constitutional articles
Dependencies
- Python 3.14t
- importlib.metadata (plugin entry point discovery)
- orchestrata-core (IComponent, ComponentType, interfaces)
- orchestrata-kernel (Volk registry, EventBus)
- orchestrata-di (dependency injection container, scoped containers)
- orchestrata-governance (PAM++ plugin policy predicates)
- orchestrata-cxp (CXP routing for plugin Volks)
- orchestrata-provider (IProviderManager for plugin providers, SPEC-011)
- orchestrata-knowledge (Neo4j for audit trail and KG extensions)
- Redis (plugin registry, resource tracking)
- Neo4j (audit trail, KG schema extensions)
- configs/monolithic.yaml (plugin manager and per-plugin config)
Testing
- [TC-001] The plugin manifest validator SHALL be implemented as a pure function accepting a dictionary and returning validation results, enabling unit testing without filesystem access.
- [TC-002] Plugin lifecycle tests SHALL use real plugin packages (minimal test plugins) installed in a temporary directory, not mocked plugin objects.
- [TC-003] Capability enforcement tests SHALL create real scoped DI containers and verify that unauthorized service resolution raises
CapabilityDeniedError. - [TC-004] Integration tests SHALL load real test plugins that register Volks, tools, and providers, and verify they are routable and invocable through the kernel.
- [TC-005] Prolog rule loading tests SHALL use a real Prolog engine and verify that plugin rules are consultable and do not conflict with core governance rules.
- [TC-006] Resource limit tests SHALL load a real plugin that deliberately exceeds limits and verify throttling behavior and auto-disable after error threshold.
- [TC-007] Hot-reload tests SHALL modify a plugin's source code on disk and trigger reload, verifying the new version is active and the old version is fully cleaned up.
- [TC-008] Dependency ordering tests SHALL create plugins with declared dependencies and verify they load in topological order and unload in reverse order.
- [TC-009] Isolation tests SHALL load a plugin that deliberately crashes and verify that the core system and other plugins remain operational.
- [TC-010] Audit trail tests SHALL verify that all plugin lifecycle events are recorded as
(:PluginAudit)nodes in a real Neo4j instance.
Success Criteria
- SDK Complete: IPlugin, PluginContext, and all registration methods implemented
- Discovery: Plugins discovered from local directories and pip packages
- Lifecycle: Full lifecycle (discover → validate → load → initialize → start → stop → unload) working
- Volk Plugins: Plugin-registered Volks routable via CXP and visible in Volk registry
- Tool Plugins: Plugin-registered tools invocable by Volks
- Provider Plugins: Plugin-registered providers participating in ProviderManager
- Isolation: Plugin errors do not crash core system or other plugins
- Governance: PAM++ controls plugin loading, capabilities, and resource limits
- CLI:
orchestrata plugins list/install/uninstall/enable/disablecommands working - Audit: All plugin operations recorded in Neo4j audit trail
Observability
Logging Requirements
- Component Logging
- Log all major operations with appropriate log levels
- Include correlation IDs for request tracing
- Log performance metrics for critical operations
- Log errors with full context for debugging
Metrics Requirements
- Performance Metrics
- Operation duration histograms
- Request/error rate counters
- Resource utilization gauges
- Business metrics relevant to domain
Tracing Requirements
- Distributed Tracing
- Trace propagation across component boundaries
- Span attributes for domain-specific context
- Trace sampling based on operational needs
Alerting Requirements
- Critical Alerts
- Error rate spikes
- Performance degradation
- Resource exhaustion
- Business metric anomalies
Dashboard Requirements
- Operational Dashboard
- Real-time system health
- Performance trends
- Error analysis
- Resource monitoring
Revision History
| Date | Version | Author | Changes |
|---|---|---|---|
| 2026-03-27 | 1.0.0 | Mohamed | Initial specification |
| 2026-03-28 | 1.0.0 | Mohamed | Added Data Models, Error Conditions, Performance Requirements, Testability, DoD, Terminology, Examples, Contracts sections per spec standard improvements |