Configuring Policy Conflict Resolution with ConflictResolutionStrategy in the Agent Governance Toolkit
The ConflictResolutionStrategy enum defines how the Microsoft Agent Governance Toolkit selects a winning decision when multiple policy rules generate conflicting candidate outcomes, supporting four strategies: DENY_OVERRIDES, ALLOW_OVERRIDES, PRIORITY_FIRST_MATCH (default), and MOST_SPECIFIC_WINS.
Policy conflict resolution in the microsoft/agent-governance-toolkit ensures deterministic access control when overlapping governance rules apply to agent requests. When the policy engine evaluates a request, it collects candidate decisions from every matching rule and uses a configurable ConflictResolutionStrategy to determine the final outcome. The resolution process is handled by the PolicyConflictResolver class, which ranks candidates based on action type, priority values, or policy scope specificity.
Understanding Conflict Resolution Strategies
The toolkit provides four distinct strategies in the ConflictResolutionStrategy enum defined in agent_os/policies/conflict_resolution.py (lines 57-64). Each strategy implements a different decision hierarchy for resolving conflicts between candidate decisions.
Available Resolution Strategies
DENY_OVERRIDES adopts a restrictive security posture where any deny rule takes precedence. If multiple candidates exist and at least one specifies a deny action, that decision wins regardless of priority or scope. Only when no deny rules are present does the highest-priority allow rule prevail.
ALLOW_OVERRIDES implements the opposite approach, favoring permissive access. Any allow rule automatically wins over deny rules. If no allow rules exist in the candidate set, the resolver falls back to selecting the highest-priority deny decision.
PRIORITY_FIRST_MATCH (the default) sorts candidates by their assigned priority value in descending order. The candidate with the highest integer priority wins the conflict, regardless of whether the action is allow or deny. This preserves legacy v1.0 toolkit behavior.
MOST_SPECIFIC_WINS evaluates the PolicyScope associated with each candidate, ranking by specificity where AGENT > ORGANIZATION > TENANT > GLOBAL (as defined in lines 66-76). When candidates share the same scope level, the strategy breaks ties using the priority value.
Core Data Models
The resolution process relies on several Pydantic models defined in the canonical implementation. The CandidateDecision class (lines 87-109) encapsulates a rule's action, priority, scope, and audit metadata including the policy name, rule name, and approver information.
The PolicyConflictResolver class (lines 42-53) serves as the execution engine. It accepts a ConflictResolutionStrategy during initialization and exposes a resolve() method that processes a list of CandidateDecision objects and returns a ResolutionResult.
The ResolutionResult object (lines 31-40) contains the winning decision, the strategy applied, the count of candidates examined, a boolean indicating whether a genuine conflict occurred, and a human-readable trace log explaining the selection rationale.
Configuring the Policy Engine
Configure the conflict resolution strategy when initializing the policy engine. The Policy class constructor in agentmesh/governance/policy.py (lines 491-513) accepts a conflict_strategy string parameter and internally converts it to the corresponding enum value.
from agentmesh.governance.policy import Policy
# Initialize with MOST_SPECIFIC_WINS strategy
engine = Policy(
conflict_strategy="most_specific_wins"
)
# Load and evaluate policies as usual
engine.load_policy(my_policy_definition)
result = engine.evaluate(request_context)
Valid string identifiers include "deny_overrides", "allow_overrides", "priority_first_match", and "most_specific_wins". Once configured, the engine automatically applies the selected strategy during every evaluation cycle without requiring manual intervention.
Resolving Conflicts Programmatically
For advanced scenarios requiring direct control, instantiate PolicyConflictResolver explicitly with your preferred strategy and pass a list of CandidateDecision objects.
from agentmesh.governance.conflict_resolution import (
ConflictResolutionStrategy,
CandidateDecision,
PolicyConflictResolver,
PolicyScope,
)
# Build candidate decisions from matching rules
candidates = [
CandidateDecision(
action="allow",
priority=10,
scope=PolicyScope.GLOBAL,
policy_name="global-policy",
rule_name="allow-everything",
),
CandidateDecision(
action="deny",
priority=5,
scope=PolicyScope.AGENT,
policy_name="agent-override",
rule_name="no-external-calls",
),
]
# Apply DENY_OVERRIDES strategy
resolver = PolicyConflictResolver(ConflictResolutionStrategy.DENY_OVERRIDES)
result = resolver.resolve(candidates)
print(f"Winner: {result.winning_decision.rule_name} -> {result.winning_decision.action}")
print("\n".join(result.resolution_trace))
This pattern is useful for unit testing custom rules or implementing custom policy evaluation pipelines outside the standard engine flow.
Implementation Architecture and Backwards Compatibility
The codebase maintains backwards compatibility through a shim layer located at agentmesh/governance/conflict_resolution.py (lines 19-34). This module attempts to re-export the canonical implementation from agent_os/policies/conflict_resolution.py when the optional agent_os package is available, otherwise falling back to a pure-Python implementation in _conflict_resolution_impl.py (lines 22-70).
This architecture ensures consistent import paths regardless of deployment context:
# Works with or without agent_os installed
from agentmesh.governance.conflict_resolution import PolicyConflictResolver
The canonical implementation in agent_os/policies/conflict_resolution.py serves as the source of truth for ConflictResolutionStrategy, PolicyScope, CandidateDecision, and PolicyConflictResolver definitions.
Summary
- ConflictResolutionStrategy provides four resolution modes:
DENY_OVERRIDES,ALLOW_OVERRIDES,PRIORITY_FIRST_MATCH(default), andMOST_SPECIFIC_WINS. - Configure the strategy in
Policy.__init__using theconflict_strategyparameter with lowercase string identifiers. - The
PolicyConflictResolverclass executes the resolution logic on lists ofCandidateDecisionobjects, producing aResolutionResultwith audit traces. - PolicyScope specificity (AGENT > ORGANIZATION > TENANT > GLOBAL) determines rankings when using
MOST_SPECIFIC_WINS. - The
agentmesh.governance.conflict_resolutionmodule provides a stable import interface that automatically selects the appropriate underlying implementation.
Frequently Asked Questions
How do I change the default conflict resolution strategy for all policy evaluations?
Pass the desired strategy identifier to the Policy constructor via the conflict_strategy parameter. According to the source code in agentmesh/governance/policy.py (lines 491-513), the constructor converts this string to a ConflictResolutionStrategy enum and initializes the internal PolicyConflictResolver. For example, use conflict_strategy="deny_overrides" to ensure deny rules always take precedence.
What happens when two rules have identical priority and scope under PRIORITY_FIRST_MATCH?
The resolver maintains the original candidate order during the priority sort (descending). When priority values are equal, the first candidate encountered in the input list wins the tie. The resolution_trace in the ResolutionResult documents which candidate was selected and why, enabling audit verification of tie-breaking behavior.
Can I use different conflict resolution strategies for different policy sets?
Each Policy engine instance maintains a single conflict resolution strategy configured at initialization. To use different strategies for different policy sets, instantiate separate Policy objects with distinct conflict_strategy values. Alternatively, invoke PolicyConflictResolver directly with different strategies for ad-hoc evaluations without relying on the engine's default configuration.
How does MOST_SPECIFIC_WINS determine scope specificity?
The strategy ranks candidates by the PolicyScope enum value defined in conflict_resolution.py (lines 66-76). The specificity hierarchy is AGENT (most specific) > ORGANIZATION > TENANT > GLOBAL (least specific). When candidates share the same scope level, the resolver breaks ties using the numeric priority value assigned to each rule.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →