Files
ss-tools/docs/adr/ADR-0016-in-process-plugin-runtime.md
busya 24d3b7d1f9 refactor(task-manager): implement task resilience and execution lifecycle improvements
Enhance the reliability and observability of the task execution engine
by introducing retry mechanisms, idempotency, and structured progress
tracking.

- Implement centralized retry logic with exponential backoff support
  in `JobLifecycle`.
- Add `retry_task` API endpoint and `TaskManager` method for manual
  task restarts.
- Introduce task idempotency using `_idempotency_key` to prevent
  duplicate executions.
- Add `retry_count`, `max_retries`, `last_error`, and `progress` fields
  to the `Task` model and ensure persistence via `TaskPersistenceService`.
- Upgrade `SchedulerService` to use differential synchronization with
  the persistent `SQLAlchemyJobStore` for better job durability.
- Implement structured heartbeat logging to support real-time progress
  updates.
- Update project documentation and ADRs to reflect the new plugin
  runtime and task resilience patterns.
- Add comprehensive unit and integration tests for the new task
  lifecycle features.
2026-07-12 15:31:56 +03:00

2.1 KiB

[DEF:ADR-0016:ADR]

@STATUS ACCEPTED

@PURPOSE Define the implemented plugin discovery and execution boundary.

@RELATION SUPERSEDES -> [ADR-0004:ADR]

@RELATION BINDS_TO -> [backend/src/core/plugin_loader.py]

@RELATION BINDS_TO -> [backend/src/core/plugin_base.py]

@RATIONALE The repository already contains first-party plugins that share the application runtime, configuration, database access patterns, and async Task Manager. The executable contract is PluginBase, not an unimplemented manifest or subprocess protocol.

@REJECTED plugin.toml manifests and a mandatory subprocess executor — rejected as the current architecture because neither is the runtime contract implemented by the repository.

@REJECTED Treating dynamic discovery as a sandbox — rejected because imported plugin code runs in the backend process and has the same process privileges.

Decision

First-party plugins live in backend/src/plugins/. PluginLoader discovers Python modules and package __init__.py files in that directory, imports them under the src.plugins package, instantiates subclasses of PluginBase, and exposes their validated PluginConfig metadata.

Every discoverable plugin must implement the abstract PluginBase contract:

  • stable id, name, description, and version properties;
  • get_schema() returning an input schema;
  • asynchronous execute(params);
  • optional ui_route and a permission derived from required_permission.

Plugins are trusted, versioned source in this repository. They are not third-party sandboxed extensions. A future marketplace or untrusted-plugin feature requires a new ADR that specifies its isolation, permission, resource, and failure model.

Consequences

  • Plugin failures during discovery are logged and do not stop discovery of other modules, but execution remains subject to normal backend failure handling.
  • Plugin dependencies must be compatible with the backend runtime.
  • Adding a plugin requires tests for discovery and its public execution contract; it does not require a plugin.toml manifest.

[/DEF:ADR-0016:ADR]