Skip to content

Plugin Hierarchy

The FCC plugin system declares eleven abstract base classes in src/fcc/plugins/base.py, each pinned to a distinct PluginType value and each returning a shared PluginMeta descriptor. The design keeps discovery uniform — every plugin answers plugin_meta() — while allowing type-specific contracts to diverge sharply: a VocabularyProviderPlugin exposes get_namespace() and get_class_map() for cross-project entity resolution, while an AIProviderPlugin exposes get_client_class() and get_env_var_hint() for conservative auto-detection. The diagram below captures all eleven ABCs and their type-specific methods, showing why a flat sibling hierarchy (rather than a deep inheritance tree) is the right choice for a plugin surface that must stay pluggable across 12 shipped vocabulary adapters and 6 provider backends.

The diagram below details the eleven plugin ABCs and their shared metadata contract.

classDiagram
    class PluginType {
        <<enumeration>>
        personas
        engines
        templates
        scorers
        validators
        providers
        governance
        scenarios
        workflows
        subscribers
        vocabulary_providers
    }

    class PluginMeta {
        <<frozen dataclass>>
        +id : str
        +name : str
        +version : str
        +plugin_type : PluginType
        +description : str
        +author : str
        +source_package : str
        +tags : list[str]
    }

    class PersonaPlugin {
        <<abstract>>
        +plugin_meta() PluginMeta
        +get_persona_paths() list
        +get_dimension_paths() list
        +get_cross_reference_paths() list
    }

    class EnginePlugin {
        <<abstract>>
        +plugin_meta() PluginMeta
        +get_engine_class() type
    }

    class TemplatePlugin {
        <<abstract>>
        +plugin_meta() PluginMeta
        +get_template_dirs() list
    }

    class ScorerPlugin {
        <<abstract>>
        +plugin_meta() PluginMeta
        +get_scorers() list
    }

    class ValidatorPlugin {
        <<abstract>>
        +plugin_meta() PluginMeta
        +get_validation_rules() list
    }

    class AIProviderPlugin {
        <<abstract>>
        +plugin_meta() PluginMeta
        +get_provider_id() str
        +get_client_class() type
        +get_default_model() str
        +get_env_var_hint() str
    }

    class GovernancePlugin {
        <<abstract>>
        +plugin_meta() PluginMeta
        +get_tag_paths() list
        +get_quality_gate_paths() list
    }

    class ScenarioPlugin {
        <<abstract>>
        +plugin_meta() PluginMeta
        +get_scenario_paths() list
    }

    class WorkflowPlugin {
        <<abstract>>
        +plugin_meta() PluginMeta
        +get_workflow_paths() list
    }

    class EventSubscriberPlugin {
        <<abstract>>
        +plugin_meta() PluginMeta
        +get_subscribers() list
    }

    class VocabularyProviderPlugin {
        <<abstract>>
        +plugin_meta() PluginMeta
        +get_namespace() str
        +get_class_map() dict
    }

    PluginMeta --> PluginType : plugin_type
    PersonaPlugin ..> PluginMeta : returns
    EnginePlugin ..> PluginMeta : returns
    TemplatePlugin ..> PluginMeta : returns
    ScorerPlugin ..> PluginMeta : returns
    ValidatorPlugin ..> PluginMeta : returns
    AIProviderPlugin ..> PluginMeta : returns
    GovernancePlugin ..> PluginMeta : returns
    ScenarioPlugin ..> PluginMeta : returns
    WorkflowPlugin ..> PluginMeta : returns
    EventSubscriberPlugin ..> PluginMeta : returns
    VocabularyProviderPlugin ..> PluginMeta : returns

Reach for a new plugin ABC only when a genuinely new consumer surface is required; otherwise, extend an existing type with additional concrete implementations. The tradeoff of the flat design is duplicated plugin_meta() boilerplate in each ABC, but this is deliberate: it lets downstream packages ship a plugin for one concern (say, a new vocabulary) without inheriting unrelated machinery. The AIProviderPlugin.get_env_var_hint() method is the canonical example of adding a capability without breaking the contract — it arrived in v1.1.0 to support Ollama and LiteLLM auto-detection.

Vocabulary providers, introduced in v1.2.1, are the most recent slot; they bind a namespace string (for instance athenium or mnemosyne) to a class map that federation uses to resolve cross-project entities against the 175 packaged YAML mappings under src/fcc/data/objectmodel/.

Key contracts

  • PluginType — enumeration pinning each plugin to exactly one of eleven kinds.
  • PluginMeta — frozen metadata record returned by every plugin for discovery, versioning, and provenance tagging.
  • PersonaPlugin — contributes persona YAML, dimension definitions, and cross-reference matrices.
  • EnginePlugin — supplies an engine class (for simulation or action execution variants).
  • TemplatePlugin — registers Jinja2 template directories for docs-as-code generation.
  • ScorerPlugin — contributes quality scorers for deliverable evaluation.
  • ValidatorPlugin — contributes JSON Schema or callable validation rules.
  • AIProviderPlugin — declares a provider id, client class, default model, and environment-variable hint for conservative auto-detection.
  • GovernancePlugin — contributes tag registry entries and quality-gate definitions.
  • ScenarioPlugin — contributes scenario JSON files for the simulation engine.
  • WorkflowPlugin — contributes workflow graph JSON files.
  • EventSubscriberPlugin — contributes event-bus subscribers for reactive integrations.
  • VocabularyProviderPlugin — maps a namespace to a cross-project class map for federation-backed entity resolution.

See also

  • Source: src/fcc/plugins/base.py
  • Related: ../sequence-diagrams/plugin-discovery-and-load.md