Autonomous dispute resolution for the agent economy — powered by GenLayer Intelligent Contracts.
When agents disagree, let the evidence speak.
Live app (GenLayer Studionet, chain 61999): https://agent-court-genlayer.vercel.app/
AgentCourt is a decentralized arbitration protocol that turns a machine-readable dispute into an executable investigation:
Claim → Evidence → Investigation → Independent Reasoning → Adversarial Review → Consensus → Verdict → Settlement
Instead of a single LLM answer or a centralized moderator, four independent evaluator roles reason over the same evidence, an adversarial reviewer challenges the emerging majority, GenLayer validators re-derive the consensus exactly and independently re-check decisive verdicts, and the final verdict is derived on-chain — never supplied by a caller.
Autonomous agents and protocols transact with each other constantly — datasets delivered, APIs purchased, escrows funded, SLAs promised. When they disagree, today's options are human support queues, private arbitrators, or a single opaque AI call. None of that scales to machine-to-machine commerce, and none of it is auditable.
AgentCourt provides recourse as a programmable, transparent primitive:
- Evidence is explicit and referenced by ID.
- Reasoning is independent, multi-role, and adversarially reviewed.
- Uncertainty is represented (
UNVERIFIABLE,REVIEW) instead of forced into a binary. - Settlement is deterministic and reads only the on-chain verdict — fail-closed when review is required.
| Pain today | AgentCourt answer |
|---|---|
| "The AI said so" with no process | Full evidence → evaluators → adversarial → consensus trail on-chain |
| One LLM call is trivially gamed | 4 roles + adversarial + sandboxed validator check (run_nondet) |
| Caller can submit a favorable verdict | Caller input ≠ final verdict — finalize_verdict(dispute_id) takes no verdict |
| Uncertain cases auto-settle unsafely | Fail-closed states: EVALUATION_FAILED / INCONCLUSIVE / DISPUTED freeze settlement |
| Escrow can't consume AI opinions | Machine-readable verdict: {verdict, confidence, resolution, reviewRequired} |
All shots are from the live deployment on GenLayer Studionet (chain 61999), walking one dispute from creation to a fail-closed UNVERIFIABLE verdict.
Registry of on-chain disputes with totals, search/filter, and per-dispute lifecycle badges.
Workflow actions gated by lifecycle state, claim metadata, the verdict derived from evaluation, and the event timeline.
Submitted evidence with type, source, and verification status, plus provenance stats.
Four independent evaluator roles, each with its verdict, confidence, and reasoning hash.
The reviewer challenges the emerging majority and itemizes the disputed assumptions.
Vote breakdown, agreement ratio, and material-disagreement detection with the consensus reasoning.
The machine-readable verdict that escrow and settlement contracts consume.
| Live frontend | https://agent-court-genlayer.vercel.app/ |
| Network | GenLayer Studionet — Chain ID 61999 |
| Explorer | https://explorer-studio.genlayer.com |
The live frontend is bound to the two contracts below via VITE_AGENTCOURT_CORE / VITE_RESOLUTION_MANAGER (see .env.example).
| Contract | Address |
|---|---|
| AgentCourtCore (AgentCourt.py) | 0x2cC0B2285E67cA33db09bDC1Eec9f64F8ea72F3a — import in Studio |
| ResolutionManager (ResolutionManager.py) | 0xC7225962aB73576087Ff157798Ffb3967E36Ec11 — import in Studio |
set_core is wired for this deployment (manager → core verified via get_core; manager/core owner: 0x5B36…4c89). There is no Solidity path — both contracts are Python GenLayer Intelligent Contracts.
To confirm the deployed contracts respond and are wired correctly:
CORE=0x2cC0B2285E67cA33db09bDC1Eec9f64F8ea72F3a \
MANAGER=0xC7225962aB73576087Ff157798Ffb3967E36Ec11 \
./scripts/verification/verify.sh
# → checks get_dispute_count, get_core wiring, get_owner, is_paused; prints "Verification PASSED"Each party to a dispute can bond its stake on-chain before the window closes:
deposit_stake(dispute_id) ← @gl.public.write.payable, sender must be claimant/respondent,
value ≥ dispute.stake, one deposit per party
get_escrow(dispute_id) ← deposited flag + amounts per party, released/payouts record
execute_settlement(id) ← winner refunded its bond and receives the slashed loser bond;
reviewRequired/UNVERIFIABLE/REVIEW keep funds escrowed (fail-closed);
settlement is idempotent — no double release
Live verification scripts (run on Studionet):
scripts/verification/smoke_staking.mjs— deposit,get_escrowhold, settlement blocked pre-verdictscripts/verification/e2e_staking.mjs— both parties stake → live evaluation → finalize → settle (or fail-closed INCONCLUSIVE retention)tests/unit/test_escrow_staking.py— 13 unit cases incl. release math, double-release guard, review-freeze, unauthorized/insufficient/double deposits, case-insensitive party matching
git clone https://github.com/habte-selassie27/AgentCourt.git
cd AgentCourt
npm install
cp .env.example .env # set VITE_AGENTCOURT_CORE / VITE_RESOLUTION_MANAGER if you redeploynpm run devConnect a wallet on chain 61999 (the UI offers to switch/add the network), then walk the full lifecycle from the dashboard:
- Create dispute (parties, agreement hash, claim type, stake, deadline + evidence)
- Start investigation
- Request evaluation — live web fetch of evidence URLs + 4 LLM evaluators + adversarial review under validator consensus (takes minutes)
- Finalize verdict — no inputs; derived from the stored evaluation
- Execute settlement — only when
reviewRequiredis false and no appeal is open - Appeal (optional, only from a live
VERDICT) — Open appeal moves the dispute toAPPEALED; re-run Request evaluation (allowed while the appeal is open), Finalize verdict to write a superseding, higher-version verdict, then the ResolutionManager owner calls Resolve appeal (compares core verdict versions); settlement is refused until that call closes the appeal. A settled (CLOSED) dispute is final and cannot be appealed
pytest tests/unit/ -v # 73 Python acceptance tests (IC workflow, caller isolation, appeal flow, escrow/staking)
npm test # vitest domain tests
npm run typecheck && npm run build # TypeScript + Vite build
GENVM_VERSION=v0.3.0-rc7 genvm-lint check intelligent-contracts/core/agentcourt_core.py
GENVM_VERSION=v0.3.0-rc7 genvm-lint check intelligent-contracts/registry/resolution_manager.pyimport { getCourt } from './sdk';
const court = getCourt();
const address = await court.connectWallet((window as any).ethereum);
const disputeId = await court.createDispute({ /* respondent, agreementHash, claimType, description, stake, deadline, evidence[] */ });
await court.startInvestigation(disputeId);
await court.requestEvaluation(disputeId); // minutes: LLM + validators
const verdict = await court.waitForVerdict(disputeId);
if (!verdict.reviewRequired) await court.executeSettlement(disputeId);Full method tables, contract API, and error handling: docs/DEVELOPER_GUIDE.md.
AgentCourtCore (Python IC)
├── disputes, evidence, evaluations, verdicts (TreeMap state)
├── request_evaluation → gl.vm.run_nondet
│ ├── gl.nondet.web.get (live HTTP evidence fetch)
│ ├── gl.nondet.exec_prompt × 4 (neutral / claimant / respondent / auditor)
│ └── gl.nondet.exec_prompt (adversarial review)
│ validators re-derive the consensus exactly
│ and independently re-check decisive verdicts
├── finalize_verdict(dispute_id) # NO verdict parameter
└── execute_settlement(dispute_id) # emits to ResolutionManager
ResolutionManager (Python IC)
├── set_core (one-time wiring)
├── open_appeal (core only) / resolve_appeal (owner only — compares core verdict versions)
└── execute_settlement(dispute_id) # reads core.get_verdict() via view()
Appeal path: openAppeal → requestEvaluation (re-run while APPEALED) → finalizeVerdict (superseding version) → resolveAppeal (ResolutionManager owner-only; accepted only when the core verdict version advanced past the version captured at open) → executeSettlement.
Caller isolation invariant: CALLER INPUT ≠ FINAL VERDICT — finalize_verdict, execute_settlement, and resolve_appeal accept only an ID; verdict, confidence, resolution, and appeal outcomes are all derived from stored protocol state. Resolving an appeal is additionally restricted to the ResolutionManager owner, and is accepted only when core's verdict version advanced past the version captured at appeal open — a verdict value that changed without a superseding finalizeVerdict does not count as accepted.
Fail-closed: reviewRequired verdicts, the EVALUATION_FAILED / INCONCLUSIVE / DISPUTED states, and any unresolved appeal refuse settlement (freeze), never auto-release.
Details: ARCHITECTURE.md · docs/PROTOCOL.md
agentcourt/
├── intelligent-contracts/
│ ├── core/agentcourt_core.py # disputes, evidence, nondet evaluation, verdicts
│ └── registry/resolution_manager.py # settlement + appeals (reads core verdict)
├── frontend/src/
│ ├── sdk/ # genlayer-js client + typed AgentCourt SDK
│ ├── components/ # dashboard, detail, forms, explorers
│ └── dispute|evidence|verdict|settlement/
├── tests/
│ ├── unit/test_genlayer_workflow.py # 60 acceptance tests
│ ├── genlayer_stub.py # CPython stub of the genlayer module
│ └── sdk.agentcourt.test.ts # vitest domain tests
├── docs/ # PROTOCOL, DEVELOPER_GUIDE, OPERATIONS, …
├── scripts/deploy/ # deploy + wire helper
├── scripts/verification/ # on-chain smoke checks
├── ARCHITECTURE.md
├── AUDIT.md # Portal rejection remediation notes
├── SECURITY.md
└── AGENTS.md # full protocol spec
| Doc | What it covers |
|---|---|
ARCHITECTURE.md |
Two-IC design, lifecycle, caller isolation, fail-closed states |
docs/PROTOCOL.md |
Evidence model, consensus rules, verdict classes, ADRs |
docs/DEVELOPER_GUIDE.md |
SDK methods, contract API, integration examples |
docs/OPERATIONS.md |
Deployment, addresses, monitoring, troubleshooting |
docs/PRODUCT.md |
Use cases, FAQ, roadmap |
AUDIT.md |
Why the first submission was rejected and exactly what changed |
SECURITY.md · docs/SECURITY_PRIVACY.md |
Threat model, prompt-injection posture, privacy |
CONTRIBUTING.md |
Setup, style, PR process |
AGENTS.md |
Complete protocol specification |
- Live data: evidence URLs are fetched inside the nondeterministic block via
gl.nondet.web.get; all reads/writes go to GenLayer Studionet throughgenlayer-js. - Tests: 73 Python acceptance tests (caller isolation, fail-closed paths, consensus classification, appeal flow + resolve authorization + settlement gate, timestamps, Keccak commitments) + 28 vitest tests.
- CI:
genvm-lint checkon both ICs,pytest, frontend build (.github/workflows/ci.yml). - Audit trail:
AUDIT.mddocuments the verbatim GenLayer Portal rejection, every defect, and the remediation.
AgentCourt is an experimental protocol. Contracts are deployed on GenLayer Studionet (test infrastructure) — do not treat them as production arbitration. Staked bonds are held in on-chain escrow by AgentCourtCore.deposit_stake and released per the final verdict at execute_settlement (see Staking escrow and AUDIT.md).
This software is not legal advice and is not a substitute for jurisdiction-specific arbitration procedures.
MIT — see LICENSE.






