Skip to content

refactor: revert plugin-trait machinery, keep reversible effects + Arc-tool optimization + OSS base - #1

Merged
dynamder merged 50 commits into
mainfrom
feature/plugin-trait
Aug 19, 2026
Merged

refactor: revert plugin-trait machinery, keep reversible effects + Arc-tool optimization + OSS base#1
dynamder merged 50 commits into
mainfrom
feature/plugin-trait

Conversation

@dynamder

@dynamder dynamder commented Aug 14, 2026

Copy link
Copy Markdown
Owner

The unified Plugin abstraction overreached and broke the clean trait-based architecture of main. This PR reverts the core to plain extensible traits and keeps only what is worth keeping.

Kept

  • Reversible effects: FuneraEnv::effect / dispose — register a side effect with its inverse; dispose() runs all inverses in reverse (LIFO) order, idempotently and panic-isolated. EnvActor calls dispose() automatically when the runtime is dropped, so registrations never leak memory or services.
  • Arc-based tool registry with lock-free execution (get_tool_arc, cloned guarded registry) and remove_tool_if_same as the leak-safe inverse of add_tool.
  • Open-source base setup (CI, community files, mdBook docs, MSRV 1.88, publish metadata).

Removed

  • plugin.rs + plugin/{adapters,broker,instance,registry}, loader.rs + loader/config.rs, env/key.rs typed-service layer, MiddlewareProcessor type erasure, AgentLoop as a plugin subtrait, ServiceBroker, plugin reconciliation, and the six plugin examples.

Also fixes pre-existing defects in main

  • funera-orchestrate/tests/integration.rs did not compile on main (send-handle semantics, AgentEvent::Text, missing provider generic).
  • clippy -D warnings violations.
  • CI: coverage ran the live-LLM real-llm tests without an API key; mutants was a silent no-op (missing origin/main and missing --workspace, so it never mutated anything) — it now genuinely runs 36 mutants against the diff (19 caught, 17 unviable, 0 missed).

Feature-combination validation (new)

  • New feature-matrix CI job runs cargo hack --feature-powerset (with -D warnings) for funera-core, funera-orchestrate, the root facade, and funera-builtin-tools, covering every distinct resolved feature combination (features are not independent: security⇒tool, sandbox⇒security, regex⇒security).
  • This surfaced and fixed cfg-gated issues: unused imports/variables under no-tool / no-security / sandbox-without-security combos, and a real feature-dependency bug where --no-default-features --features funera-builtin-tools did not compile (the feature enabled funera-core/tool but not the local tool feature).

Tests added to cover the changed code

  • get_tool_arc / remove_tool_if_same (raw and guarded registries), ToolExecutor::run command round-trip, non-default SandboxPolicy round-trip, register_all_tools_with_sandbox, AgentRuntimeBuilder::with_builtin_tools, plus the existing reversible-effects LIFO/panic/isolation tests.

New example: cargo run -p funera-orchestrate --example reversible_effects.

Add funera_core::plugin::Plugin (name/inject/provides/apply) with
InstanceState + PluginInstance, and make Tool, ChatProvider,
InspectorMiddleware, and MutatorMiddleware subtraits of it.

Migration is backward-compatible: each capability type only adds an
impl Plugin { fn name() } block (or the impl_plugin! macro);
ame()
moves up to Plugin. Re-export Plugin from funera-orchestrate.

Not compile-verified: sandbox blocks crates.io HTTPS (SEC_E_NO_CREDENTIALS).
Add a shared service table (coeffect context, TypeId-keyed) and an
effect accumulator (disposers, LIFO) to FuneraEnv, with:
- effect(): register a reversible effect (body runs now, returns its inverse)
- provide()/get()/contains(): typed service provision and resolution
- dispose(): run all disposers in reverse registration order

5 unit tests cover provide/get roundtrip, dispose-driven removal,
LIFO effect ordering, and dispose idempotence.
Make each FuneraEnv own its effect accumulator (disposers) while sharing
the service table, add derive() for child envs, and add a ServiceObserver
notification primitive (on_service_change).

Add PluginRegistry: mounts plugins as PluginInstances on derived envs and
drives their lifecycle reactively via a fixpoint refresh — activating
Pending instances whose inject requirements are met, deactivating Active
instances whose requirements are lost, rolling back partial effects on
apply failure, and reverting effects on unmount.

6 tests cover pending-until-satisfied, provider-satisfies-dependent,
removal-deactivates-dependent, unmount-reverts-effects, failed-apply
rollback, and the service-change observer.
Add funera_core::loader::{PluginEntry, Loader}. PluginEntry describes one
desired plugin (id, plugin, disabled, revision); Loader::reconcile diffs
the desired set against the current one and applies minimal mount/unmount
operations — unmounting vanished/disabled/changed-revision entries and
mounting new ones. A changed revision is a hot replacement: the old
instance's effects are reverted and a fresh instance is mounted.

6 tests cover mount, idempotence, removal, disabled entries, revision
reload, and effect reversal on reload.
Cover FuneraEnv/FuneraEnvWatcher methods that had no funera-core-level
tests: set_model/model/watch_model, set_client/has_client_changed,
model_changed/client_changed (async), add/remove/set_tool_availability
tools, add/activate/deactivate/remove skills, skill prompt plumbing,
and with_sandbox_policy (add a sandbox_policy() getter to observe the
previously set-only field).

+17 tests; all pass under --all-features.
- assert the actual tool/skill registry field (not just the watcher
  snapshot) in with_tool_registry/with_skill_registry, catching the
  'delete field' mutations
- rewrite the async *_changed tests to verify they BLOCK when no change
  is pending (50ms timeout), catching 'return Ok(())' mutations

env.rs mutation score: 64 mutants -> 51 caught, 13 unviable, 0 missed.
- README: add 'Plugin architecture' section (Plugin trait, capability
  layer, PluginRegistry, Loader/HMR); fix the Custom tool example for the
  Plugin supertrait; update project structure
- funera_core lib docs: add loader/plugin module rows
- re-export PluginRegistry/Loader/PluginEntry from funera-orchestrate
- add Loader::entry_state (by entry id) with a unit test
- new example funera-orchestrate/examples/plugin_architecture.rs:
  reactive activation, deactivation on provider removal, and hot reload,
  all assertion-checked without an LLM
Gate nono (Landlock, MSRV 1.95) behind the sandbox feature; declare rust-version 1.88 (let-chains require 1.88); add a CI job checking the default build on 1.88.
CONTRIBUTING, CoC 3.0, SECURITY, CHANGELOG, issue/PR templates, CODEOWNERS, .editorconfig, README badges + Contributing.
…c-tool optimization

The unified Plugin abstraction overreached and broke the clean trait-based
architecture of main. Revert the core to plain extensible traits and keep
only the two things worth keeping from the experiment:

- Reversible effects (FuneraEnv::effect / dispose): register a side effect
  with its inverse; dispose() runs all inverses in reverse (LIFO) order,
  idempotently and panic-isolated. EnvActor now calls dispose() automatically
  when the runtime is dropped, so registrations never leak memory or services.
- Arc-based tool registry with lock-free execution (get_tool_arc, cloned
  guarded registry) and remove_tool_if_same as the leak-safe inverse of
  add_tool for a disposer.

Removed: plugin.rs + plugin/{adapters,broker,instance,registry}, loader.rs +
loader/config, env/key.rs typed-service layer, MiddlewareProcessor type
erasure, AgentLoop-as-plugin (loop.rs), ServiceBroker, plugin reconciliation
in runtime/env_actor, and the six plugin examples.

Also fixes pre-existing main defects: integration.rs did not compile (send
handle semantics, AgentEvent::Text, missing provider generic), clippy lints
under -D warnings, and an unused import.
Replace the plugin narrative with the real architecture (actor-based runtime, trait-based extensibility) plus a new 'Reversible effects' section; swap concepts/plugin.md for concepts/effects.md; rewrite env.md and architecture.md; drop agent-loop/service-broker pages; update SUMMARY/getting-started/examples index; document the reversible-effects keep and plugin removal in CHANGELOG; drop stale plugin references from CONTRIBUTING/ROADMAP.
coverage: --all-features enables real-llm, which runs the live-LLM integration tests without an API key and panics; use the same feature set as the test jobs. mutants: the PR checkout does not carry origin/main, so --in-diff origin/main...HEAD failed to produce a diff; fetch main and pass an explicit diff file.
@dynamder dynamder changed the title feat: unified Plugin abstraction with reactive registry + declarative loader refactor: revert plugin-trait machinery, keep reversible effects + Arc-tool optimization + OSS base Aug 19, 2026
cargo-mutants without --workspace only considers the current package (the root funera re-export crate), which contains no mutation opportunities, so the job silently reported 'No mutants to filter' and passed without testing anything. Add --workspace so the diff is mutated across all workspace members.
Add tests for code paths that cargo-mutants flagged as uncovered when mutation testing runs with --all-features: RawToolRegistry/GuardedToolRegistry get_tool_arc and remove_tool_if_same, ToolExecutor::run (direct command/response round-trip with a bounded wait), a non-default SandboxPolicy round-trip through the env getter, register_all_tools_with_sandbox, and AgentRuntimeBuilder::with_builtin_tools.
Without --all-features the mutants test build only compiles default-feature code, so mutants in security/sandbox/builtin-tools-gated code are trivially reported missed.
cargo-hack --feature-powerset surfaced cfg-gated unused imports/variables and one real feature-dependency bug that broke '--no-default-features --features funera-builtin-tools': the feature only enabled funera-core/tool, not the local tool feature, so AgentRuntimeBuilder::with_builtin_tools referenced a field that did not exist. Fix feature deps and cfg-gate imports/bindings (tool/skill/security/sandbox combinations).
Checks every distinct resolved feature combination compiles with -D warnings for funera-core, funera-orchestrate, the root funera facade, and funera-builtin-tools.
@dynamder
dynamder merged commit 6404598 into main Aug 19, 2026
26 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant