# Council and Voting

TnsAI.Coordination provides two complementary systems for group decision-making: `CouncilExecutor` for multi-model deliberation with peer review, and `ConsensusBuilder` / `GroupDecisionFramework` for agent voting.

## CouncilExecutor

The `CouncilExecutor` lets you get the best answer from multiple LLMs by having them independently respond, anonymously peer-review each other, and then synthesize a top-rated answer. It is a 3-stage deliberation pipeline inspired by Karpathy's llm-council. Multiple LLM models independently answer a question, peer-review each other's responses (anonymized), and a chairman synthesizes the best answer.

### Three Stages

The deliberation process follows three stages. Each stage builds on the previous one to produce a high-quality final answer.

1. **Individual Responses** -- All members answer the question independently in parallel.
2. **Peer Review** -- Each member evaluates all anonymized responses, providing analysis and a ranking.
3. **Chairman Synthesis** -- The chairman receives all responses, reviews, and aggregate rankings, then produces a final synthesized answer.

### Usage

Create a council with at least two LLM members and a chairman, then call `deliberate()` with your question. The result includes the final synthesized answer, individual responses, peer reviews, and an aggregate ranking of models.

```java
CouncilExecutor council = CouncilExecutor.builder()
    .members(List.of(gpt4Client, claudeClient, geminiClient, grokClient))
    .chairman(geminiClient)
    .anonymize(true)             // default: true
    .timeout(Duration.ofMinutes(5))
    .build();

CouncilResult result = council.deliberate("How should we architect the auth system?");

// Final synthesized answer
System.out.println(result.finalAnswer());

// Aggregate ranking of models
result.topRanked().ifPresent(r ->
    System.out.println("Top: " + r.llmName() + " (avg rank: " + r.averageRank() + ")"));

// Access each stage
List<CouncilResponse> responses = result.individualResponses();  // Stage 1
List<CouncilReview> reviews = result.peerReviews();              // Stage 2
CouncilSynthesis synthesis = result.synthesis();                  // Stage 3
List<RankedMember> ranking = result.aggregateRanking();
```

### Builder Configuration

These are the parameters you can set when building a council.

| Parameter | Default | Description |
|---|---|---|
| `members` | (required) | At least 2 `LLMClient` instances |
| `chairman` | (required) | `LLMClient` that produces final synthesis |
| `anonymize` | `true` | Whether to hide model identities during peer review |
| `timeout` | 5 minutes | Per-stage timeout |

### Anonymization

Anonymization prevents bias during peer review by hiding which LLM produced each response. When `anonymize` is true, model identities are replaced with labels (Model A, Model B, ...) during peer review. The chairman receives both labels and real names.

### CouncilResult

The `CouncilResult` contains all outputs from the three deliberation stages, plus an aggregate ranking of which model performed best.

| Field | Type | Description |
|---|---|---|
| `individualResponses()` | `List<CouncilResponse>` | Stage 1 responses (llmName, response, durationMs) |
| `peerReviews()` | `List<CouncilReview>` | Stage 2 reviews (reviewerName, evaluation, parsedRanking) |
| `synthesis()` | `CouncilSynthesis` | Stage 3 chairman output (chairmanModel, synthesis) |
| `aggregateRanking()` | `List<RankedMember>` | Models ranked by average position across reviews |
| `finalAnswer()` | `String` | Shortcut to `synthesis().synthesis()` |
| `topRanked()` | `Optional<RankedMember>` | The highest-ranked model |

### RankedMember

A `RankedMember` records how well a specific LLM performed across all peer reviews. Lower `averageRank` is better (1.0 means consistently ranked first).

```java
public record RankedMember(String llmName, double averageRank, int rankingsCount)
```

Lower `averageRank` is better (1.0 = consistently ranked first).

## VotingMethod

TnsAI provides five voting methods for group consensus, ranging from simple majority to weighted voting. Choose the method that matches how much agreement you need.

| Method | Description | Threshold |
|---|---|---|
| `MAJORITY` | Simple majority (> 50%) | 50% |
| `SUPERMAJORITY` | Configurable threshold (default 2/3) | 66.7% |
| `UNANIMOUS` | All voters must agree | 100% |
| `RANKED_CHOICE` | Instant-runoff with ranked preferences | Majority after elimination |
| `WEIGHTED` | Votes weighted by voter weight | 50% of total weight |

## ConsensusBuilder

The `ConsensusBuilder` is the simplest way to run a vote among agents. You specify the question, the options, the voters, and the voting method, then call `.vote()` to get the result. It is a fluent builder that orchestrates a voting session among agents. Collects votes in parallel, applies the chosen voting method, and returns results.

```java
VotingResult result = ConsensusBuilder.create()
    .topic("Which database to use?")
    .options("PostgreSQL", "MySQL", "MongoDB")
    .among(List.of(dbExpert, backendDev, infraAgent))
    .method(VotingMethod.MAJORITY)
    .timeout(Duration.ofSeconds(30))
    .quorum(2)
    .vote();

System.out.println("Winner: " + result.winner());
System.out.println("Total votes: " + result.totalVotes());
System.out.println("Quorum met: " + result.quorumMet());
```

### Configuration

These builder methods let you configure every aspect of the voting session.

| Method | Default | Description |
|---|---|---|
| `.topic(String)` | (required) | The question to vote on |
| `.options(String...)` | (required) | Available choices |
| `.among(List<Agent>)` | (required) | Voting agents |
| `.method(VotingMethod)` | `MAJORITY` | Voting algorithm |
| `.timeout(Duration)` | 30 seconds | Maximum wait for votes |
| `.quorum(int)` | 1 | Minimum votes required |
| `.supermajorityThreshold(double)` | 0.667 | Threshold for `SUPERMAJORITY` |
| `.listener(VotingListener)` | none | Event callbacks |

### How Voting Works

Here is what happens internally when you call `.vote()`:

1. A prompt is built with the topic and options, sent to all agents in parallel.
2. Each agent's response is parsed using case-insensitive matching against the option list.
3. Votes are tallied using the selected method (`MajorityVoting` or `RankedChoiceVoting`).
4. The `VotingResult` includes the winner, vote counts, and quorum status.

### VotingListener

Attach a `VotingListener` to get real-time callbacks as votes come in, when quorum is reached, and when the final result is ready.

```java
ConsensusBuilder.create()
    .topic("Deploy now?")
    .options("Yes", "No", "Defer")
    .among(agents)
    .listener(new VotingListener() {
        @Override
        public void onVoteReceived(Vote vote) {
            System.out.println(vote.voterId() + " voted: " + vote.choice());
        }

        @Override
        public void onQuorumReached(int voteCount, int quorum) {
            System.out.println("Quorum reached: " + voteCount + "/" + quorum);
        }

        @Override
        public void onComplete(VotingResult result) {
            System.out.println("Winner: " + result.winner());
        }
    })
    .vote();
```

## GroupDecisionFramework

The `GroupDecisionFramework` is a more structured alternative to `ConsensusBuilder`, designed for use within an `AgentGroup`. It follows a formal propose-vote-converge lifecycle and records a decision trace for auditability. It provides a structured 3-phase protocol for group-level decision-making within an `AgentGroup`. Provides propose-vote-converge semantics with decision tracing.

```java
GroupDecisionFramework framework = new DefaultGroupDecisionFramework(traceStore);

// Full pipeline in one call
DecisionProposal proposal = DecisionProposal.of(
    "Which database?",
    List.of("PostgreSQL", "MySQL", "MongoDB")
);
DecisionResult result = framework.makeDecision(group, proposal);
```

### Three Phases

The framework splits every group decision into three distinct phases:

1. **Propose** -- Create a `DecisionProposal` with a topic, options, and voting method.
2. **Vote** -- Collect votes from all group members.
3. **Converge** -- Tally votes, determine outcome, save trace, broadcast result. Falls back to leader decision if no consensus and a leader exists.

```java
// Step-by-step usage for more control
DecisionProposal proposal = framework.propose(
    "Which database?",
    List.of("PostgreSQL", "MySQL", "MongoDB"),
    VotingMethod.RANKED_CHOICE
);

List<Vote> votes = framework.vote(group, proposal);
DecisionResult result = framework.converge(group, votes, proposal);
```

### DecisionTraceStore

Every decision is recorded in a trace store so you can review what was proposed, who voted for what, and what the outcome was. All decisions are persisted to a `DecisionTraceStore` for auditability. The default implementation `InMemoryDecisionTraceStore` keeps traces in memory.
