# Conflicting plugins detected

- **Pattern:** `ab-002092` (`plugin-extension-architecture.versioning-contracts.conflict-detection`)
- **Severity:** low
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-002092
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-002092)

## Why it matters

When two plugins register the same route, command ID, or decorator key and the host silently accepts both, behavior depends on which plugin loaded last — an implementation detail that changes across restarts, deploys, and config edits. ISO 25010 maintainability.modularity requires that namespace boundaries be enforced; silent last-wins collision is a namespace boundary failure. Operators cannot predict or debug behavior that changes based on plugin load order with no logged conflict.

## Severity rationale

Low because undetected duplicate plugin IDs or namespace collisions produce load-order-dependent behavior that is indistinguishable from intentional configuration until a conflict silently activates the wrong handler.

## Remediation

Reject duplicate plugin names at load time with an explicit `PluginConflictError`. Extend conflict detection to namespace collisions (duplicate routes, commands, or decorator keys) as the plugin ecosystem grows.

```typescript
function loadPlugin(manifest: PluginManifest) {
  if (this.plugins.has(manifest.name)) {
    throw new PluginConflictError(
      `Plugin "${manifest.name}" is already loaded. Cannot load a second plugin with the same name.`
    );
  }
  for (const conflict of manifest.conflicts ?? []) {
    if (this.plugins.has(conflict)) {
      throw new PluginConflictError(
        `Plugin "${manifest.name}" conflicts with loaded plugin "${conflict}".`
      );
    }
  }
}
```

## Detection

- **ID:** `conflict-detection`
- **Severity:** `low`
- **What to look for:** Check whether the host detects and handles conflicting plugins — plugins that register handlers for the same hooks with incompatible behavior, plugins that extend the same functionality, or plugins that declare mutual exclusivity. Look for:
  1. Duplicate plugin detection (two plugins with the same name or ID)
  2. Conflict declarations in manifests (e.g., `"conflicts": ["other-plugin"]`)
  3. Warnings when multiple plugins register mutating handlers for the same hook
  4. Namespace collision detection (two plugins adding the same route, decorator, or key)
  Real-world examples:
  - VS Code: extensions can't register the same command ID
  - WordPress: plugin conflicts are detected when two plugins define the same shortcode
  - Terraform: providers with overlapping resource types produce explicit errors
- **Pass criteria:** Count all conflict detection mechanisms. At minimum, duplicate plugin detection (two plugins with the same name/ID are rejected). Ideally, conflict declarations in manifests and namespace collision detection. The host warns or errors when conflicts are detected, not silently last-wins.
- **Fail criteria:** No conflict detection. Duplicate plugin IDs are silently accepted (last one wins). No namespace collision detection — two plugins can register the same route/command/key and the behavior is undefined.
- **Skip (N/A) when:** The plugin system is small enough (fewer than 5 plugins) that conflicts are unlikely, AND all plugins are first-party. Also skip for middleware chains where "multiple handlers" is the expected pattern, not a conflict.
- **Detail on fail:** `"No duplicate detection — loading two plugins with name 'auth-plugin' silently overwrites the first with the second. No conflict declarations exist in the manifest schema. Two plugins can both register a '/health' route and the behavior depends on load order with no warning."`
- **Remediation:** Conflict detection prevents subtle bugs where two plugins silently interfere with each other. At minimum, detect and reject duplicate plugin IDs.

  ```typescript
  function loadPlugin(manifest: PluginManifest) {
    // Check for duplicate:
    if (this.plugins.has(manifest.name)) {
      throw new PluginConflictError(
        `Plugin "${manifest.name}" is already loaded. Cannot load a second plugin with the same name.`
      );
    }
    // Check for declared conflicts:
    for (const conflict of manifest.conflicts ?? []) {
      if (this.plugins.has(conflict)) {
        throw new PluginConflictError(
          `Plugin "${manifest.name}" conflicts with loaded plugin "${conflict}".`
        );
      }
    }
  }
  ```

## External references

- iso-25010 maintainability.modularity

Taxons: code-quality

HTML version: https://auditbuffet.com/patterns/ab-002092
