Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ futures-core = "0.3"
async-stream = "0.3"
async-trait = "0.1"
uuid = { version = "1", features = ["v4"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }

[dev-dependencies]
tempfile = "3"
Expand Down
25 changes: 14 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Reference runtime for the Multi-Agent Coordination Protocol (MACP).

This runtime implements the current MACP core/service surface, the five standards-track modes in the main RFC repository, and one experimental `multi_round` mode that remains available only by explicit canonical name. The focus of this release is freeze-readiness for SDKs and real-world unary and streaming integrations: strict `SessionStart`, mode-semantic correctness, authenticated senders, bounded resources, and durable restart recovery.
This runtime implements the current MACP core/service surface and all six standards-track modes in the main RFC repository. The focus of this release is freeze-readiness for SDKs and real-world unary and streaming integrations: strict `SessionStart`, mode-semantic correctness, authenticated senders, bounded resources, and durable restart recovery.

## What changed in v0.4.0

Expand Down Expand Up @@ -40,7 +40,11 @@ This runtime implements the current MACP core/service surface, the five standard
- **StreamSession enabled**
- `Initialize` advertises `stream: true`
- `StreamSession` provides per-session bidirectional streaming of accepted envelopes
- `WatchModeRegistry` and `WatchRoots` remain unimplemented
- `WatchModeRegistry` and `WatchRoots` implemented (basic: send initial state, hold stream open)
- **Structured logging via `tracing`**
- use `RUST_LOG` env var to control log level (e.g. `RUST_LOG=info`)
- **Per-mode metrics**
- tracked via `src/metrics.rs`

## Implemented modes

Expand All @@ -51,16 +55,13 @@ Standards-track modes:
- `macp.mode.task.v1`
- `macp.mode.handoff.v1`
- `macp.mode.quorum.v1`

Experimental mode:

- `macp.mode.multi_round.v1`

## Runtime behavior that SDKs should assume

### Session bootstrap

For the five standards-track modes, `SessionStartPayload` must include:
For all six standards-track modes, `SessionStartPayload` must include:

- `participants`
- `mode_version`
Expand Down Expand Up @@ -95,6 +96,7 @@ Unless `MACP_MEMORY_ONLY=1` is set, the runtime persists session and log snapsho
| `MACP_BIND_ADDR` | bind address | `127.0.0.1:50051` |
| `MACP_DATA_DIR` | persistence directory | `.macp-data` |
| `MACP_MEMORY_ONLY` | disable persistence when set to `1` | unset |
| `RUST_LOG` | `tracing` log level filter (e.g. `info`, `debug`) | unset |
| `MACP_ALLOW_INSECURE` | allow plaintext transport when set to `1` | unset |
| `MACP_TLS_CERT_PATH` | PEM certificate for TLS | unset |
| `MACP_TLS_KEY_PATH` | PEM private key for TLS | unset |
Expand Down Expand Up @@ -187,8 +189,8 @@ cargo run --bin fuzz_client
| `ListModes` | implemented |
| `ListRoots` | implemented |
| `StreamSession` | implemented |
| `WatchModeRegistry` | unimplemented |
| `WatchRoots` | unimplemented |
| `WatchModeRegistry` | implemented |
| `WatchRoots` | implemented |

## Architecture

Expand Down Expand Up @@ -227,11 +229,12 @@ runtime/
│ ├── log_store.rs # in-memory accepted-history log cache
│ ├── storage.rs # storage backend trait, FileBackend, crash recovery
│ ├── replay.rs # session rebuild from append-only log
│ ├── metrics.rs # per-mode metrics counters
│ ├── mode/ # mode implementations
│ └── bin/ # local development example clients
├── tests/
│ ├── integration_mode_lifecycle.rs # full-stack integration tests
│ ├── replay_round_trip.rs # replay tests for all 5 modes
│ ├── replay_round_trip.rs # replay tests for all 6 modes
│ ├── conformance_loader.rs # JSON fixture runner
│ └── conformance/ # per-mode conformance fixtures
├── docs/
Expand Down Expand Up @@ -261,8 +264,8 @@ Run `make sync-protos` to update local proto files from BSR.
## Development notes

- The RFC/spec repository remains the normative source for protocol semantics.
- This runtime only accepts the canonical standards-track mode identifiers for the five main modes.
- `multi_round` remains experimental and is not advertised by discovery RPCs.
- This runtime only accepts the canonical standards-track mode identifiers for all six modes.
- `multi_round` is now standards-track and is advertised by discovery RPCs.
- `StreamSession` is enabled and binds one gRPC stream to one session, emitting accepted envelopes in order.

See `docs/README.md` and `docs/examples.md` for the updated local development and usage guidance.
10 changes: 5 additions & 5 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ The RFC/spec repository is still the normative source for MACP semantics. These
## What is in this runtime profile

- MACP server over gRPC with unary RPCs and per-session bidirectional streaming
- five standards-track modes from the main RFC repository
- one experimental `macp.mode.multi_round.v1` mode kept off discovery surfaces
- six standards-track modes from the main RFC repository
- strict canonical `SessionStart` for standards-track modes
- authenticated sender derivation
- payload limits and rate limiting
Expand All @@ -21,6 +20,7 @@ The RFC/spec repository is still the normative source for MACP semantics. These
- `macp.mode.task.v1`
- `macp.mode.handoff.v1`
- `macp.mode.quorum.v1`
- `macp.mode.multi_round.v1`

## Freeze profile

Expand All @@ -37,10 +37,10 @@ Implemented and supported:
- `ListModes`
- `ListRoots`

Not yet implemented:
Streaming watch RPCs (basic — send initial state, hold stream open):

- `WatchModeRegistry` is unimplemented
- `WatchRoots` is unimplemented
- `WatchModeRegistry`
- `WatchRoots`

## Security model

Expand Down
5 changes: 2 additions & 3 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,10 @@ The `ModeRegistry` is the single source of truth for mode dispatch, replay, and

Responsibilities:

- register all mode implementations (standards-track and experimental)
- register all mode implementations
- provide mode lookup for dispatch and replay
- provide standards-track mode names for `ListModes`
- provide mode descriptors for `ListModes` and `GetManifest`
- classify modes as standards-track or experimental

Key methods:

Expand All @@ -58,7 +57,7 @@ Implemented modes:
- Task — delegated task with serial assignment, progress tracking, terminal reports
- Handoff — serial handoff offers with accept/decline disposition
- Quorum — threshold approval with ballots
- MultiRound (experimental) — iterative value convergence
- MultiRound — iterative value convergence with explicit Commitment

## 5. Storage layer

Expand Down
16 changes: 14 additions & 2 deletions docs/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ For these modes:
- `macp.mode.task.v1`
- `macp.mode.handoff.v1`
- `macp.mode.quorum.v1`
- `macp.mode.multi_round.v1`

`SessionStartPayload` must include all of the following:

Expand Down Expand Up @@ -131,15 +132,26 @@ Flow:
3. participants send ballots
4. coordinator emits `Commitment` after threshold is satisfied

## Example 6: Experimental multi-round mode
## Example 6: Multi-round mode

Run:

```bash
cargo run --bin multi_round_client
```

This mode is still experimental. It remains callable by the explicit canonical name `macp.mode.multi_round.v1`, but it is not advertised by discovery RPCs and it does not use the strict standards-track `SessionStart` contract.
Flow:

1. coordinator starts the session with strict `SessionStart` (participants, mode_version, configuration_version, ttl_ms)
2. participants exchange proposals across multiple rounds
3. convergence is tracked by the runtime
4. coordinator emits `Commitment` after convergence

Important runtime behavior:

- `macp.mode.multi_round.v1` is a standards-track mode and is advertised by discovery RPCs
- convergence is tracked but does not auto-resolve the session — an explicit `Commitment` is required
- uses the same strict `SessionStart` contract as all other standards-track modes

## Example 7: StreamSession

Expand Down
15 changes: 9 additions & 6 deletions docs/protocol.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,13 @@ Clients should call `Initialize` before using the runtime.
- `GetManifest`
- `ListModes`
- `ListRoots`
- `WatchModeRegistry`
- `WatchRoots`

## Still unimplemented
## Streaming watch RPCs

- `WatchModeRegistry` is unimplemented
- `WatchRoots` is unimplemented
- `WatchModeRegistry` — sends the current registry state, then holds the stream open
- `WatchRoots` — sends the current roots state, then holds the stream open

## StreamSession profile

Expand All @@ -47,6 +49,7 @@ For these modes:
- `macp.mode.task.v1`
- `macp.mode.handoff.v1`
- `macp.mode.quorum.v1`
- `macp.mode.multi_round.v1`

`SessionStartPayload` must bind:

Expand All @@ -57,9 +60,9 @@ For these modes:

Empty payloads are rejected. Empty `mode` values are rejected. Duplicate participant IDs are rejected.

## Experimental mode rule
## Multi-round mode

`macp.mode.multi_round.v1` remains available as an explicit experimental mode. It is not advertised by discovery RPCs and retains a more permissive bootstrap path for backward compatibility.
`macp.mode.multi_round.v1` is a standards-track mode. It uses the same strict `SessionStart` contract as all other standards-track modes. Convergence is tracked but does not auto-resolve the session — an explicit `Commitment` is required after convergence.

## Security profile

Expand Down Expand Up @@ -93,4 +96,4 @@ For standards-track modes, `CommitmentPayload` must carry version fields that ma

## Discovery notes

`ListModes` returns the five standards-track modes. `GetManifest` exposes a manifest that matches the implemented unary and streaming capabilities.
`ListModes` returns all six standards-track modes. `GetManifest` exposes a manifest that matches the implemented unary and streaming capabilities.
51 changes: 37 additions & 14 deletions src/bin/multi_round_client.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
#[path = "support/common.rs"]
mod common;

use common::{envelope, get_session_as, print_ack, send_as};
use macp_runtime::pb::SessionStartPayload;
use prost::Message;
use common::{
canonical_commitment_payload, canonical_start_payload, envelope, get_session_as, print_ack,
send_as,
};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
Expand All @@ -12,16 +13,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {

println!("=== Multi-Round Convergence Demo ===\n");

let start_payload = SessionStartPayload {
intent: "convergence test".into(),
ttl_ms: 60_000,
participants: vec!["alice".into(), "bob".into()],
mode_version: "experimental".into(),
configuration_version: "legacy".into(),
policy_version: String::new(),
context: vec![],
roots: vec![],
};
// Standards-track SessionStart with required fields
let ack = send_as(
&mut client,
"coordinator",
Expand All @@ -31,12 +23,13 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
"m0",
&session_id,
"coordinator",
start_payload.encode_to_vec(),
canonical_start_payload("convergence test", &["alice", "bob"], 60_000),
),
)
.await?;
print_ack("session_start", &ack);

// Alice contributes
let ack = send_as(
&mut client,
"alice",
Expand All @@ -52,6 +45,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
.await?;
print_ack("alice_contributes", &ack);

// Bob contributes differently
let ack = send_as(
&mut client,
"bob",
Expand All @@ -71,6 +65,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let meta = session.metadata.expect("metadata");
println!("[get_session] state={} mode={}", meta.state, meta.mode);

// Bob revises to match Alice → convergence
let ack = send_as(
&mut client,
"bob",
Expand All @@ -86,6 +81,34 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
.await?;
print_ack("bob_revises", &ack);

let session = get_session_as(&mut client, "alice", &session_id).await?;
let meta = session.metadata.expect("metadata");
println!(
"[get_session] state={} mode={} (converged, awaiting commitment)",
meta.state, meta.mode
);

// Coordinator commits after convergence
let ack = send_as(
&mut client,
"coordinator",
envelope(
"macp.mode.multi_round.v1",
"Commitment",
"m4",
&session_id,
"coordinator",
canonical_commitment_payload(
"c1",
"multi_round.converged",
"convergence",
"all participants agreed",
),
),
)
.await?;
print_ack("commitment", &ack);

let session = get_session_as(&mut client, "alice", &session_id).await?;
let meta = session.metadata.expect("metadata");
println!("[get_session] state={} mode={}", meta.state, meta.mode);
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ pub mod quorum_pb {

pub mod error;
pub mod log_store;
pub mod metrics;
pub mod mode;
pub mod mode_registry;
pub mod registry;
Expand Down
Loading
Loading