Skip to content

Plugin Boundaries

The FCC framework supports 11 plugin types, each declared as an ABC under src/fcc/plugins/base.py (with two — EventSubscriberPlugin and VocabularyProviderPlugin — living in their respective subsystem modules). This page is the reference for plugin authors: per type, the contract you must implement, the lifecycle hook the framework will call, and a minimal working example.

The capability map below shows where each plugin type plugs into the framework's load path.

graph LR
    subgraph DISC["Discovery"]
        REG["PluginRegistry<br/>(plugins/registry.py)"]
        EP[entry_points group<br/>fcc.plugins.*]
    end
    subgraph TYPES["11 plugin types"]
        PE[PersonaPlugin]
        EN[EnginePlugin]
        TM[TemplatePlugin]
        SC[ScorerPlugin]
        VA[ValidatorPlugin]
        PR[AIProviderPlugin]
        GO[GovernancePlugin]
        SN[ScenarioPlugin]
        WF[WorkflowPlugin]
        SU[EventSubscriberPlugin]
        VP[VocabularyProviderPlugin]
    end
    subgraph CONS["Consumers in src/fcc/"]
        C_REG[personas/registry.py]
        C_SIM[simulation/engine.py]
        C_DG[scaffold/doc_generator.py]
        C_SCO[collaboration/scoring.py]
        C_VAL[compliance/pipeline.py]
        C_AI[simulation/ai_client.py]
        C_GOV[governance/]
        C_SC[scenarios/loader.py]
        C_WF[workflow/loader.py]
        C_BUS[messaging/bus.py]
        C_VOC[objectmodel/vocabulary_loader.py]
    end

    EP --> REG
    REG --> PE
    REG --> EN
    REG --> TM
    REG --> SC
    REG --> VA
    REG --> PR
    REG --> GO
    REG --> SN
    REG --> WF
    REG --> SU
    REG --> VP

    PE --> C_REG
    EN --> C_SIM
    TM --> C_DG
    SC --> C_SCO
    VA --> C_VAL
    PR --> C_AI
    GO --> C_GOV
    SN --> C_SC
    WF --> C_WF
    SU --> C_BUS
    VP --> C_VOC

The pattern across all 11 types is the same: an ABC with a plugin_meta() classmethod that returns a PluginMeta frozen dataclass, plus type-specific hooks. The framework discovers plugins through Python entry points; nothing else is required at install time.

The PluginMeta contract

Every plugin must return a PluginMeta from plugin_meta():

@dataclass(frozen=True)
class PluginMeta:
    id: str            # globally-unique plugin id, kebab-case
    name: str          # human-readable label
    version: str       # SemVer
    plugin_type: PluginType
    description: str
    author: str
    source_package: str  # the importable package name
    tags: tuple[str, ...]

plugin_type must match the ABC the plugin extends. The registry rejects mismatches at discovery time.

The 11 plugin types

1. PersonaPlugin

Consumer: src/fcc/personas/registry.py (PersonaRegistry merges plugin personas into the core registry).

Methods:

  • plugin_meta() -> PluginMeta
  • get_persona_paths() -> list[Path] — YAML files defining personas
  • get_dimension_paths() -> list[Path] — optional dimension-profile YAMLs
  • get_cross_reference_paths() -> list[Path] — optional matrix YAMLs

Used by: vocabulary-provider plugins from sister projects (PAOM, AOME, CTO) that bundle their own persona libraries.

2. EnginePlugin

Consumer: src/fcc/simulation/engine.py (SimulationEngine selects between mock + AI + plugin-supplied engines).

Methods:

  • plugin_meta() -> PluginMeta
  • get_engine_class() -> type — must implement the EngineProtocol

Used by: experimental engine swaps (e.g. trace-replay engine for testing).

3. TemplatePlugin

Consumer: src/fcc/scaffold/doc_generator.py (DocGenerator stacks plugin template dirs over the core template set).

Methods:

  • plugin_meta() -> PluginMeta
  • get_template_dirs() -> list[Path] — Jinja2 template roots

Used by: vertical-pack plugins that need custom docs-as-code templates (e.g. healthcare HIPAA section in compliance reports).

4. ScorerPlugin

Consumer: src/fcc/collaboration/scoring.py (ScoringEngine routes deliverables through registered scorers).

Methods:

  • plugin_meta() -> PluginMeta
  • get_scorers() -> dict[str, Any] — name → scorer callable

Used by: domain-specific quality scorers (e.g. clinical-relevance scorer for medical content).

5. ValidatorPlugin

Consumer: src/fcc/compliance/pipeline.py (CompliancePipeline runs each rule against generated artifacts).

Methods:

  • plugin_meta() -> PluginMeta
  • get_validation_rules() -> dict[str, Any] — rule name → callable

Used by: regulatory packs (HIPAA, SOX, GDPR) that ship rule libraries.

6. AIProviderPlugin

Consumer: src/fcc/simulation/ai_client.py (BaseAIClient picks provider via auto-detection or scenario override).

Methods:

  • plugin_meta() -> PluginMeta
  • get_provider_id() -> str — e.g. "ollama"
  • get_client_class() -> type — implements BaseAIClient
  • get_default_model() -> str
  • get_env_var_hint() -> str | None — name of env var that, if set, signals "use me"

Used by: built-ins (anthropic, openai, azure_openai), v1.1.0 plugins (ollama, litellm), v1.1.1 plugin (vLLM).

Auto-detection: if get_env_var_hint() returns "OLLAMA_BASE_URL" and that var is set, the provider is auto-selected.

7. GovernancePlugin

Consumer: src/fcc/governance/ (TagRegistry + QualityGateRegistry merge plugin contributions).

Methods:

  • plugin_meta() -> PluginMeta
  • get_tag_paths() -> list[Path] — additional tag-registry YAMLs
  • get_quality_gate_paths() -> list[Path] — additional gate YAMLs

Used by: regulatory packs (e.g. an NIST gate set).

8. ScenarioPlugin

Consumer: src/fcc/scenarios/loader.py (ScenarioLoader concatenates plugin scenarios with built-ins).

Methods:

  • plugin_meta() -> PluginMeta
  • get_scenario_paths() -> list[Path] — scenario JSON/YAML files

Used by: vertical packs that bundle domain scenarios.

9. WorkflowPlugin

Consumer: src/fcc/workflow/loader.py (WorkflowGraph registry).

Methods:

  • plugin_meta() -> PluginMeta
  • get_workflow_paths() -> list[Path] — workflow graph JSONs

Used by: rare — most teams reuse the 7 built-in graphs (5-node base, 20-node extended, 24-node complete, 55-node extended_84, 2 EAIFC).

10. EventSubscriberPlugin

Consumer: src/fcc/messaging/plugin_bridge.pysrc/fcc/messaging/bus.py (EventBus).

Methods:

  • plugin_meta() -> PluginMeta
  • get_subscribers() -> list[tuple[Subscriber, EventFilter | None]] — pairs of (callable, optional filter)

Used by: ComplianceSubscriber (auto re-audit on persona change), external observability bridges, custom alerting.

11. VocabularyProviderPlugin

Consumer: src/fcc/objectmodel/vocabulary_loader.py (VocabularyMappingLoader verifies sister-project vocab against FCC's 175 packaged YAML mappings).

Methods:

  • plugin_meta() -> PluginMeta
  • get_namespace() -> str — e.g. "athenium", "libra"
  • get_class_map() -> dict[str, type] — local-id → dataclass type

Used by: 12 plugins shipped as of v1.3.0 — fcc-athenium-plugin, fcc-mnemosyne-plugin, plus 10 constellation packs (Caelum, Columba, Crater, Libra, Norma, Ophiuchus, Pyxis, Scutum, Serpens, Vela). Sister projects remain pure-data dataclass libraries with no FCC dependency; the plugin packages live under FCC's plugins/ directory.

Discovery + lifecycle

The plugin discovery sequence is the same for every type:

  1. install time — plugin package declares an entry_points block in its pyproject.toml under the matching group (e.g. fcc.plugins.vocabulary_providers).
  2. first call to PluginRegistry.discover(plugin_type) — the registry enumerates entry points, instantiates each, calls plugin_meta(), verifies the type, caches the instance.
  3. subsystem use — the consumer subsystem (e.g. PersonaRegistry) calls the type-specific methods (get_persona_paths()) and merges results.
  4. event emission — each successful load emits a plugin.loaded event; each failure emits plugin.failed with the traceback.

There is no eager import — a plugin that fails to import is logged but does not crash the framework.

Minimal example: an EventSubscriberPlugin

from fcc.messaging import EventBus, EventFilter, EventType
from fcc.messaging.plugin_bridge import EventSubscriberPlugin
from fcc.plugins.base import PluginMeta, PluginType

class AuditLogPlugin(EventSubscriberPlugin):
    @classmethod
    def plugin_meta(cls) -> PluginMeta:
        return PluginMeta(
            id="example-audit-log",
            name="Audit Log",
            version="0.1.0",
            plugin_type=PluginType.SUBSCRIBERS,
            description="Append every workflow event to disk.",
            author="example.com",
            source_package="example_audit_log",
            tags=("audit", "observability"),
        )

    def get_subscribers(self):
        def write(evt):
            with open("audit.log", "a") as f:
                f.write(f"{evt.timestamp} {evt.event_type.value} {evt.source}\n")
        return [(write, EventFilter(event_types={EventType.WORKFLOW_STEP}))]

pyproject.toml:

[project.entry-points."fcc.plugins.subscribers"]
audit_log = "example_audit_log:AuditLogPlugin"

After pip install ./example-audit-log, PluginRegistry.discover() picks it up and the EventBus calls write on every WORKFLOW_STEP event.

See also