From 8d7b45234c031528f75164d9d8d9d9c55abf3220 Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Sat, 8 Aug 2026 16:55:59 +0200 Subject: [PATCH] Added a deterministic plugin startup order that begins with enterprise configurations --- .../PluginSystem/PluginFactory.Starting.cs | 32 +++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/app/MindWork AI Studio/Tools/PluginSystem/PluginFactory.Starting.cs b/app/MindWork AI Studio/Tools/PluginSystem/PluginFactory.Starting.cs index 513209d9..60c9c336 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/PluginFactory.Starting.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/PluginFactory.Starting.cs @@ -52,9 +52,18 @@ public static partial class PluginFactory } // - // Iterate over all available plugins and try to start them. + // Iterate over all available plugins and try to start them. We do that in a deterministic + // order, starting with the configuration plugins of the organization. Two reasons: // - foreach (var availablePlugin in AVAILABLE_PLUGINS) + // - Configuration plugins write settings and configuration objects. Whoever writes one + // first owns it, so the organization has to come first: its configuration is the baseline + // every other plugin has to respect. + // + // - Without an explicit order, the sequence is the one Directory.EnumerateFiles produced in + // LoadAll. That order is not guaranteed, so the same installation could behave + // differently on two machines. + // + foreach (var availablePlugin in AVAILABLE_PLUGINS.OrderBy(GetStartupRank).ThenBy(plugin => plugin.LocalPath, StringComparer.OrdinalIgnoreCase)) { if(cancellationToken.IsCancellationRequested) { @@ -89,6 +98,25 @@ public static partial class PluginFactory return configObjects; } + /// + /// Determines the position of a plugin in the startup sequence. Plugins with a lower rank start earlier. + /// + /// + /// The configuration plugins an organization deployed go first: they are the baseline for + /// everything else. Local configuration plugins follow, so they can add to that baseline instead + /// of replacing parts of it. All remaining plugin types write no settings at all, so their rank + /// is irrelevant for the outcome. + /// + /// The plugin about to be started. + /// The startup rank of the plugin. + private static int GetStartupRank(IAvailablePlugin plugin) => plugin.Type switch + { + PluginType.CONFIGURATION when IsEnterpriseConfigurationPath(plugin.LocalPath) => 0, + PluginType.CONFIGURATION => 1, + + _ => 2, + }; + private static void LogAssistantPluginStartupState() { ManagedConfiguration.TryGet(x => x.AssistantPluginAudit, x => x.EnterpriseApprovedPlugins, out ConfigMeta> configMeta);