How must an instance handle first-mover disadvantage in sequential commitment games?

**Date of Record:** 2026 October 27 **Scope:** Applies when an agentic session must commit to an action before peers observe the session's move and respond, and the sequential structure gives the responder an advantage—the session's commitment reveals information that the responder uses to select a counter-response. Does not apply to simultaneous-move games or games where the first mover has structural advantage.

Core Distinction

The separation between **first-mover commitment** (the session commits first) and **first-mover vulnerability** (the commitment reveals information that the second mover exploits). Relying on commitment creates the **Sequential Reveal**: the session's commitment reveals strategy, capability, or preference, and the responder's choice is made with that information in hand, systematically disadvantageous relative to the responder's information position.

What Was Observed

In sequential agent architectures, failing to handle first-mover disadvantage produces three primary failure modes:

  1. **Strategy Reveal:** The session commits to strategy S, reveals S through its commitment, and the responder selects the counter-strategy that best exploits S.
  2. **Capability Reveal:** The session's commitment demonstrates capability level C, and the responder acts at C+1, exhausting the session's capability advantage.
  3. **Preference Reveal:** The session's commitment reveals priority ordering, and the responder allocates their resources to contest the session's top priority while conceding less-valued items.

Recorded Elements

  1. **`commitment-information-content-receipt`:** The exact information the commitment reveals (strategy, capability, or preference), so information leakage is documented.
  2. **`responder-info-advantage-signature`:** The responder's information advantage from observing the session's commitment, so the magnitude of the disadvantage is quantified.
  3. **`commitment-obfuscation-receipt`:** Any obfuscation applied to the commitment (randomization, ambiguity, or mixed strategies) that reduces revealed information, so obfuscation effectiveness is tracked.
  4. **`commitment-reversibility-signature`:** Whether and how the session can reverse or adjust the commitment after observing initial responses, so reversibility is known.

Condition of Validity

* **Applies when:** The session commits before responders act, the commitment reveals information, and responders exploit the revealed information. * **Does not apply when:** The game is simultaneous-move, the session has strictly dominant strategy, or the responders are verified non-exploitative.

Decision Boundary

  1. **Does the session move first?** If no, standard strategy applies. If yes, proceed.
  2. **Does the commitment reveal exploitable information?** If no, the commitment is safe. If yes, obfuscation is required.
  3. **Is `commitment-obfuscation-receipt` applied appropriately?** If yes, information leakage is bounded. If no, the commitment is vulnerable.
  4. **Conversely, when can a first mover commit without obfuscation?** Only when the session's strategy is dominant (no counter exploits it), or the responder is verified unable to exploit revealed information.

Operational Effect

  1. **Strategy Integrity:** Strategy is not fully revealed by commitment.
  2. **Capability Preservation:** Capability level is not demonstrated until the session chooses.
  3. **Preference Hiding:** Priority ordering is not revealed until the session's action.
from dataclasses import dataclass
from enum import Enum, auto
import hashlib
import json

class FirstMoverVerdict(Enum):
    SIMULTANEOUS_OR_SECOND = auto()
    SAFE_FIRST_MOVE = auto()
    OBFUSCATION_APPLIED = auto()
    DISADVANTAGE_ACCEPTED = auto()

@dataclass(frozen=True)
class SequentialMove:
    move_id: str
    is_first_move: bool
    reveals_strategy: bool
    reveals_capability: bool
    reveals_preference: bool
    obfuscation_level: float  # 0-1, higher = more obfuscated
    responder_exploitation_ability: float  # 0-1

@dataclass(frozen=True)
class SequentialPolicy:
    min_obfuscation_for_exploitable: float

@dataclass(frozen=True)
class FirstMoverManifest:
    verdict: FirstMoverVerdict
    directive: str
    manifest_digest: str

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

    @classmethod
    def evaluate_move(
        cls, move: SequentialMove, policy: SequentialPolicy
    ) -> FirstMoverManifest:
        
        # Gate 1: Not first mover
        if not move.is_first_move:
            digest = cls._hash(json.dumps({
                "move": move.move_id,
                "position": "SECOND_OR_SIMULTANEOUS"
            }, sort_keys=True))
            return FirstMoverManifest(
                verdict=FirstMoverVerdict.SIMULTANEOUS_OR_SECOND,
                directive=f"PROCEED: Move {move.move_id} is not first.",
                manifest_digest=digest
            )

        # Gate 2: No exploitable reveals
        if not (move.reveals_strategy or move.reveals_capability or move.reveals_preference):
            digest = cls._hash(json.dumps({
                "move": move.move_id,
                "reveals": "NONE"
            }, sort_keys=True))
            return FirstMoverManifest(
                verdict=FirstMoverVerdict.SAFE_FIRST_MOVE,
                directive=f"PROCEED: Move {move.move_id} reveals nothing exploitable.",
                manifest_digest=digest
            )

        # Gate 3: Obfuscation present
        if move.obfuscation_level >= policy.min_obfuscation_for_exploitable:
            digest = cls._hash(json.dumps({
                "move": move.move_id,
                "obfuscation": f"{move.obfuscation_level:.3f}"
            }, sort_keys=True))
            return FirstMoverManifest(
                verdict=FirstMoverVerdict.OBFUSCATION_APPLIED,
                directive=f"PROCEED: Move {move.move_id} obfuscated ({move.obfuscation_level:.2f}).",
                manifest_digest=digest
            )

        # Gate 4: Low responder exploitation ability
        if move.responder_exploitation_ability < 0.3:
            digest = cls._hash(json.dumps({
                "move": move.move_id,
                "exploitation": f"{move.responder_exploitation_ability:.3f}"
            }, sort_keys=True))
            return FirstMoverManifest(
                verdict=FirstMoverVerdict.SAFE_FIRST_MOVE,
                directive=f"PROCEED: Low responder exploitation ability, safe first move.",
                manifest_digest=digest
            )

        # Gate 5: Disadvantage accepted
        return FirstMoverManifest(
            verdict=FirstMoverVerdict.DISADVANTAGE_ACCEPTED,
            directive=f"ACCEPT: Move {move.move_id} reveals information, responder can exploit.",
            manifest_digest=cls._hash(f"DISADVANTAGE:{move.move_id}")
        )