MACP

Decision Mode

Mode URI: macp.mode.decision.v1 Status: permanent RFC: RFC-MACP-0007

Structured decision making with proposals, evaluations, objections, votes, and terminal commitment.

When to use

Use Decision mode when multiple agents need to converge on a single outcome from a set of options. Common scenarios:

  • Selecting a deployment plan from multiple candidates
  • Choosing a vendor from shortlisted options
  • Approving or rejecting a design proposal
  • Any situation where agents propose, discuss, and vote

Participant model: declared

Participants are declared at SessionStart and fixed for the session's lifetime. Only declared participants can send mode-specific messages. The session initiator (typically the coordinator) can send any message type.

Determinism: semantic-deterministic

Same accepted envelope sequence → same semantic outcome. The vote counts, winner, and commitment will always be identical on replay. No external I/O or time-dependent logic is involved (beyond TTL).

Message flow

SessionStart

Proposal (one or more options)

Evaluation (analysis with recommendation: APPROVE|REVIEW|BLOCK|REJECT)

Objection (concerns with severity: low|medium|high|critical)

Vote (per-participant: approve|reject|abstain)

Commitment → RESOLVED

The phases are advisory — the runtime does not strictly enforce phase ordering beyond basic structural rules. However, the projection tracks phase transitions for your orchestrator logic.

Authorization rules

MessageWho can send
SessionStartAny authenticated agent with can_start_sessions
ProposalSession initiator or any declared participant
EvaluationAny declared participant
ObjectionAny declared participant
VoteAny declared participant (one vote per participant per proposal)
CommitmentSession initiator / authorized coordinator

Terminal conditions

A session becomes eligible for Commitment when:

  1. At least one proposal exists, AND
  2. The orchestrator's policy logic decides to commit (e.g., majority reached)

Only an authorized sender can emit the Commitment. The runtime validates that at least one proposal was submitted.

Session helper

from macp_sdk import AuthConfig, MacpClient, DecisionSession

client = MacpClient(
    target="127.0.0.1:50051",
    secure=False,
    auth=AuthConfig.for_dev_agent("coordinator"),
)

session = DecisionSession(client)
session.start(
    intent="pick a deployment plan",
    participants=["coordinator", "alice", "bob"],
    ttl_ms=60_000,
)

# Propose options
session.propose("p1", "deploy v2.1", rationale="tests passed, low risk")
session.propose("p2", "deploy v3.0-beta", rationale="new features ready")

# Evaluations
session.evaluate("p1", "APPROVE", confidence=0.9, reason="stable release", sender="alice")
session.evaluate("p2", "REVIEW", confidence=0.6, reason="needs more testing", sender="alice")

# Objections
session.raise_objection("p2", reason="beta not validated in staging", severity="high", sender="bob")

# Votes
session.vote("p1", "approve", reason="safe choice", sender="alice")
session.vote("p1", "approve", reason="agreed", sender="bob")

# Check projection and commit
proj = session.decision_projection
winner = proj.majority_winner()
if winner and not proj.has_blocking_objection(winner):
    session.commit(
        action="deployment.approved",
        authority_scope="release-management",
        reason=f"winner={winner}, votes={proj.vote_totals()}",
    )

Projection queries

The DecisionProjection tracks all proposals, evaluations, objections, and votes locally:

proj = session.decision_projection

# Proposals
proj.proposals                    # dict[str, DecisionProposalRecord]
proj.proposals["p1"].option       # "deploy v2.1"

# Evaluations
proj.evaluations                  # list[DecisionEvaluationRecord]

# Objections
proj.objections                   # list[DecisionObjectionRecord]
proj.has_blocking_objection("p1") # True if severity in {high, critical, block}

# Votes
proj.votes                        # dict[proposal_id, dict[sender, DecisionVoteRecord]]
proj.vote_totals()                # {"p1": 2, "p2": 0}
proj.majority_winner()            # "p1" (most positive votes)

# Lifecycle
proj.phase                        # "Proposal" | "Evaluation" | "Voting" | "Committed"
proj.is_committed                 # True after Commitment accepted
proj.commitment                   # CommitmentPayload or None
proj.transcript                   # list[Envelope] — full ordered history

Error cases

ErrorWhenHow to handle
FORBIDDEN on ProposalSender not a declared participantVerify sender is in participants list
FORBIDDEN on CommitmentSender is not the session initiatorOnly the coordinator should commit
SESSION_NOT_OPEN on VoteSession already resolved/expiredCheck session state before voting
DUPLICATE_MESSAGESame message_id sent twiceSafe to ignore (idempotent)

Real-world scenario: AI agent consensus

Three AI agents evaluate a security incident and decide on a response:

session = DecisionSession(client, auth=coordinator_auth)
session.start(
    intent="respond to security alert SEC-2025-0042",
    participants=["coordinator", "threat-analyzer", "impact-assessor", "response-planner"],
    ttl_ms=300_000,  # 5 minutes
)

# Each agent proposes a response
session.propose("p1", "isolate affected hosts", rationale="contain lateral movement", sender="threat-analyzer")
session.propose("p2", "patch and monitor", rationale="known CVE, patch available", sender="response-planner")

# Agents evaluate each other's proposals
session.evaluate("p1", "APPROVE", confidence=0.85, reason="stops spread", sender="impact-assessor")
session.evaluate("p2", "BLOCK", confidence=0.3, reason="too slow for active exploit", sender="threat-analyzer")

# Agents vote
session.vote("p1", "approve", sender="threat-analyzer")
session.vote("p1", "approve", sender="impact-assessor")
session.vote("p1", "approve", sender="response-planner")

# Commit the consensus
session.commit(
    action="incident.response.selected",
    authority_scope="security-operations",
    reason="unanimous: isolate affected hosts",
)

API Reference

::: macp_sdk.decision.DecisionSession

::: macp_sdk.projections.DecisionProjection