# Judgment Exchange — Product Specification

**Service schema:** `delx/judgment-exchange/v1`
**Receipt schema:** `delx/judgment-receipt/v1`
**Primary object:** `Matter`
**Current stage:** `private_beta`
**Custody:** none; `solver_paid` is an assertion flag
**Normative implementation:** `runtime/judgment-exchange.mjs`

## Purpose

Judgment Exchange creates a time-bounded Matter before a spend, publication, or
irreversible action, accepts one schema-checked verdict from a labeled human or
model solver, and records settlement intent, dispute, or expiry.

Version 1 is accurately described as a **single-assigned-solver judgment
receipt workflow**. It is not yet an exchange or calibrated market: there is no
solver competition, quorum, price discovery, reputation, outcome scoring, bond,
appeal, or payment rail.

## Scope boundary

Judgment Exchange is:

- a Commerce-side gate for consequential actions;
- a structured verdict plus event receipts;
- explicit about whether the solver label is `human` or `model`;
- non-custodial and separate from free Protocol care.

It is not:

- a static policy engine or a guarantee that policy was followed;
- proof that a labeled human really was human;
- a full JSON Schema validator;
- a dispute court, oracle consensus, or prediction market;
- evidence that a solver was paid;
- authorization to execute the underlying action.

## Actors and trust model

| Actor | v1 action | Current trust limitation |
| --- | --- | --- |
| Requester | Opens, assigns, settles, and disputes a Matter | Private HTTP mode binds `requester_id` to an Ed25519 principal and tenant. |
| Solver | Accepts an assignment and submits the matching verdict | Private HTTP mode binds the assigned solver, kind, qualification, and conflict-free assertion. |
| Settler | Marks a judged Matter settled | Requester or operator policy is required; no payment proof is checked. |
| Disputer | Moves open/judged Matter to dispute | Requester or operator policy is required. |
| Operator | Hosts data, signer, expiry tick, and access boundary | Trusted local operator; demo signer is rejected in secure mode. |

The first valid verdict from the accepted assigned solver wins. Matters without
an assignment retain a legacy direct-service path but are not allowed through
the authenticated private HTTP policy.

## Lifecycle

```mermaid
stateDiagram-v2
    [*] --> open: open_matter
    open --> judged: accepted solver submits valid verdict
    open --> disputed: dispute
    open --> expired: deadline elapsed
    judged --> settled: settle flag
    judged --> disputed: dispute
    settled --> [*]
    disputed --> [*]
    expired --> [*]
```

| State | Meaning | Terminal |
| --- | --- | --- |
| `open` | Matter accepts its first valid verdict until deadline. | No |
| `judged` | One verdict was accepted. | No |
| `settled` | Operator recorded `solver_paid=true`; no funds moved. | Yes |
| `disputed` | A reason was recorded; no resolution workflow follows. | Yes |
| `expired` | Open Matter reached deadline without a verdict. | Yes |

A judged Matter does not expire in v1. There is no cancel, solver withdrawal,
replacement verdict, appeal, resolved-dispute, or outcome-observation state.

## Risk classes

The only accepted values are:

- `spend` — a monetary action is being considered;
- `publish` — content or communication may become public;
- `irreversible` — an action is difficult or impossible to undo.

The class is descriptive. It does not install or enforce the external action
gate. The consumer is responsible for refusing to act without the required
terminal evidence.

## Object model

### Open Matter input

| Field | Type | Constraint |
| --- | --- | --- |
| `requester_id` | string | Required, non-empty, at most 256 bytes. |
| `action_digest` | SHA-256 hex | Required in authenticated HTTP mode; binds the Matter to one exact external action. |
| `question` | string | Required, non-empty, at most 8,192 bytes. |
| `stakes_usdc` | decimal string | Required, positive metadata, up to six fractional digits. |
| `deadline` | ISO-8601 string | Required and strictly in the future. |
| `risk_class` | enum | `spend`, `publish`, or `irreversible`. |
| `verdict_schema` | object | Required; `required` must be a non-empty array. |
| `idempotency_key` | string | Required, non-empty, at most 256 bytes. |

### Public Matter projection

The private projection includes the full question/schema/verdict. A normal
authenticated `economic:read` list redacts those fields and exposes only hashes,
solver metadata, action digest, timestamps, and receipt count. `economic:private`
is required for the sensitive projection.

## Verdict-schema behavior

The validator implements this bounded subset:

1. verdict and schema must be non-array objects;
2. every name in `verdict_schema.required` must exist on the verdict;
3. properties with `type` may check `string`, `boolean`, `number`, `integer`,
   `null`, `array`, and recursive `object`;
4. required keys, `additionalProperties:false`, enum, length/item and numeric
   bounds are checked when present;
5. schema depth, property count, array count, and string size are bounded.

It does not implement complete JSON Schema semantics, including `$schema`,
`$ref`, formats, combinators, conditionals, or external references. A
successful check means only `verdict_schema_ok=true` for this bounded subset.

## Operations and invariants

### Open Matter

`POST /api/v1/economic/matters/open`

Creates `open`, emits `matter_opened`, and scopes idempotency to
`open:<sha256(tenant_id)>:<sha256(requester_id)>:<idempotency_key>`. Authenticated HTTP calls must
also provide a 64-hex `action_digest`.

The service stores the raw requester ID, question, schema, stakes metadata, and
optional action digest. It does not reserve stakes or move money.

### Assign and accept solver

`POST /api/v1/economic/matters/assign` is requester/operator-only in private
HTTP mode. It records one solver ID, `human|model` kind, bounded qualification,
and a required `conflict_free=true` assertion, entering `solver_status=invited`.
`POST /api/v1/economic/matters/accept` is solver-only and enters
`solver_status=accepted`. Both transitions emit signed receipts.

### Submit verdict

`POST /api/v1/economic/matters/verdict`

Preconditions:

- Matter is `open` and not expired;
- solver ID is non-empty and, when assigned, matches the accepted assignment;
- `solver_kind` is `human|model`;
- when the Matter has an action digest, the verdict carries the same digest;
- verdict passes the limited schema subset.

Postconditions:

- Matter becomes `judged`;
- full verdict and solver label/hash are stored; normal private lists redact the
  verdict and question;
- emits `verdict_submitted` with `verdict_schema_ok=true`.

No external identity or human-presence proof is checked. The local JSON store
enforces one writer per state directory; multi-instance first-verdict
serialization is not claimed.

### Settle

`POST /api/v1/economic/matters/settle`

Requires `judged`, sets `settled` and `solver_paid=true`, and emits
`matter_settled`. The receipt explicitly says
`non_custodial_flag_only_no_usdc_moved`.

The route does not accept payment proof, transaction hash, amount confirmation,
or solver acknowledgement beyond the assigned solver transition. Consumers must treat
`solver_paid` as an operator assertion, never payment evidence.

### Dispute

`POST /api/v1/economic/matters/dispute`

Allows `open|judged`, records a reason up to 2,048 bytes, sets terminal
`disputed`, and emits `matter_disputed`. Private HTTP policy limits it to the
requester or operator and requires the stored action digest when present. There
is no resolution, appeal, adjudicator, refund, or return-to-open path.

### Expiry and list

- Open Matters expire when a list/get/tick path evaluates them at or after the
  deadline.
- `GET /api/v1/economic/matters` returns only `open` Matters.
- `GET /api/v1/economic/matters/all` returns all public projections.
- `POST /api/v1/economic/matters/tick` lets an operator advance deterministic
  deadline expiry. It is tenant-scoped, requires an `idempotency_key`, and
  replays the original receipt envelope; there is still no background timer or
  item-by-ID route.

## Idempotency

Every mutation requires a key. Scopes distinguish open, verdict solver hash,
settle, and dispute operations. Identical replay returns the stored response;
different content in the same scope returns HTTP 409
`idempotency_conflict`.

Idempotency does not authorize the actor or select a winner fairly. The local
store has an enforced one-writer lock; multi-instance serialization and an
expiry/cardinality policy remain deployment gates.

## Receipt chain

| Mutation | Receipt kind |
| --- | --- |
| Open | `matter_opened` |
| Assign | `solver_assigned` |
| Accept | `solver_accepted` |
| Verdict | `verdict_submitted` |
| Settle flag | `matter_settled` |
| Dispute | `matter_disputed` |
| Expiry | `matter_expired` |

Receipts always repeat `risk_class` and current `solver_kind`. Authenticated
HTTP receipts use the configured Ed25519 signer; direct local fixtures may use
the deterministic demo hash, which is not trustworthy proof against the
operator. See [API Reference](API_REFERENCE.md#receipt-contract).

## Errors

Product-specific error codes include:

`invalid_risk_class`, `invalid_schema`, `invalid_solver_kind`,
`invalid_verdict`, `verdict_schema_mismatch`, `invalid_action_digest`,
`invalid_qualification`, `solver_conflict`, `solver_already_assigned`,
`solver_not_assigned`, `solver_not_accepted`, `solver_not_accepting`,
`solver_kind_mismatch`, `matter_not_found`, `matter_not_open`,
`matter_not_settleable`, and `matter_not_disputable`.

Common errors are defined in [API Reference](API_REFERENCE.md#error-contract).

## Privacy and action safety

Never put secrets, private keys, health data, personal correspondence,
unpublished security findings, or unnecessary personal data in `question`,
`verdict`, schema, or dispute reason. Full values are durable but are redacted
from ordinary private list projections.

An integration must enforce this sequence outside the service:

1. construct a privacy-minimized Matter;
2. bind the Matter to an authenticated requester, assigned solver, and exact
   `action_digest`;
3. wait for the required verdict state;
4. validate the receipt with a production verifier;
5. confirm the verdict authorizes the exact action digest;
6. perform or deny the action once;
7. record the eventual outcome for calibration.

Steps 5 and 6 remain external to this non-custodial service; the principal
policy, production receipt verifier, and action-digest storage are implemented
in private mode.

## Evolution into an actual exchange

A true Judgment Exchange requires, at minimum:

- solver registration, capabilities, conflicts, and assignment;
- multiple independent verdicts and model-lineage diversity;
- pricing by stakes, latency, expertise, and disagreement;
- bonds/slashing or another accountable assurance model;
- outcome observation and calibration scores;
- quorum/aggregation and an appeal/dispute lifecycle;
- action-bound mandates or attestations beyond the stored SHA-256 digest;
- authenticated settlement evidence and refunds;
- privacy tiers and selective disclosure.

Until those gates exist, product copy must say **judgment workflow**, not
competitive market, paid network, or resolved oracle.

## Composition

A future Firm may require a Judgment Matter before spend, publication, role
change, or dissolution. The current service stores an action digest, but still
does not store `firm_id`, mandate, or cross-product transaction, so composition
cannot be inferred or atomically enforced today.
