Decision Mode

Mode identifier: macp.mode.decision.v1 Participant model: declared Determinism: semantic-deterministic

Purpose

Structured decision-making with proposals, evaluations, objections, and votes leading to a bound outcome via Commitment.

Canonical references: RFC-MACP-0007 (Decision Mode) is normative for the state machine, authority rules, and validation constraints. See also the spec mode summaries and runtime modes guide › Decision Mode for validation as implemented. This page covers the TypeScript API.

Session Lifecycle

SessionStart → Proposal → Evaluation → Objection? → Vote → Commitment

Phase Transitions

PhaseTriggered ByAllowed Messages
ProposalSession startProposal
EvaluationFirst ProposalProposal, Evaluation, Objection
VotingFirst VoteVote
CommittedCommitment(terminal)

API

DecisionSession

import { DecisionSession, MacpClient, Auth } from 'macp-sdk-typescript';

const session = new DecisionSession(client, {
  sessionId: 'optional-custom-id',  // default: auto-generated UUID
  modeVersion: '1.0.0',             // default
  configurationVersion: 'config.default',
  policyVersion: 'policy.default',
  auth: Auth.devAgent('coordinator'),
});

start(input)

Initiates the decision session.

await session.start({
  intent: 'choose deployment strategy',
  participants: ['alice', 'bob', 'carol'],
  ttlMs: 300_000,    // 5 minutes (max 24 hours)
  contextId: 'proj-web-app',                 // optional correlation id
  extensions: { note: Buffer.from('...') },  // optional opaque extension payloads
  roots: [{ uri: 'https://git.example.com/repo', name: 'main-repo' }],
  maxSuspendMs: 3_600_000,  // optional (proto ≥ 0.1.5); 0/absent = runtime default
  sender: 'coordinator',    // optional, derived from auth
});

propose(input)

Submit a proposal for consideration.

await session.propose({
  proposalId: 'p1',          // unique within session
  option: 'canary-deploy',   // the proposed option
  rationale: 'gradual rollout with monitoring',
  supportingData: Buffer.from('...'),  // optional
});

evaluate(input)

Add an evaluation of a proposal.

await session.evaluate({
  proposalId: 'p1',
  recommendation: 'approve',  // 'approve' | 'review' | 'block' | 'reject' (case-insensitive)
  confidence: 0.92,            // 0.0 - 1.0
  reason: 'risk assessment favorable',
  sender: 'alice',
  auth: Auth.devAgent('alice'),
});

raiseObjection(input)

Flag an objection against a proposal.

await session.raiseObjection({
  proposalId: 'p1',
  reason: 'insufficient monitoring coverage',
  severity: 'high',  // 'low' | 'medium' | 'high' | 'critical' (case-insensitive; only 'critical' blocks)
  sender: 'bob',
  auth: Auth.devAgent('bob'),
});

vote(input)

Cast a vote on a proposal.

await session.vote({
  proposalId: 'p1',
  vote: 'approve',  // 'approve' | 'reject' | 'abstain' (case-insensitive; validated client-side)
  reason: 'all concerns addressed',
  sender: 'carol',
  auth: Auth.devAgent('carol'),
});

commit(input)

Finalize the decision session.

await session.commit({
  action: 'deployment.approved',
  authorityScope: 'release-management',
  reason: 'unanimous approval for canary deploy',
  commitmentId: 'optional-custom-id',  // default: auto-generated UUID
  outcomePositive: true,               // optional; omitted = positive outcome
});

Lifecycle helpers

Every session class (all 5 modes) shares the same lifecycle surface:

await session.metadata();            // { metadata: SessionMetadata } from the runtime
await session.cancel('reason');      // CancelSession — terminal (SESSION_STATE_CANCELLED)
await session.suspend('reason');     // SuspendSession (proto 0.1.3+) — non-terminal pause:
                                     // remaining TTL is banked; messages are rejected until resume()
await session.resume('reason');      // ResumeSession — restores SESSION_STATE_OPEN and the banked TTL
const stream = session.openStream(); // duplex MacpStream using the session's auth

Each helper accepts an optional trailing auth override. cancel(), suspend(), and resume() throw MacpAckError if the runtime rejects the request.

DecisionProjection

The projection tracks all proposals, evaluations, objections, and votes locally.

State

PropertyTypeDescription
proposalsMap<string, DecisionProposalRecord>Proposals by ID
evaluationsDecisionEvaluationRecord[]All evaluations
objectionsDecisionObjectionRecord[]All objections
votesMap<string, Map<string, DecisionVoteRecord>>Votes by proposalId → sender
transcriptEnvelope[]All accepted envelopes
phase'Proposal' | 'Evaluation' | 'Voting' | 'Committed'Current phase
commitmentRecord<string, unknown> | undefinedCommitment payload if resolved

Query Helpers

// Count positive votes per proposal
session.projection.voteTotals();
// → { 'p1': 2, 'p2': 1 }

// Proposal holding a strict majority (>50%) of all non-abstain votes, if any
session.projection.majorityWinner();
// → 'p1'

// APPROVE ratio for one proposal (ABSTAIN excluded from the denominator)
session.projection.voteRatio('p1');
// → 0.67

// Check for blocking objections (only severity 'critical' blocks per RFC-MACP-0004)
session.projection.hasBlockingObjection('p1');
// → true/false

// Evaluations split by recommendation: REVIEW (informational) vs the rest (qualifying)
session.projection.reviewEvaluations();
session.projection.qualifyingEvaluations();

// Commitment state (getters, available on every mode projection)
session.projection.isCommitted;        // true once a Commitment is applied
session.projection.isPositiveOutcome;  // undefined until committed; then outcomePositive (default true)

Vote Deduplication

If the same sender votes twice on the same proposal, the later vote replaces the earlier one.

RFC Validation Rules

The runtime enforces the cross-message rules — unique proposal_ids, Evaluation/Objection/Vote referencing an existing proposal, one Vote per participant per proposal (latest wins), commitment authority, and at least one proposal before resolution. The SDK validates only field formats client-side (vote/recommendation/severity values, confidence range, required fields). The normative rule set lives in RFC-MACP-0007 §4; the runtime modes guide › Decision Mode documents validation as implemented, including the NACK codes each rejection produces.

Example

See examples/decision-smoke.ts for a complete working example.