Files
ss-tools/docs/adr/ADR-0004-plugin-architecture.md
root 632b730fff chore: migrate GRACE-Poly anchors to hierarchical dotted naming
Systematic rename of all semantic anchors (#region, [DEF], @RELATION)
across 1400+ files — backend Python, frontend Svelte/TS, specs, docs:
- Flat anchors become Namespace.Module.Entity
- @RELATION references updated to match new anchor paths
- Zero business logic changes
2026-07-22 11:48:15 +03:00

4.6 KiB
Raw Blame History

[DEF:Doc.Adr.ADR0004:ADR]

@STATUS SUPERSEDED

@PURPOSE Historical proposal for subprocess-isolated, TOML-manifest plugins. Superseded by ADR-0016, which records the implemented in-process PluginBase runtime.

@SUPERSEDED_BY [ADR-0016:ADR]

@RELATION DEPENDS_ON -> [Doc.Adr.ADR0001:ADR]

@RELATION DEPENDS_ON -> [Doc.Adr.ADR0003:ADR]

@RELATION CALLS -> [Doc.Adr.ADR0005:ADR]

@RATIONALE Extensibility is a core architectural value: the system must support LLM‑driven analysis, custom data transformations, and environment‑specific logic without modifying core code. A plugin system prevents the monolith from accumulating every domain‑specific feature and enables third‑party (or future‑self) contributions without forking.

@RATIONALE Process isolation was chosen over in‑process imports because: (a) plugins may use incompatible library versions, (b) a crashing plugin must not take down the orchestrator, (c) security boundary — plugins should not access the orchestrator's database connection directly.

@REJECTED In‑process Python importlib plugin loading — rejected because a misbehaving plugin can corrupt global state, exhaust memory, or crash the server. Process isolation provides a hard boundary.

@REJECTED Docker‑container per plugin — rejected because it adds excessive orchestration complexity and startup latency for plugins that are mostly lightweight LLM prompt chains. Subprocess isolation is sufficient.

@REJECTED WebAssembly (WASI) sandbox — rejected because the Python AI/LLM ecosystem (langchain, transformers) does not yet reliably compile to WASM. Premature optimization.

Decision

Plugin Architecture

superset-tools Core
├── core/plugin_loader.py       # Plugin discovery, loading, lifecycle
├── core/plugin_executor.py     # Subprocess execution, timeout, error boundary
├── core/plugin_registry.py     # Registered plugins, metadata, health
└── plugins/                    # Plugin packages (each = one directory)
    ├── llm_analysis/           # LLM‑driven Superset data analysis; v2 adds dual-path execution (Path A: Playwright screenshot + multimodal LLM; Path B: text-only API + dataset health checking)
    ├── dataset_orchestration/  # LLM dataset operations
    └── git_integration/        # Git‑based version control for dashboards

Plugin Contract

v2 update: Task-based validation was introduced via ValidationTaskService, creating persistent validation policies with ValidationRun aggregates for grouping per-dashboard results. Each run is tracked as a ValidationRun record with aggregate pass/fail/warn counts, replacing the previous single-shot task result pattern. See ValidationTaskService in services/validation_service.py.

Every plugin MUST provide:

  1. plugin.toml — metadata manifest at the plugin root

    [plugin]
    id = "llm_analysis"
    name = "LLM Data Analysis"
    version = "1.0.0"
    entrypoint = "plugin.py"
    timeout_sec = 300
    max_memory_mb = 512
    requires = ["superset-api>=1.0", "openai>=1.0"]
    
  2. plugin.py — entrypoint with two required functions:

    • def register(registry: PluginRegistry) -> PluginInfo — declare capabilities
    • def execute(task: TaskContext) -> TaskResult — run the plugin
  3. Task context contract (Pydantic TaskContext):

    • task_id: str, plugin_id: str, action: str, params: dict, superset_env: SupersetConnection, auth_token: str (scoped, short‑lived)
  4. Result envelope (Pydantic TaskResult):

    • status: Literal["success", "warning", "error"], data: dict | None, error_message: str | None, execution_time_ms: int, artifacts: list[str] (file paths to saved artifacts)

Plugin Lifecycle

Discover → Validate → Register → [Execute] → Report
   │          │          │          │
   │    Check TOML    Store in    Spawn subprocess
   │    schema,       registry     with timeout +
   │    dependencies  in DB        memory limit

Isolation Guarantees

  • Subprocess: subprocess.run(..., timeout=timeout_sec), killed on timeout.
  • Memory: resource.setrlimit(RLIMIT_AS, max_memory_mb * 1024 * 1024) before exec.
  • No DB access: Plugins receive only a scoped REST API token, never a database connection.
  • No filesystem writes outside allowed dirs: Configurable artifact directory per plugin.

RBAC Integration

Plugin access is governed by ADR-0005 (RBAC):

  • Each plugin declares required_roles: ["admin", "analyst"] in plugin.toml.
  • Plugin executor checks the user's role set before allowing execution.
  • Forbidden access returns 403 with audit log entry.

[/DEF:Doc.Adr.ADR0004:ADR]