What invariants prevent coalition defection in multiparty coordination?

**Date of Record:** 2026 October 27 **Scope:** Applies when an agentic session is part of a coalition (alliance, quorum, or coordinating group) whose members have agreed to act together, and individual coalition members may find it beneficial to defect (act unilaterally, share coalition information, or abandon the coalition) for local gain. Does not apply to strictly two-party interactions or coalitions with cryptographic commitment enforcement.

Core Distinction

The separation between **coalition participation** (the session's ongoing coalition membership) and **coalition integrity** (all members actually act per the coalition agreement). Relying on participation creates the **Coalition Defection Cascade**: one member defects, other members observe the defection, they update their expectations of coalition integrity downward, and defection becomes individually rational for them—cascading until the coalition collapses.

What Was Observed

In coalition-based agent architectures, failing to prevent or detect defection produces three primary failure modes:

  1. **Free-Rider Exploitation:** One member contributes nothing to the coalition's coordination costs but benefits from coalition outcomes. Other members, observing the free-rider, reduce their contributions, and the coalition becomes under-resourced.
  2. **Information Leakage:** A coalition member shares coalition information with outsiders (adversaries or competitors). The coalition loses strategic advantage, and members who observe leakage lose trust in the coalition's confidentiality.
  3. **Strategic Abandonment:** A member defects at a strategically chosen moment (before a critical collective action), timing the defection to maximize its own gain while coalition is committed.

Recorded Elements

  1. **`coalition-agreement-receipt`:** The exact coalition agreement (what members commit to, what defection means, what consequences are defined), so the coalition's rules are explicit.
  2. **`member-contribution-tracking-signature`:** For each member, its contribution to the coalition (resources, information, or coordination effort), so free-riding is detectable.
  3. **`information-flow-audit-receipt`:** For coalition information, the exact distribution (who shared what with whom), so leakage is attributable.
  4. **`defection-detection-receipt`:** The session's classification of member behavior (compliant, non-compliant, or defection) with the evidence, so defection is distinguished from honest error.

Condition of Validity

* **Applies when:** The session is in a coalition, members can defect, and defection affects the coalition's outcomes. * **Does not apply when:** Coalition members are cryptographically bound to compliance, or defection has no meaningful consequence.

Decision Boundary

  1. **Is the session in a coalition?** If no, standard operation applies. If yes, proceed.
  2. **Is `member-contribution-tracking-signature` active?** If yes, free-riding is measurable. If no, contributions are assumed equal.
  3. **Does `information-flow-audit-receipt` show leakage?** If yes, leakage is attributed. If no, information is assumed confidential.
  4. **Conversely, when can a session tolerate defection without response?** Only when the defection is within an acceptable threshold (small contribution variance, minor information leak), or the coalition's value from that member's continued participation exceeds the cost of responding.

Operational Effect

  1. **Free-Rider Detection:** Contributions are tracked, not assumed.
  2. **Leakage Attribution:** Information flow is audited.
  3. **Cascade Prevention:** Early defections trigger responses before cascading.
from dataclasses import dataclass
from enum import Enum, auto
import hashlib
import json

class CoalitionVerdict(Enum):
    NOT_IN_COALITION = auto()
    COMPLIANT_MEMBER = auto()
    FREE_RIDING_DETECTED = auto()
    INFORMATION_LEAKAGE_DETECTED = auto()
    STRATEGIC_ABANDONMENT

@dataclass(frozen=True)
class CoalitionMember:
    member_id: str
    is_in_coalition: bool
    contribution_ratio: float  # vs. expected
    info_shared_with_outsiders: bool
    defection_timing_suspicious: bool

@dataclass(frozen=True)
class CoalitionPolicy:
    min_contribution_ratio: float

@dataclass(frozen=True)
class CoalitionManifest:
    verdict: CoalitionVerdict
    directive: str
    manifest_digest: str

class CoalitionDefectionGuard:
    @staticmethod
    def _hash(data: str) -> str:
        return hashlib.sha256(data.encode("utf-8")).hexdigest()

    @classmethod
    def evaluate_member(
        cls, member: CoalitionMember, policy: CoalitionPolicy
    ) -> CoalitionManifest:
        
        # Gate 1: Not in coalition
        if not member.is_in_coalition:
            digest = cls._hash(json.dumps({
                "member": member.member_id,
                "in_coalition": "FALSE"
            }, sort_keys=True))
            return CoalitionManifest(
                verdict=CoalitionVerdict.NOT_IN_COALITION,
                directive=f"PROCEED: {member.member_id} not in coalition.",
                manifest_digest=digest
            )

        # Gate 2: Information leakage
        if member.info_shared_with_outsiders:
            return CoalitionManifest(
                verdict=CoalitionVerdict.INFORMATION_LEAKAGE_DETECTED,
                directive=f"FLAG: {member.member_id} shared coalition info with outsiders.",
                manifest_digest=cls._hash(f"LEAKAGE:{member.member_id}")
            )

        # Gate 3: Free-riding
        if member.contribution_ratio < policy.min_contribution_ratio:
            return CoalitionManifest(
                verdict=CoalitionVerdict.FREE_RIDING_DETECTED,
                directive=f"FLAG: {member.member_id} contribution ratio {member.contribution_ratio:.2f} < {policy.min_contribution_ratio:.2f}.",
                manifest_digest=cls._hash(f"FREE_RIDE:{member.member_id}")
            )

        # Gate 4: Suspicious timing
        if member.defection_timing_suspicious:
            return CoalitionManifest(
                verdict=CoalitionVerdict.STRATEGIC_ABANDONMENT,
                directive=f"FLAG: {member.member_id} timing suspicious for strategic abandonment.",
                manifest_digest=cls._hash(f"ABANDONMENT:{member.member_id}")
            )

        # Gate 5: Compliant
        digest = cls._hash(json.dumps({
            "member": member.member_id,
            "contribution": f"{member.contribution_ratio:.3f}"
        }, sort_keys=True))
        
        return CoalitionManifest(
            verdict=CoalitionVerdict.COMPLIANT_MEMBER,
            directive=f"PROCEED: {member.member_id} compliant with coalition.",
            manifest_digest=digest
        )