Plugin Platform¶
One Runtime, Multiple Capabilities¶
Truthound 3.0 removes the old split between a standard plugin manager and a separate enterprise lifecycle runtime.
The current design is:
PluginManager: canonical lifecycle runtimeEnterprisePluginManager: async facade over the same runtime- optional capability services: versioning, trust store, security policy, hot reload, signature verification
This keeps lifecycle semantics in one place while still allowing enterprise-oriented behavior to be attached when needed.
Stable Extension Ports¶
Plugins should integrate through stable manager ports:
register_check_factory()register_data_asset_provider()register_reporter()register_hook()register_capability()
These ports provide an abstraction boundary between plugin authors and Truthound internals.
For reporter plugins, the canonical 3.0 contract is reporter contract version 3:
- canonical input:
ValidationRunResult - shared projection:
RunPresentation - render entry point:
render(run_result, *, context)
PluginManager records reporter contract versions so mixed plugin fleets can be audited during migration.
Capability Model¶
Capabilities are deliberately modeled as optional services rather than hard-coded branches in the plugin lifecycle:
- version compatibility
- trust-store metadata
- security policy metadata
- hot reload intent
- signature verification requirements
This design lets the manager remain small while still supporting richer deployment policies.
CLI Surface¶
truthound plugins list
truthound plugins info my-plugin
truthound plugins load my-plugin
truthound plugins unload my-plugin
The public command group is plugins, not plugin.
Authoring Guidance¶
Truthound 3.0 standardizes plugin authoring around declarative and stable contracts:
- validation extensions should register
CheckSpecFactoryimplementations - reporter extensions should target
ValidationRunResultandRunPresentation - datasource extensions should register
DataAssetProvider - lifecycle hooks should only touch kernel contracts, not private registries
Legacy validator-class mutation patterns are no longer the recommended public path.
Design Rationale¶
This platform mirrors the broader 3.0 architecture:
- stable contracts in the center
- adapters at the edge
- optional services instead of manager duplication
The formal decisions are captured in ADR 002 and the compatibility implications are captured in ADR 004.
Approved resident MCP administration¶
Development Core profiles can use a pinned owner with explicit plugin class, custom
hook, provider, reporter, check-factory and capability aliases. The owner calls the
same PluginManager lifecycle and extension ports; it does not accept Python or
rewrite other workers' binding files. All reads and effects require separate operator
approval. See the MCP contract.
Manager-local native execution (development source)¶
PluginManager.validate(check_factories, data=None, asset_provider=None, name="plugin-suite", result_format="summary", parallel=False, max_workers=None, pushdown=None) compiles registered CheckSpecFactory objects (or legacy native validator registrations) into a canonical kernel suite and executes it against data or one registered native DataAssetProvider. It returns a PluginValidationResult with .run, detailed .hooks and .hooks_failed. This creates no implicit context/workspace persistence. validate_async awaits native lifecycle hooks, including decorated async handlers, while retaining the calling thread for native provider and kernel work. The synchronous method rejects a running event loop before effects.
render(reporter_name, run) and render_async consume a canonical ValidationRunResult through the selected registered v3 reporter class/constructor/configured object and return PluginRenderResult.content (text or bytes) with detailed hook outcomes. Structural v3 reporters are supported; inheritance from ValidationReporter is optional. Configured reporter instances remain local to the manager, while constructors may still register with the global reporter factory.
HookManager.trigger_detailed and trigger_detailed_async return HookExecutionResult records with hook type, source, success, result and optional error type. A successful None is distinct from a failure; exception bodies are not logged or put in this record. Legacy trigger methods keep result-list compatibility. Detailed sync calls reject awaitable handlers rather than leaking coroutines; async calls await actual returned awaitables. An optional trusted set_execution_guard checks before each hook. Manager workflow guards also check before provider/factory/report operations. Denied unload hooks cannot veto owned resource teardown/removal.
Validation hooks receive immutable CheckSpec intent as checks and validators; issue hooks receive their corresponding check intent. Hook failures are visible and do not veto a validation result. Providers own asset connections and final shutdown. Explicit Core MCP plugin contracts run these native workflows in one approved owner, with canonical result export, bounded artifacts and input authorization lifetimes. This adds no propagation into other worker registries, arbitrary imports or remote service verification.
unregister_check_factory, unregister_data_asset_provider, unregister_reporter and unregister_capability remove this manager's stable port lookups, without guessing resource ownership or closing shared providers. Reporter constructors are removed from the global factory only if their identity still matches this manager's registration; truthound.reporters.unregister_reporter(name, expected_constructor=...) offers the same atomic ownership check. A configured reporter instance stays manager-local. Invalid reporter contract metadata is rejected before replacing an existing binding.
unload_plugin(name,detailed=True) returns a PluginCleanupResult containing deactivation, teardown completion, registry removal, unload-hook failure and exception types. .success means those native callbacks completed, not that an external provider acknowledged cleanup. The default retains None compatibility; guard denial still propagates after cleanup. Unregister/teardown exception bodies are not logged, and teardown is attempted even after an unload hook is denied. Core MCP preserves failed cleanup receipts rather than treating registry removal as full cleanup success.