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);