Portal Community

Why Ordering Matters

Plugin ordering affects three distinct behaviours:

BehaviourEffect of Order
DI registrationWhen two plugins register the same interface, the last registration wins. This is how you override built-in services.
OnStartAsync sequencePlugins start in registration order. A plugin that depends on another plugin's services must be registered after it.
OnStopAsync sequencePlugins stop in reverse registration order. Last registered, first stopped — ensuring dependents clean up before dependencies.

Recommended Registration Order

builder.Services.AddOctopus(config =>
{
    // ── Tier 1: Core storage (everything else depends on these) ──────────
    config.AddPlugin<SqlServerPlugin>();         // Must be first
                                                  // Registers: OctopusDbContext, IEpisodicMemoryStore,
                                                  //   IProceduralMemoryStore, IAgentStore, IAIFunctionStore
                                                  // Runs EF Core migrations in OnStartAsync

    // ── Tier 2: AI / Semantic layer ──────────────────────────────────────
    config.AddPlugin<SemanticKernelPlugin>();     // Requires IAgentStore from SqlServerPlugin
                                                  // Registers: ISemanticMemoryStore, IEmbeddingProvider
                                                  // Ensures vector collections exist in OnStartAsync

    // ── Tier 3: UI and integration features ──────────────────────────────
    config.AddPlugin<ChatbotUIPlugin>();          // Requires SqlServerPlugin (session storage)
    config.AddPlugin<ProcessPlugin>();            // Requires SqlServerPlugin + ChatbotUIPlugin (SSE)
    config.AddPlugin<WebDriverPlugin>();          // No dependency on other built-ins

    // ── Tier 4: Your custom plugins (last) ───────────────────────────────
    config.AddPlugin<MyHRPlugin>();               // Uses IAgentStore, MCPToolRegistry
    config.AddPlugin<MyInventoryPlugin>();        // Depends on MyHRPlugin? Register after it.
});

Dependency Rules

If Your Plugin Uses...Register After...
OctopusDbContext, IAgentStore, IEpisodicMemoryStoreSqlServerPlugin
ISemanticMemoryStore, IEmbeddingProviderSemanticKernelPlugin
ISSEResponseWriter, IChatUIRendererChatbotUIPlugin
IWorkflowTriggerService, IOctopusHILActorProcessPlugin
IBrowserSession, IPlaywrightManagerWebDriverPlugin
Another custom plugin's servicesThat custom plugin

Service Override Pattern

Because the DI container uses the last registration for an interface, you can override any built-in service by registering after the plugin that provides it:

// Override IEpisodicMemoryStore with a custom Redis-backed implementation

config.AddPlugin<SqlServerPlugin>();          // Registers default IEpisodicMemoryStore

// Your plugin's OnRegister runs after SqlServerPlugin's:
public void OnRegister(IServiceCollection services, OctopusConfig config)
{
    // This supersedes the SqlServerPlugin registration
    services.AddScoped<IEpisodicMemoryStore, RedisEpisodicMemoryStore>();
}

// ✓ RedisEpisodicMemoryStore will be resolved for IEpisodicMemoryStore everywhere
Override responsibility. When you override a service, your implementation must satisfy the full contract of the interface. Check what methods the Octopus core calls on the interface and ensure all are implemented correctly.

What Happens with Wrong Order

MistakeSymptomFix
Custom plugin registered before SqlServerPluginInvalidOperationException: IAgentStore is not registeredMove SqlServerPlugin first
SemanticKernelPlugin before SqlServerPluginIAgentStore not found during OnStartAsync vector collection setupAlways register SqlServerPlugin first
Override plugin registered before the plugin it overridesBuilt-in implementation is used instead of your override (last wins)Register your plugin after the one you override
Plugin A depends on Plugin B's service, registered before BOnStartAsync resolves null or throws because B has not run yetRegister B before A

Startup and Shutdown Order Summary

1
OnRegister: SqlServerPlugin → SemanticKernelPlugin → ChatbotUIPlugin → ProcessPlugin → WebDriverPlugin → Custom DI services registered in this order. Last registration wins for duplicate interfaces.
2
OnStartAsync: same order (SqlServerPlugin first) SqlServerPlugin runs migrations, creates Octopus tables. SemanticKernelPlugin creates vector collections. Custom plugins register MCP tools.
3
OnStopAsync: reverse order (Custom first → SqlServerPlugin last) Custom plugins clean up first. Core storage plugins stop last to ensure any in-flight persistence completes before the database context closes.