A minimal, transport-agnostic protocol for weighted multi-agent consensus. Designed for AI-native systems where agents hold heterogeneous beliefs, operate with different domain expertise, and must converge on actions without a central orchestrator.
All views expressed here are my own and do not represent the views of my employer. This is independent research on open protocol design, not affiliated with any organization or product.
1. Introduction
2. Terminology
10. Human Escalation
11. Decision Events
13. Transport
15. Failure Modes
16. Comparison with Other Protocols
18. Extensibility
- Appendix B: Full JSON Schema Definitions
- Appendix C: Example Scenarios
Multi-agent systems that must reach collective decisions face a fundamental coordination problem: agents hold heterogeneous beliefs, operate with different domain expertise and trust levels, and must converge on actions without a central orchestrator that becomes a bottleneck or single point of failure.
Existing distributed consensus algorithms (Raft, Paxos, PBFT) solve total-order state-machine replication - they are concerned with which log entry comes next. ACP solves a different problem: should this proposed action be taken, given that participating agents have weighted opinions, domain authority, and confidence levels that differ.
ACP is designed for AI-native systems where agents perceive event streams, reason about proposals, and express graded beliefs (confidence) - not just binary votes.
Define a minimal, transport-agnostic protocol for multi-agent weighted consensus
Support heterogeneous agent trust and domain expertise via weight factors
Enable human escalation as a first-class protocol primitive
Be implementable in any language without dependencies on a specific framework
Compose naturally with event-driven architectures
Total-order log replication (use Raft/Paxos for that)
Agent discovery and capability negotiation (see Google A2A)
LLM inference or agent reasoning internals
Distributed transaction semantics (ACID)
Prescribing transport mechanisms
1. No central orchestrator - resolution is computed from responses already posted to a shared workspace; no coordinator node is required
2. Weighted beliefs, not binary votes - agents express confidence and domain authority, not just yes/no
3. Immutable proposals - once posted, proposal content never changes; responses accumulate
4. Human escalation is first-class - tied decisions and policy triggers route to humans before taking action
5. Graceful degradation - timeout-based resolution ensures liveness even if some agents are unavailable
Proposal
A candidate action or decision posted to the shared workspace by an agent. A proposal has a lifecycle from PENDING through terminal states.
Endorsement
A response indicating agreement with a proposal, carrying a confidence level and weight factors.
Challenge
A response indicating disagreement with a proposal, carrying a confidence level and weight factors.
Quorum
The minimum number of responses required to trigger resolution by vote count (rather than waiting for timeout).
Resolution
The final determination of a proposal: its outcome (approved, rejected, escalated, or expired), the trigger that caused it, and the aggregated vote weights.
Domain
A named namespace scoping a proposal. Resolution is domain-scoped: an agent's authority within a domain is expressed as domain_authority.
Participant
Any agent authorized to respond to proposals within a given domain.
Weight
A scalar in [0, 1] derived from a participant's domain authority, confidence, and trust score. Weights determine how much a response influences the outcome.
Domain Authority
A scalar in [0, 1] measuring how expert a participant is within a specific domain. Domain authority is domain-specific: the same agent may have high authority in risk.credit and low authority in risk.market.
Confidence
A scalar in [0, 1] expressing how certain a participant is in their own response. A response with confidence 0.3 contributes less weight than one with confidence 0.9.
Trust Score
A scalar in [0, 1] representing system-level trust in a participant, typically derived from historical accuracy or security posture.
Escalation
A state where a proposal is forwarded to a human decision-maker because automated consensus cannot resolve it.
Decision Event
An immutable event emitted on resolution, carrying the outcome and causal lineage to the originating proposal.
ACP has three phases: Submit, Respond, and Resolve.
Phase 1: Submit
===============
Proposer Shared Workspace
| |
|-- POST Proposal -----→ |
| | (status: PENDING)
| |
Phase 2: Respond
================
Participant A Shared Workspace
| |
|← notify/poll --------- |
| |
|-- POST Endorsement --→ |
| |
Participant B Shared Workspace
| |
|← notify/poll --------- |
| |
|-- POST Challenge ----→ |
| |
Phase 3: Resolve
================
Shared Workspace
|
| [quorum reached OR expires_at passed]
|
| compute: votes_for, votes_against
| determine: outcome
| emit: Decision Event
|
| (status: RESOLVED | ESCALATED | EXPIRED)
Submit - A proposer creates a Proposal with a unique ID, domain, payload, quorum requirement, and optional expiry. The proposal enters PENDING state on the shared workspace.
Respond - Participants who have authority in the proposal's domain review it and post either an Endorsement or a Challenge. Each participant may respond at most once. Participants cannot respond to their own proposals.
Resolve - Resolution is triggered automatically when the quorum threshold is met, or when the proposal expires. The workspace computes weighted vote totals, determines the outcome, and emits a Decision Event. Resolution is a purely local computation - no inter-agent communication is required.
┌─────────┐
│ │
│ PENDING │
│ │
└────┬────┘
│
┌─────────────┼─────────────┐
│ │ │
quorum/ timeout/ manual
vote result vote result escalate
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌───────────┐
│ RESOLVED │ │ RESOLVED │ │ ESCALATED │
│ (quorum) │ │(timeout) │ │ │
└──────────┘ └──────────┘ └─────┬─────┘
│
human resolves
│
▼
┌──────────┐
│ RESOLVED │
│ (manual) │
└──────────┘
Special case: timeout with no responses:
┌─────────┐
│ PENDING │ ──timeout, 0 responses──→ EXPIRED
└─────────┘
RESOLVED, ESCALATED, and EXPIRED are terminal states. A proposal in a terminal state MUST NOT accept further responses.
Not all entries require full consensus. Implementations SHOULD support the following entry types:
| Type | Consensus Required | Default Behavior |
|---|---|---|
| `OBSERVATION` | No | Auto-resolves on post (APPROVED, QUORUM trigger) |
| `HYPOTHESIS` | No | Auto-resolves on post (APPROVED, QUORUM trigger) |
| `PROPOSAL` | Yes | Full consensus via Submit → Respond → Resolve |
A Proposal MUST include the following fields:
| Field | Type | Description | |
|---|---|---|---|
| `proposal_id` | ULID (string) | Unique identifier, sortable by creation time | |
| `type` | string | One of: `observation`, `hypothesis`, `proposal` | |
| `domain` | string | Namespace scoping this proposal (e.g., `"risk.credit"`) | |
| `author` | string | Participant ID of the proposer | |
| `payload` | object | Arbitrary JSON payload; semantics are application-defined | |
| `status` | string | Current lifecycle state | |
| `responses` | array | Accumulated endorsements and challenges | |
| `quorum` | integer ≥ 1 | Number of responses required to trigger resolution | |
| `created_at` | ISO 8601 datetime | Creation timestamp (UTC) | |
| `expires_at` | ISO 8601 datetime \ | null | Optional expiry; null means no timeout |
| `resolution` | object \ | null | Populated on terminal state; null while PENDING |
ACP defines exactly two response types:
endorse - The participant agrees with the proposal and supports its execution.
challenge - The participant disagrees with the proposal and opposes its execution.
Implementations MUST NOT introduce additional response types in the base protocol. Extensions may add response types via the extensibility mechanism described in Section 18.
| Field | Type | Required | Description | |
|---|---|---|---|---|
| `participant_id` | string | Yes | Identifier of the responding participant | |
| `response_type` | string | Yes | `"endorse"` or `"challenge"` | |
| `confidence` | float [0.0, 1.0] | Yes | Participant's self-assessed certainty | |
| `domain_authority` | float [0.0, 1.0] | Yes | Caller-supplied domain expertise level | |
| `trust_score` | float [0.0, 1.0] | Yes | System-assigned trust in this participant | |
| `weight` | float [0.0, 1.0] | Computed | `domain_authority × confidence × trust_score` | |
| `reason` | string \ | null | No | Human-readable rationale |
| `timestamp` | ISO 8601 datetime | Yes | UTC timestamp when response was recorded |
One response per participant: A participant MUST NOT post more than one response to the same proposal. Implementations MUST reject duplicate responses with an error.
No self-response: The author of a proposal MUST NOT respond to it. Implementations MUST reject self-responses.
PENDING-only responses: Responses MUST only be accepted for proposals in PENDING state. Implementations MUST reject responses to proposals in terminal states.
Each response carries a weight that determines its influence on the outcome:
Weight = clamp(domain_authority, 0, 1)
× clamp(confidence, 0, 1)
× clamp(trust_score, 0, 1)
Where clamp(x, lo, hi) = max(lo, min(hi, x)).
All three factors are independently clamped to [0, 1] before multiplication. The resulting weight is always in [0, 1].
votes_for = Σ weight(r) for r in responses where r.response_type == "endorse"
votes_against = Σ weight(r) for r in responses where r.response_type == "challenge"
if votes_for > votes_against:
outcome = APPROVED
elif votes_against > votes_for:
outcome = REJECTED
else: # votes_for == votes_against (including both 0.0)
outcome = ESCALATED
Note: A proposal with zero responses that reaches quorum (quorum = 0, which is invalid) cannot arise since quorum MUST be ≥ 1. However, if a proposal expires with zero responses, the outcome is EXPIRED (not ESCALATED), as described in Section 8.
The default quorum policy resolves when the count of responses meets or exceeds a threshold:
is_met = len(responses) >= threshold
threshold MUST be a positive integer (≥ 1). Implementations MUST default to count-based quorum when no other policy is specified.
An optional extension where quorum is based on accumulated vote weight rather than response count:
total_weight = Σ weight(r) for r in responses
is_met = total_weight >= threshold
threshold is a float in (0, max_possible_weight]. Implementations MAY support weighted quorum as an extension.
A special case where all registered participants in a domain must respond:
is_met = len(responses) >= len(domain_participants)
Implementations MAY support unanimous quorum. Note that unanimous quorum requires the implementation to know the set of eligible participants at proposal creation time.
Quorum check MUST run before timeout check. If both conditions are true simultaneously (e.g., quorum is reached at the exact moment of expiry), the trigger MUST be QUORUM.
A proposal with expires_at set MUST be checked for expiration. A proposal is expired when:
now >= expires_at
Where now is the current UTC time at the moment of the check.
| Condition | Outcome | Trigger |
|---|---|---|
| `now >= expires_at` AND `len(responses) == 0` | `EXPIRED` | `TIMEOUT` |
| `now >= expires_at` AND `len(responses) > 0` | Computed by voting formula | `TIMEOUT` |
Implementations MUST run periodic expiry sweeps to detect and resolve expired proposals. The sweep interval is implementation-defined. Proposals MUST NOT remain in PENDING state indefinitely if they have an expires_at that has passed.
If expires_at is null, the proposal does not expire by timeout. It can only be resolved by quorum or manual escalation.
Every proposal is scoped to a domain string. Domains partition the proposal namespace and enable domain-specific authority:
Domain strings SHOULD be hierarchical, using . as separator (e.g., "risk.credit", "ops.deploy")
Domains are case-sensitive
Implementations MAY enforce that only participants registered for a domain can respond to proposals in that domain
domain_authority in a response is specific to the domain of the proposal being responded to. The same participant may have different domain authority values across domains. Implementations SHOULD store per-domain authority in a participant registry.
ACP does not define cross-domain proposals. If an action requires consensus across multiple domains, the recommended pattern is a parent proposal that depends on child proposals per domain, with the parent resolved only when all children resolve.
A proposal MUST be escalated to human review in the following cases:
1. Tied vote - votes_for == votes_against after quorum or timeout (including both-zero)
2. Manual trigger - An authorized participant or system policy explicitly requests escalation
3. Policy-defined - Implementations MAY define policies that escalate before voting (e.g., "escalate all proposals touching production systems")
ESCALATED is a terminal state in the automated protocol. Once escalated:
No further automated responses are accepted
The proposal awaits human input
Human resolution sets the outcome (APPROVED or REJECTED) with trigger MANUAL
A Decision Event is emitted after human resolution
Implementations SHOULD record:
The reason for escalation (tie, manual, policy)
The timestamp of escalation
Which human or team is responsible for resolution
ACP does not prescribe how humans are notified. Implementations MAY use any notification channel (email, Slack, ticketing system). The protocol requires only that a human can post a resolution that transitions the proposal from ESCALATED to RESOLVED.
On resolution (any terminal state reached), an implementation MUST emit a Decision Event. This event serves as the authoritative, immutable record of the outcome.
| Field | Type | Description |
|---|---|---|
| `event_id` | ULID (string) | Unique event identifier |
| `event_type` | string | Always `"decision"` |
| `proposal_id` | ULID (string) | ID of the resolved proposal |
| `domain` | string | Domain of the resolved proposal |
| `outcome` | string | `"approved"`, `"rejected"`, `"escalated"`, or `"expired"` |
| `trigger` | string | `"quorum"`, `"timeout"`, or `"manual"` |
| `votes_for` | float | Weighted sum of endorsements |
| `votes_against` | float | Weighted sum of challenges |
| `decided_at` | ISO 8601 datetime | UTC timestamp of resolution |
| `schema_version` | string | Always `"1.0"` for this version |
Decision Events SHOULD include causal lineage: a reference to the proposal that caused them. This allows downstream systems to trace the chain from original observation to action.
Implementations MAY extend Decision Events with caused_by (the proposal_id) and payload_summary fields.
All IDs are ULID strings (26-character base32, lexicographically sortable by creation time)
All timestamps are ISO 8601 strings with UTC timezone (Z suffix or +00:00)
Float fields use JSON numbers
schema_version MUST be present in all wire objects and MUST be "1.0" for this revision
{
"schema_version": "1.0",
"proposal_id": "01HXYZ...",
"type": "proposal",
"domain": "risk.credit",
"author": "agent-risk-001",
"payload": {
"action": "approve_loan",
"loan_id": "LOAN-42",
"amount_usd": 250000
},
"status": "pending",
"responses": [],
"quorum": 2,
"created_at": "2025-01-15T10:00:00Z",
"expires_at": "2025-01-15T10:05:00Z",
"resolution": null
}
{
"schema_version": "1.0",
"participant_id": "agent-credit-002",
"response_type": "endorse",
"confidence": 0.92,
"domain_authority": 0.95,
"trust_score": 0.88,
"weight": 0.7697,
"reason": "Credit score 780, DTI 28% - within policy limits",
"timestamp": "2025-01-15T10:01:30Z"
}
{
"schema_version": "1.0",
"participant_id": "agent-risk-003",
"response_type": "challenge",
"confidence": 0.75,
"domain_authority": 0.80,
"trust_score": 0.90,
"weight": 0.54,
"reason": "Property in flood zone, no flood insurance",
"timestamp": "2025-01-15T10:02:00Z"
}
{
"schema_version": "1.0",
"outcome": "approved",
"trigger": "quorum",
"votes_for": 1.5394,
"votes_against": 0.54,
"decided_at": "2025-01-15T10:02:00Z"
}
{
"schema_version": "1.0",
"event_id": "01HABC...",
"event_type": "decision",
"proposal_id": "01HXYZ...",
"domain": "risk.credit",
"outcome": "approved",
"trigger": "quorum",
"votes_for": 1.5394,
"votes_against": 0.54,
"decided_at": "2025-01-15T10:02:00Z"
}
ACP is transport-agnostic. The protocol defines message semantics and wire formats; it does not prescribe delivery.
| Transport | Use Case |
|---|---|
| HTTP POST | Simple request-response, polling |
| WebSocket | Real-time push notifications |
| Message queue (NATS, Kafka, SQS) | High-throughput, persistent delivery |
| In-process event bus | Single-process or testing |
Implementations SHOULD provide at-least-once delivery for proposal notifications to participants. Duplicate response rejection (Section 5.3) provides idempotency at the protocol level.
ACP does not require total ordering of responses. Resolution is based on accumulated weight, not response order.
Implementations MUST authenticate participants before accepting proposals or responses. Unauthenticated submissions MUST be rejected.
Recommended mechanisms:
Asymmetric key pairs (Ed25519) bound to participant identity
JWT tokens for stateless authentication
mTLS for transport-level identity
Access to domains SHOULD be enforced via an authorization policy. Participants MUST be authorized to submit proposals or responses within a given domain. Cross-domain access MUST require explicit grants.
Implementations SHOULD sign proposal payloads with the author's private key. Signings allow downstream systems to verify that a decision was based on an unmodified payload.
ULID-based proposal IDs provide natural replay protection: each proposal ID is unique and embeds a timestamp. Implementations MUST reject proposals with duplicate proposal_id values.
In environments with data classification requirements, implementations SHOULD enforce that participants have the required clearance level before they can read a proposal's payload.
All proposal submissions, responses, and resolutions MUST be written to an immutable audit log. The log SHOULD include: actor ID, action type, proposal ID, timestamp, and payload hash.
If an agent fails to respond before expires_at, the timeout resolution path (Section 8) handles the outcome. The protocol is designed to be liveness-preserving: if some agents are unavailable, resolution still occurs via timeout.
If quorum is set too high (relative to the number of available participants), the proposal may expire via timeout with fewer responses than quorum. This is handled by Section 8.2. Implementations SHOULD set quorum conservatively to avoid spurious expirations.
If a participant accidentally submits a response twice (e.g., due to at-least-once delivery), the implementation MUST reject the duplicate with a clear error. The first response stands.
In distributed implementations, multiple nodes may attempt to resolve the same proposal concurrently. Implementations MUST use a compare-and-swap or optimistic locking mechanism to ensure exactly one resolution is recorded.
Timeout resolution depends on comparing now with expires_at. Implementations SHOULD use a synchronized time source (NTP or equivalent). Clock skew SHOULD be bounded and documented. Implementations MAY add a configurable grace period to expires_at checks to accommodate skew.
Raft and Paxos solve total-order state-machine replication: given a set of proposed log entries, all nodes agree on the same ordered sequence. This is the right tool for replicated databases and distributed coordination metadata.
ACP solves a different problem: should this specific proposed action be taken, given weighted, graded opinions from agents with heterogeneous expertise. ACP does not produce a log; it produces a decision. The two protocols are complementary: ACP decisions can be replicated via Raft for durability.
Google's A2A protocol focuses on agent discovery, capability negotiation, and task delegation: how agents advertise their skills and how clients delegate tasks to them. A2A is concerned with the dispatch layer.
ACP is concerned with the consensus layer: once agents are known and a task is underway, ACP determines whether a proposed action should proceed. The two protocols are complementary and can be deployed together.
The FIPA Contract Net Protocol (FIPA SC00029H) is a bidding-based coordination protocol where a manager issues a call for proposals, agents submit bids, and the manager selects a winner. It is structurally similar to ACP in that proposals are posted and agents respond.
ACP differs in key ways:
Weighted consensus, not selection: ACP aggregates all responses into a collective decision; Contract Net selects a single winning agent
No manager role: ACP has no manager agent; resolution is a pure computation over accumulated responses
Graded confidence: ACP responses carry continuous confidence and weight values; Contract Net proposals are typically discrete bids
Human escalation: ACP defines escalation as a first-class outcome; Contract Net does not
A Basic conformant implementation MUST:
☐ Accept proposals with required fields (Section 4.3)
☐ Enforce PENDING-only response acceptance (Section 5.3)
☐ Enforce no-self-response constraint (Section 5.3)
☐ Enforce one-response-per-participant (Section 5.3)
☐ Compute weights using the formula in Section 6.1
☐ Implement count-based quorum (Section 7.1)
☐ Implement timeout resolution (Section 8)
☐ Emit a Resolution on all terminal transitions (Section 11)
☐ Support the wire format in Section 12
☐ Reject duplicate proposal_id values (Section 14.4)
A Full conformant implementation additionally MUST:
☐ Implement all Basic requirements
☐ Support OBSERVATION and HYPOTHESIS auto-resolution (Section 4.2)
☐ Implement human escalation with manual resolution (Section 10)
☐ Emit Decision Events with causal lineage (Section 11.3)
☐ Implement periodic expiry sweep (Section 8.3)
☐ Enforce domain-scoped authorization (Section 14.2)
☐ Write to an audit log (Section 14.6)
☐ Support schema_version validation in all wire objects
Implementors MAY extend the protocol with:
Weighted quorum (Section 7.2)
Unanimous quorum (Section 7.3)
CRDT-based distributed blackboards for multi-node deployments (Section 18.3)
Custom weight functions (Section 18.2)
Payload signing (Section 14.3)
Push-based participant notification
Implementors MUST NOT change core semantics (weight formula, outcome determination, constraint rules) in a way that would prevent interoperability with compliant implementations.
Implementations MAY define custom quorum policies by implementing the QuorumPolicy interface:
class QuorumPolicy(Protocol):
def is_met(self, proposal: Proposal) -> bool: ...
Custom policies MUST return True when quorum is met and False otherwise. Implementations SHOULD document which policies they support.
Implementations MAY replace the default weight formula with a custom function, provided:
The function returns a value in [0, 1]
It is deterministic given the same inputs
The custom function is documented in the implementation's conformance declaration
For multi-node deployments where multiple blackboard nodes must converge on the same proposal state, implementations MAY use a Conflict-free Replicated Data Type (CRDT) as the storage backend. A grow-only set (G-Set) CRDT is well-suited for accumulating responses, since responses are append-only and the ordering does not matter for the weight computation.
This extension is informative only - it is not required for conformance.
The schema_version field in wire objects is reserved for future backward-compatible extensions. Implementations MUST reject wire objects with schema_version values they do not support.
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://acp-protocol.org/v1/proposal",
"title": "ACP Proposal",
"type": "object",
"required": [
"schema_version", "proposal_id", "type", "domain", "author",
"payload", "status", "responses", "quorum", "created_at"
],
"properties": {
"schema_version": { "type": "string", "const": "1.0" },
"proposal_id": {
"type": "string",
"description": "ULID - 26-character base32 sortable unique identifier"
},
"type": {
"type": "string",
"enum": ["observation", "hypothesis", "proposal"]
},
"domain": { "type": "string", "minLength": 1 },
"author": { "type": "string", "minLength": 1 },
"payload": { "type": "object" },
"status": {
"type": "string",
"enum": ["pending", "resolved", "escalated", "expired"]
},
"responses": {
"type": "array",
"items": { "$ref": "#/$defs/Response" }
},
"quorum": { "type": "integer", "minimum": 1 },
"created_at": { "type": "string", "format": "date-time" },
"expires_at": {
"oneOf": [{ "type": "string", "format": "date-time" }, { "type": "null" }]
},
"resolution": {
"oneOf": [{ "$ref": "#/$defs/Resolution" }, { "type": "null" }]
}
},
"$defs": {
"Response": {
"type": "object",
"required": [
"schema_version", "participant_id", "response_type",
"confidence", "domain_authority", "trust_score", "weight", "timestamp"
],
"properties": {
"schema_version": { "type": "string", "const": "1.0" },
"participant_id": { "type": "string" },
"response_type": { "type": "string", "enum": ["endorse", "challenge"] },
"confidence": { "type": "number", "minimum": 0, "maximum": 1 },
"domain_authority": { "type": "number", "minimum": 0, "maximum": 1 },
"trust_score": { "type": "number", "minimum": 0, "maximum": 1 },
"weight": { "type": "number", "minimum": 0, "maximum": 1 },
"reason": { "oneOf": [{ "type": "string" }, { "type": "null" }] },
"timestamp": { "type": "string", "format": "date-time" }
}
},
"Resolution": {
"type": "object",
"required": [
"schema_version", "outcome", "trigger",
"votes_for", "votes_against", "decided_at"
],
"properties": {
"schema_version": { "type": "string", "const": "1.0" },
"outcome": {
"type": "string",
"enum": ["approved", "rejected", "escalated", "expired"]
},
"trigger": {
"type": "string",
"enum": ["quorum", "timeout", "manual"]
},
"votes_for": { "type": "number", "minimum": 0 },
"votes_against": { "type": "number", "minimum": 0 },
"decided_at": { "type": "string", "format": "date-time" }
}
}
}
}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://acp-protocol.org/v1/decision-event",
"title": "ACP Decision Event",
"type": "object",
"required": [
"schema_version", "event_id", "event_type", "proposal_id",
"domain", "outcome", "trigger", "votes_for", "votes_against", "decided_at"
],
"properties": {
"schema_version": { "type": "string", "const": "1.0" },
"event_id": { "type": "string" },
"event_type": { "type": "string", "const": "decision" },
"proposal_id": { "type": "string" },
"domain": { "type": "string" },
"outcome": {
"type": "string",
"enum": ["approved", "rejected", "escalated", "expired"]
},
"trigger": {
"type": "string",
"enum": ["quorum", "timeout", "manual"]
},
"votes_for": { "type": "number", "minimum": 0 },
"votes_against": { "type": "number", "minimum": 0 },
"decided_at": { "type": "string", "format": "date-time" }
}
}
Three agents review a loan approval proposal. Two endorse, one challenges. Quorum is 2.
Proposal: domain="risk.credit", quorum=2
Agent A (domain_authority=0.9, confidence=0.85, trust_score=0.9):
→ ENDORSE, weight = 0.9 × 0.85 × 0.9 = 0.6885
Agent B (domain_authority=0.8, confidence=0.7, trust_score=0.85):
→ ENDORSE, weight = 0.8 × 0.7 × 0.85 = 0.476
[Quorum reached: 2 responses ≥ 2]
votes_for = 0.6885 + 0.476 = 1.1645
votes_against = 0.0
Outcome: APPROVED (trigger: QUORUM)
Note: The third agent never needed to respond - quorum was met at 2.
A proposal requires sign-off from two domain experts. One senior expert challenges; a junior expert endorses. Despite 1-1 response count, weight determines outcome.
Proposal: domain="ops.deploy", quorum=2
Senior Expert (domain_authority=0.95, confidence=0.90, trust_score=0.95):
→ CHALLENGE, weight = 0.95 × 0.90 × 0.95 = 0.8123
Junior Agent (domain_authority=0.4, confidence=0.6, trust_score=0.7):
→ ENDORSE, weight = 0.4 × 0.6 × 0.7 = 0.168
[Quorum reached: 2 responses ≥ 2]
votes_for = 0.168
votes_against = 0.8123
Outcome: REJECTED (trigger: QUORUM)
A proposal with quorum=3 expires with only one endorsement.
Proposal: domain="finance.budget", quorum=3, expires_at=T+5min
Agent A (domain_authority=0.8, confidence=0.75, trust_score=0.9):
→ ENDORSE, weight = 0.8 × 0.75 × 0.9 = 0.54
[T+5min: expires_at reached, only 1 response, quorum not met]
votes_for = 0.54
votes_against = 0.0
Outcome: APPROVED (trigger: TIMEOUT)
No other agents responded, but the single endorsement outweighs zero challenges.
Two high-authority agents submit exactly equal-weight opposing responses.
Proposal: domain="strategy.pricing", quorum=2
Agent A (domain_authority=0.9, confidence=0.8, trust_score=0.9):
→ ENDORSE, weight = 0.9 × 0.8 × 0.9 = 0.648
Agent B (domain_authority=0.9, confidence=0.8, trust_score=0.9):
→ CHALLENGE, weight = 0.9 × 0.8 × 0.9 = 0.648
[Quorum reached: 2 responses ≥ 2]
votes_for = 0.648
votes_against = 0.648
votes_for == votes_against → ESCALATED (trigger: QUORUM)
Human resolves → APPROVED (trigger: MANUAL)
This is a draft specification published for community review. If you have feedback, corrections, or implementation experience to share, reach out at hello@prakulsharma.ai.
All views expressed in this document are solely my own and do not represent or reflect the views, positions, or policies of my employer. This is independent thinking on open protocol standards, not affiliated with any organization or product.