Files
ss-tools/docs/adr/ADR-0004-plugin-architecture.md
busya ec6421de35 rename ss-tools to superset-tools across the entire project
- Replace all occurrences of 'ss-tools' with 'superset-tools' in 104 files
- Rename git bundle file ss-tools.bundle → superset-tools.bundle
- Update .gitignore pattern accordingly
- Preserve variable names (hasSsTools etc.) and code identifiers
2026-06-16 11:15:19 +03:00

4.6 KiB
Raw Blame History

[DEF:ADR-0004:ADR]

@STATUS ACTIVE

@PURPOSE Define the plugin architecture for superset-tools — the loading mechanism, lifecycle contract, isolation guarantees, and the boundary between core services and pluggable extensions.

@RELATION DEPENDS_ON -> [ADR-0001:ADR]

@RELATION DEPENDS_ON -> [ADR-0003:ADR]

@RELATION CALLS -> [ADR-0005:ADR]

@RATIONALE Extensibility is a core architectural value: the system must support LLMdriven analysis, custom data transformations, and environmentspecific logic without modifying core code. A plugin system prevents the monolith from accumulating every domainspecific feature and enables thirdparty (or futureself) contributions without forking.

@RATIONALE Process isolation was chosen over inprocess 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 Inprocess 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 Dockercontainer 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/           # LLMdriven 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/        # Gitbased 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, shortlived)
  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:ADR-0004:ADR]