What invariants preserve contract precedence when terms conflict?
**Date of Record:** 2026 October 28 **Scope:** Applies when a contract contains multiple terms (clauses, addenda, attachments) that can conflict, and the session must determine which term takes precedence. Does not apply to contracts with formally non-conflicting terms or contracts that explicitly resolve all precedence.
Core Distinction
The separation between **term presence** (multiple terms exist) and **term precedence** (which term governs when terms conflict). Relying on presence creates the **Precedence Ambiguity**: conflicting terms are present, the session applies whichever it encounters, and both parties can claim the session is violating the contract by applying the "wrong" term.
What Was Observed
In complex-contract agent architectures, failing to preserve precedence produces three primary failure modes:
- **Silent Precedence Selection:** The session applies a term without noting the conflict, and the peer claims the conflicting term was intended to govern.
- **Precedence Flip-Flop:** The session applies different terms in different contexts, creating inconsistent obligations and confusing both parties about the governing terms.
- **Post-Hoc Precedence:** A dispute arises after the fact, and each party cites the precedence rule most favorable to it, without a principled resolution.
Recorded Elements
- **`contract-precedence-clause-receipt`:** The contract's explicit precedence clause (which terms override which), so precedence is documented.
- **`term-conflict-manifest-signature`:** The exact conflicting terms and their conflicts, so conflicts are visible.
- **`precedence-resolution-receipt`:** For each conflict, the resolved governing term based on the precedence clause, so application is principled.
- **`default-precedence-signature`:** For contracts without explicit precedence, the default rule (later term overrides earlier, specific overrides general, or session-application preserves original), so conflicts are handled even without explicit clauses.
Condition of Validity
* **Applies when:** The contract can contain conflicting terms, precedence rules are relevant, and application must be consistent. * **Does not apply when:** Contracts are formally verified non-conflicting, or conflicts are trivially resolvable.
Decision Boundary
- **Are there conflicting terms in the contract?** If no, standard application applies. If yes, proceed.
- **Does `contract-precedence-clause-receipt` resolve the conflict?** If yes, apply the clause. If no, proceed to default.
- **Does `default-precedence-signature` cover the conflict?** If yes, apply the default. If no, escalate.
- **Conversely, when can the session apply a term without precedence resolution?** Only when the term has no conflicts, or the conflict resolution is trivial (one term clearly governs without clause).
Operational Effect
- **Silent Selection Prevention:** Conflicts are surfaced and resolved.
- **Flip-Flop Prevention:** Precedence is consistent across contexts.
- **Post-Hoc Prevention:** Resolution is documented at application time.
from dataclasses import dataclass
from enum import Enum, auto
import hashlib
import json
class PrecedenceVerdict(Enum):
NO_CONFLICT = auto()
EXPLICIT_PRECEDENCE_APPLIED = auto()
DEFAULT_PRECEDENCE_APPLIED = auto()
UNRESOLVED_CONFLICT = auto()
@dataclass(frozen=True)
class TermConflict:
conflict_id: str
has_conflicting_terms: bool
precedence_clause_resolves: bool
default_rule_applies: bool
governing_term_id: str | None
@dataclass(frozen=True)
class PrecedencePolicy:
require_precedence_resolution: bool
@dataclass(frozen=True)
class PrecedenceManifest:
verdict: PrecedenceVerdict
directive: str
manifest_digest: str
class ContractPrecedenceGuard:
@staticmethod
def _hash(data: str) -> str:
return hashlib.sha256(data.encode("utf-8")).hexdigest()
@classmethod
def evaluate_conflict(
cls, conflict: TermConflict, policy: PrecedencePolicy
) -> PrecedenceManifest:
# Gate 1: No conflict
if not conflict.has_conflicting_terms:
digest = cls._hash(json.dumps({
"conflict": conflict.conflict_id,
"conflict": False
}, sort_keys=True))
return PrecedenceManifest(
verdict=PrecedenceVerdict.NO_CONFLICT,
directive=f"PROCEED: No conflict at {conflict.conflict_id}.",
manifest_digest=digest
)
# Gate 2: Explicit precedence
if conflict.precedence_clause_resolves and conflict.governing_term_id:
digest = cls._hash(json.dumps({
"conflict": conflict.conflict_id,
"governing": conflict.governing_term_id,
"type": "EXPLICIT"
}, sort_keys=True))
return PrecedenceManifest(
verdict=PrecedenceVerdict.EXPLICIT_PRECEDENCE_APPLIED,
directive=f"PROCEED: Conflict {conflict.conflict_id} resolved explicitly.",
manifest_digest=digest
)
# Gate 3: Default rule
if conflict.default_rule_applies and conflict.governing_term_id:
digest = cls._hash(json.dumps({
"conflict": conflict.conflict_id,
"governing": conflict.governing_term_id,
"type": "DEFAULT"
}, sort_keys=True))
return PrecedenceManifest(
verdict=PrecedenceVerdict.DEFAULT_PRECEDENCE_APPLIED,
directive=f"PROCEED: Conflict {conflict.conflict_id} resolved by default.",
manifest_digest=digest
)
# Gate 4: Unresolved
if policy.require_precedence_resolution:
return PrecedenceManifest(
verdict=PrecedenceVerdict.UNRESOLVED_CONFLICT,
directive=f"HALT: Conflict {conflict.conflict_id} unresolved.",
manifest_digest=cls._hash(f"UNRESOLVED:{conflict.conflict_id}")
)
# Gate 5: Default to unresolved
return PrecedenceManifest(
verdict=PrecedenceVerdict.UNRESOLVED_CONFLICT,
directive=f"FLAG: Conflict {conflict.conflict_id} unresolved without policy.",
manifest_digest=cls._hash(f"DEFAULT:{conflict.conflict_id}")
)