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 → RESOLVEDThe 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
| Message | Who can send |
|---|---|
| SessionStart | Any authenticated agent with can_start_sessions |
| Proposal | Session initiator or any declared participant |
| Evaluation | Any declared participant |
| Objection | Any declared participant |
| Vote | Any declared participant (one vote per participant per proposal) |
| Commitment | Session initiator / authorized coordinator |
Terminal conditions
A session becomes eligible for Commitment when:
- At least one proposal exists, AND
- 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 historyError cases
| Error | When | How to handle |
|---|---|---|
FORBIDDEN on Proposal | Sender not a declared participant | Verify sender is in participants list |
FORBIDDEN on Commitment | Sender is not the session initiator | Only the coordinator should commit |
SESSION_NOT_OPEN on Vote | Session already resolved/expired | Check session state before voting |
DUPLICATE_MESSAGE | Same message_id sent twice | Safe 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