Twin verdicts and peer history
What happens after two nodes answer the same marked pair differently: who may referee, how the signed verdict reaches every node it concerns, how each node counts verdicts about its peers (counts, never a score), and the operator's opt-in rule to stop routing to a peer. Every line is checked against the capsules plugin at release v0.1.4, and the code that is not released yet is marked as such.
1What it is
When a client marks two requests as one pair and the two nodes that served them give different answers, the node that asked can have a third node re-answer the request and sign a verdict naming the twin it does not agree with.
Each node the verdict concerns checks the referee's signature and seals its own record of it, and the node that asked counts verdicts about each peer in four plain counts, kept on its own disk.
An operator can opt in to one rule on top of those counts: after N contradictions within D days, ask the host to stop routing to that peer, which the host does only if its own operator allowed it.
2Design choices
x-mesh-twin-bracket); the host never adds a second request.docs/TWIN-REFEREE-SELECTION.md §1; crates/evidence-plugin/src/referee/live.rs)weights_mismatch); that pair is not comparable and nothing counts against either twin.crates/evidence-plugin/src/referee/verdict.rs)TWIN-REFEREE-SELECTION.md §2, §3, §5)adjudication_issued or adjudication_received record citing it; a refused delivery is sealed by the node that tried to deliver it.crates/evidence-plugin/src/adjudication_records.rs, referee/hold.rs)crates/evidence-plugin/src/verdict_counts.rs)allow_peer_blocks = true for this plugin.crates/evidence-plugin/src/routing_rule.rs; CHANGELOG.md 0.1.3)adjudicate_differing_twins) and verdict delivery (share_adjudications) are both off until the operator turns them on, and the referee also needs record push (share_record_at_completion, off by default and symmetric) on both twins, since it is asked only once both pushed halves are held.docs/SHARING-POLICY.md; CHANGELOG.md 0.1.3)3What it shows / what it does not
Shows
- That the referee's key signed a ruling on one named pair of halves, at the tier it was asked from; a verdict with an altered tier fails verification.
- For a node that seals
adjudication_received: that it checked the referee's signature under the key it holds for that referee (announced by the referee's host, or set by the operator inCAPSULES_PEER_KEYS) before recording the verdict. - On the asking node: how many verified verdicts about each peer fall in each of four buckets, per model, with every verdict listed by id.
- For a rule block: the rule's name and a commitment to each verdict it cited, sealed by the node that asked for the block.
Does not show
- That an answer was right. A verdict says which twin's words the referee's own re-answer matched; the referee can be wrong too.
- That a twin really loaded the model or weights it names: both are the node's own word in its record. Hardware evidence is the route beyond that; v0.1.4's referee flow does not use it.
- Anything about pairs no client marked, about pairs whose twins named different weights, or about a node's future answers.
- Anything to other nodes: the counts are this node's own and are never sent. (A peer can ask for
history_card/1, a different thing: a derivation of the node's checkpoint history, with consistency proofs.) - A live run: v0.1.4 says a referee verdict on live hosts “is covered by tests only” (CHANGELOG 0.1.4).
4Example
A case from the plugin's referee parity corpus: node R holds three verdicts about three twin pairs served by node-a and node-b for one model, from three different referees, each one R asked for. The operator set N = 3 contradictions within 7 days. One of the three input records, then the answer the rules give.
// corpus/counts.json, case "fires_at_n_within_window": settings and one of three records
"settings": { "stop_routing_after_contradictions": "3", "stop_routing_window_days": "7" },
"host": { "blocked": [], "peer_blocks": true },
{
"model_attestation": {
"compute_attestation": {
"adjudication_received": {
"half_node_ids": ["node-a", "node-b"],
"halves": ["f47851c19964011b864120be12a7e10aea6e7c5d5076f8b97bd64ae39abcc97e",
"003127801349c5cb9d5cd627fc8ec273bdbcc4f428fa252eae141c6ccfb8b610"],
"model_hash": "b2767326b03d5e629546363ebe41714b78065e028709580996d60d2d119192da",
"received_at": "2026-09-29T00:00:00Z",
"referee_node_id": "node-c",
"verdict": "contradicted:node-a",
"verdict_capsule_id": "0660df2a44bc180e4a474fd809e3116ebde1a29c5f154260db7c9bca2f288301"
}
}
}
}
// rule_answers/counts.json: the answer (verdict id lists trimmed)
"counts": {
"node-a": { "b2767326…92da": { "contradicted": { "count": 3, … }, "corroborated": { "count": 0 },
"inconclusive": { "count": 0 }, "not_comparable": { "count": 0 } } },
"node-b": { "b2767326…92da": { "contradicted": { "count": 0 }, "corroborated": { "count": 3, … },
"inconclusive": { "count": 0 }, "not_comparable": { "count": 0 } } }
},
"outcomes": [ { "outcome": "blocked", "peer_id": "node-a", "verdict_capsule_ids": [ … 3 ids … ] } ],
"rule": { "after": 3, "window_days": 7 }
| Field | Type | Meaning |
|---|---|---|
| adjudication_received | object | The block of R's own record of a verdict it verified: sealed only after the referee's signature checked under the key R holds for that referee. |
| half_node_ids | [string, string] | The two twins the verdict judges. |
| halves | [hex, hex] | The twins' served records, by capsule_id; with the referee they form the pair key that counts once. |
| model_hash | hex | The model the twins named. Counts, the bar window and the referee tiers are all per model. |
| received_at | RFC 3339 | When R recorded the verdict (to the minute). Windows run from this time, never from a time the referee wrote. |
| referee_node_id | string | Who signed. A verdict whose referee is one of the twins counts nowhere. |
| verdict | string | contradicted:node-a: the referee's answer did not agree with node-a's. It counts as contradicted for node-a and corroborated for node-b. |
| verdict_capsule_id | hex | The signed verdict this record cites; each count lists these ids. |
| counts | object | Per peer, per model, the four buckets, zeros included. Never sent to anyone. |
| outcomes | array | blocked: the host took the block of node-a; the three cited verdicts never count toward the rule again. |
| host.peer_blocks | bool | The host offers the stop-routing path. Without it the outcome is no_host_hook and no verdict is spent. |
The same corpus pins the edges: N−1 contradictions do not fire; four contradictions from one referee never reach three; a contradiction seven days and one second old is outside a seven-day window; after the operator undoes a block, only new contradictions fire it again.
Source: Mesh-LLM/capsules:vectors/parity/referee/corpus/counts.json and vectors/parity/referee/rule_answers/counts.json @ b4e24453f572b2d9fe8f2cefbd0f35a5eb54b0ad (tag v0.1.4). Node ids are the corpus's placeholders.
5Status
The September deck split this into a “demo build today” and a later upstream plugin release. The code differs from both columns. Today means the latest tagged release; the upstream column is the plugin's move into the mesh-llm repository, which is not merged.
| Piece | Today: capsules v0.1.4 | Not released yet |
|---|---|---|
| Client-marked twin pairs | Built. Host side in mesh-llm v0.78.1 (PR 2144): the client's x-mesh-twin-bracket value reaches the plugin and is stripped before forwarding. | mesh-llm PR 2300 (merged, in no release yet) advertises it as the twin_bracket capability; no change in behaviour. |
| Referee verdict, signed by the referee | Built, off by default; on live hosts covered by tests only (CHANGELOG 0.1.4). The deck's “sealed by the requester, not yet signed by the referee” no longer holds: the referee signs. | capsules main @ efcada74026a2e18b9e62d77432888a31bbe9de4: the referee checks the signature on a signed adjudicate request before reading it; an unsigned one is still answered (PR 31, PR 33). |
| Verdict sealed by every node it concerns | Built: adjudication_issued on the referee, adjudication_received on the asking node and, with share_adjudications on (off by default), on each twin. | — |
| Verdict counts per peer | Built: the Peers page's four counts, local only (verdict_counts.rs). | — |
| Opt-in stop-routing rule | Built, off by default (routing_rule.rs). The host path it needs, allow_peer_blocks, is in mesh-llm release v0.78.1 (crates/mesh-llm-config/src/model.rs). The deck's “stop routing is manual” no longer holds. | — |
| The plugin inside mesh-llm | A separate repository and release. | mesh-llm PR 2459 (open) moves it into the mesh-llm workspace; PR 2337 (merged) lets a node install default plugins from a release bundle, and is inert until a release carries one. mesh-llm v0.78.1, its latest release, carries none. |
Evidence: Mesh-LLM/capsules tag v0.1.4 @ b4e24453f572b2d9fe8f2cefbd0f35a5eb54b0ad (CHANGELOG.md, docs/TWIN-REFEREE-SELECTION.md, docs/SHARING-POLICY.md, crates/evidence-plugin/src/verdict_counts.rs, routing_rule.rs, referee/); capsules main @ efcada74026a2e18b9e62d77432888a31bbe9de4; Mesh-LLM/mesh-llm tag v0.78.1 @ bc215ce5b880e21c6c3840b8574ab64f83278821; mesh-llm PRs 2144, 2300, 2337, 2459 (checked 2026-10-19).
6Specs
- Internet-Draft draft-mih-scitt-agent-action-capsule-05 — the record envelope each half, verdict and node's record of it is sealed in; the adjudication fields inside it are the plugin's own.
- Internet-Draft draft-mih-agent-evidence-request-00 — how a peer asks a node for records or its
history_card/1; the verdict counts are not among the answers. - Internet-Draft draft-mih-scitt-checkpointed-local-log-02 — the node's own log that each sealed record joins.
- RFC RFC 9052 — COSE: the
COSE_Sign1signature on each record and verdict. - RFC RFC 3339 — the timestamps the windows are measured from.
- Code Mesh-LLM/capsules v0.1.4 — the plugin, the referee parity corpus and the docs cited above.
- Code Mesh-LLM/mesh-llm#2459 — the plugin moving into the mesh-llm workspace (open).