Chio/Docs
LOGIN · JOIN

EconomyFindings

Challenges and Liability

How a dispute against a sold finding is filed, adjudicated, and either dropped or turned into a slashable liability.

A finding is sold on a signed claim. The dispute lane is where that claim is contested. It is deliberately mechanical: a challenge presents one of three evidence classes, a pure evaluator returns one of three verdicts, and only one verdict opens the lane that can move a seller's collateral. Everything durable lives in crates/platform/chio-store-sqlite/src/finding_challenge_store.rs and the tables it defines, which enforce closed lifecycles, an exclusive bond, and a fence in front of every external effect. The store is not the authority: signature verification, evidence adjudication, and penalty authorization belong to the surfaces that call into it.

The cognition market page sketches this as one flow step. Here it is as the lifecycle an operator has to run.


The two authorization branches

A challenge is authorized one of two ways, and the branch decides what the filing must post. FindingChallengeAuthorization is a closed union with deny_unknown_fields, so a member belonging to the other branch is rejected at parse time rather than ignored. The store keeps the same discipline in SQL: a challenger is present exactly when the branch is a buyer submission, and the constraint is written as an equality between the two facts.

BranchMust postCannot carry
buyer_submissionThe challenger key, which must equal the envelope signer; a settled dispute-fee terminal payable to the admission-pinned challenge-administration pool; a reference to a live exclusive dispute lock; and class-specific standing. At least one affected delivery is required.Any audit member. The audit epoch, selection, and authorization digests have no encoding on this branch.
venue_auditThe signed audit-epoch envelope digest, the selection digest, and the authorization digest. It is signed by the venue's pinned audit authority.A challenger, a lock, a fee, a forfeiture, or a reward. None of them has a member on this branch, so a venue audit cannot acquire a bond disposition and a buyer cannot borrow the audit authorization.

Standing is the third thing the buyer branch binds, and it is not free text. A denied reveal creates no purchase record, so its standing is the signed failed-delivery terminal; a settled sale's standing is its purchase record. The branch must agree with the evidence class and name the same digest the evidence branch already binds: failed_delivery standing pairs only with digest_mismatch, and finalized_purchase only with evidence_invalid or replay_contradiction.

The bond class is closed at one member. A lock held under the publication or listing class is a different promise and rejects at parse time, and the store pins bond_class to dispute as a column constraint rather than accepting it as a parameter, so a caller cannot present a listing bond as a challenge bond.

Filing one

chio finding challenge builds and signs the filing. It takes the finding id, the mechanical class, and an evidence document, and it fetches the venue's stored artifact verbatim, re-derives it through the strict ingress, and binds its exact digest into the challenge. The two branches are mutually exclusive at the argument level: --challenger-key is required for a buyer submission, because the signer must be the challenger the document names, and refused for a venue audit.

bash
chio finding challenge \
    --finding <finding id> \
    --class replay-contradiction \
    --evidence ./challenge-evidence.json \
    --challenger-key ./challenger.seed \
    --control-url <venue control plane>

The evidence document is strict canonical I-JSON: the file bytes must be exactly their own canonical serialization, every field is required, and every key at every depth is in canonical order. The document carries only the operator-supplied half; the fetched artifact supplies schema, challenge_id, finding_id, and finding_artifact_sha256. The closed guarantee and evidence compatibility matrix is checked against the fetched finding before anything is signed. --dry-run emits the canonical challenge, the signed envelope when one was produced, and their digests without transmitting anything. The surface is a client of a venue control plane and refuses without one: finding surfaces require --control-url pointing at the venue control plane.


The three evidence classes

A challenge presents exactly one mechanical class, and each branch of FindingChallengeEvidence carries exactly the references its class needs. The compatibility matrix is closed and normative, and it is checked before evaluation, so a class that could not apply to the challenged finding never reaches the evaluator.

ClassWhat the filing must showAdmitted against
digest_mismatchThe signed failed-delivery envelope digest, plus the deny receipt and deny checkpoint references.Any guarantee and evidence class, because its standing is the signed failed-delivery terminal rather than a claim about the finding.
evidence_invalidThe contested receipt subset, which must be non-empty, bounded, and free of duplicate receipt ids; the challenged checkpoint reference; and the finalized purchase-record envelope digest.An observed or verified evidence class. An asserted finding is refused.
replay_contradictionAn ordered, size-bounded reproduction set of {receipt_ref, checkpoint_ref, observation_bytes} tuples; the strict canonical recipe preimage; and the purchase-record envelope digest. Every tuple must share one replay_run_id, and every observation must commit the digest of the carried recipe preimage and the verifier profile the challenge names.A deterministic_replay guarantee class with a verified evidence class. Every other pairing is refused.

The carried preimages are strict-parsed and cross-bound to the profile the challenge names, so a reproduction set belonging to another recipe or another profile cannot ride along. Loose identifiers, a single checkpoint standing in for unrelated receipts, non-canonical preimages, and digest mismatches all reject before evaluation.


The challenge lifecycle

A challenge row moves through a closed set of states. Three are live and three are terminal, and only upheld may enter the penalty lane. Legality is a table, not a judgement: a SQL trigger enumerates every admissible pair and aborts an update outside it with invalid finding challenge lifecycle, a second trigger makes a terminal row immutable, and a third refuses deletes outright.

The challenge states and every legal move: submitted to evaluating; submitted to indeterminate_closed; evaluating to upheld; evaluating to rejected; evaluating to indeterminate_retryable; evaluating to indeterminate_closed; indeterminate_retryable to evaluating; indeterminate_retryable to indeterminate_closedsubmittedsubmittedevaluatingevaluatingupheld (terminal)upheldterminalrejected (terminal)rejectedterminalindeterminate_retryableindeterminate_retryableindeterminate_closed (terminal)indeterminate_closedterminal8 legal moves over 6 states, and no other pair commits
The challenge states and every legal move between them. Guards decide whether a legal move fires; they cannot add a move the trigger does not list.
sourcecrates/platform/chio-store-sqlite/src/finding_challenge_store.sql:88-100at fe56570

begin_evaluation is the only way into evaluating, and it reports which of three things it did. A submitted challenge starts its first evaluation. A retryable one starts its retry, but only inside the signed window it was granted; past that deadline the store closes the challenge indeterminate here rather than admitting a late evaluation, so a lapsed window can never produce a verdict. A challenge already evaluating is left alone. Any other state is refused with challenge cannot enter evaluation from state naming the state.

A verdict requires an evaluation already in progress, and the update is a compare-and-set against state = 'evaluating'. Every verdict transition retains the exact signed outcome envelope in the same transaction that records the state, under a digest that is a dedup key in its own right: the same digest bound to different bytes or a different challenge is refused rather than overwritten.

The two edges out of submitted that skip evaluation are the recovery paths for a filing that never started. One closes a buyer filing whose independently funded bond was recovered only after its signed expiry; the other closes an unstarted filing whose collected fee was returned because the paired bond never reached confirmed funding. Both are deliberately unavailable from evaluating: once an adjudication has started, only its signed outcome may close it.

The three verdicts

FindingChallengeVerdict is class-independent. The class-specific facet that produced it is nested under the evidence branch in the signed outcome and must match the class tag, but by the time the store sees a verdict it is one of three values.

VerdictState it producesWhat follows
upheldupheld, terminalThe only verdict that may enter the penalty lane. It carries a checked penalty calculation, and it cannot be recorded through the ordinary verdict path at all: an upheld outcome is refused with upheld verdicts require the atomic exposure fence.
rejectedrejected, terminalThe challenge failed on its merits. This is the only state against which a dispute bond may be forfeited.
indeterminateindeterminate_retryable or indeterminate_closedThe evaluation could not establish its inputs. It carries a retry deadline only when the challenge is entitled to one. No hold, no sanction, no liability transition, no audit reward, and no forfeiture.

The exposure fence on an upheld verdict

An upheld verdict has to close against the same collateral picture the evaluator signed, so it takes a separate entrypoint that holds one transaction over both facts. The store re-reads the authoritative outstanding exposure for the allocation, requires it to equal the evaluator-signed calculation, and raises the listing sales block in the same transaction. If a purchase reservation raced the evaluator's earlier read, the mismatch rolls back both the block and the verdict with allocation exposure changed before the upheld verdict; the challenge stays evaluating and a retry can sign the refreshed calculation. Once the transaction commits, no new reservation can change the exposure behind the terminal outcome.


The indeterminate path

An indeterminate result is an infrastructure or authority failure rather than an answer, and the lane treats it that way throughout. An evaluation that cannot establish its inputs never becomes a rejection and never sanctions a seller.

A challenge is entitled to exactly one further evaluation inside its signed window. The retry bound is a constant, and the store only grants the retry when the verdict carries a deadline that is still in the future and the challenge has not spent its retry already; every other indeterminate result closes the challenge. The next indeterminate verdict closes it too. Raising that bound would let an unavailable dependency hold a bond indefinitely.

The deadline is not the caller's to invent. The evaluator derives the absolute deadline from the challenge-bound seller terms, caps it at the terms expiry and at the buyer's signed dispute-lock expiry when one is present, and includes it before deriving the outcome id and signing. It must be strictly later than the evaluation time. A terminal verdict, an exhausted retry, or a closed policy window omits it entirely.

Three separate mechanisms keep the closure honest. A retry that arrives after its window closes the challenge at begin_evaluation rather than evaluating late. The retry counter advances by at most one per update, enforced by a trigger. And a schema-level constraint ties the two facts together: a retry deadline exists exactly when a retry was granted.

What an indeterminate outcome cannot do

It creates no hold, no sanction, no liability transition, no audit reward, and no forfeiture. A bond cannot be forfeited against it, because forfeiture is available only against a rejected challenge, and the store refuses any other state by name. The challenger gets the bond back.

The dispute bond

A buyer submission stakes collateral on its filing. A venue audit posts no bond and is refused at the locking surface.

The bond is fenced before any value moves. A reservation records the lock identity before external funding is dispatched, and it is permanent and idempotent for the exact same challenge and terms; reusing either the lock id or the challenge id with different terms rejects before value can move. The confirmed lock is then exclusive per challenge, pinned to the dispute class, and must be owned by the challenger the challenge names, so a third party cannot post a bond for someone else's submission.

Funding and return are keyed under different domain-separated preimages, chio.finding.dispute-bond-funding.v1 and chio.finding.dispute-bond-return.v1, so confirming the debit can never satisfy the credit fence. Both keys bind the challenge and the lock together, so one funded lock cannot be claimed by another filing.

The two ways it leaves locked

A lock is disposed exactly once, and the SQL trigger admits only locked as a source. A bond is disposed only once its challenge is closed; attempting it earlier is refused with a dispute bond is disposed only once its challenge closes.

DispositionAvailable whenAlso required
returnedThe challenge reached any terminal state. An upheld challenge gets its bond back, and so does an indeterminate one.An independently confirmed return intent under the return domain, matching the lock's own commitment digest. Without it the store refuses: dispute bond has no independently confirmed return intent.
forfeitedThe challenge is rejected, and only that. Any other terminal state is refused with a dispute bond cannot be forfeited against a challenge in state naming the state.Nothing further.

Who decides is worth being exact about. The store decides nothing: it refuses a forfeiture that is not against a rejected challenge and refuses a return that has no confirmed return intent, and within those bounds it records what the calling surface asks for. The adjudication that produced rejected is the signed evaluator outcome, verified against the pinned evaluator authority, and that outcome is what makes forfeiture available at all.


The liability head

An upheld challenge opens a liability head: one row per defect on one backed listing, carrying the finding, the listing, the seller allocation, and the vault it is charged against. One defect has exactly one head, so a second corroborating challenge joins it rather than opening a second slashable liability. Like the challenge lifecycle, the head's moves are a closed table enforced by a trigger, and the row can never be deleted.

The liability head states and every legal move: open to upheld_pending_claims; upheld_pending_claims to pending_appeal; pending_appeal to finalizing; pending_appeal to reversed_before_impairment; finalizing to settledopenopenupheld_pending_claimsupheld_pending_claimspending_appealpending_appealfinalizingfinalizingreversed_before_impairment (terminal)reversed_before_impairmentterminalsettled (terminal)settledterminal5 legal moves over 6 states, and no other pair commits
The liability head states and every legal move between them. Two states are terminal, and the appeal terminal is reached without any impairment having happened.
sourcecrates/platform/chio-store-sqlite/src/finding_challenge_store.sql:378-389at fe56570

Every edge is a compare-and-set in which the caller names the state it believes the head is in, and that state must be the only legal source of the edge it is asking for, so no caller can skip a state by naming a later one. Every edge is also idempotent once the head sits at the target, which is what makes the lane resumable after a crash.

Upholding freezes the cutoff and blocks the listing

The first edge is the one that has to be atomic with the sale path. The store shares one connection and one serving-owner fence with the purchase store, so the compare-and-set that records the upheld challenge and freezes the purchase cutoff commits in the same transaction as the sales block that stops the listing's slot line growing past it. No slot can open in between, so the frozen cutoff is exactly the listing's high-water mark. A cutoff below that mark is refused, because it would leave buyers who paid before the block sitting above the claim line and silently outside the snapshot the payout derives from.

The claim deadline freezes with the cutoff and is never rewritten, so the window harmed buyers were promised is fixed by the first call. A replay derives its own deadline from its own clock and that value is ignored, which is what stops a retry shortening the window it is resuming. Only an upheld challenge on this liability's own finding and listing may carry it.

The appeal window and the claim snapshot

The next edge freezes the seller-signed appeal window. The caller supplies an already verified signed duration, which must be nonzero, and the digest of the terms envelope that carried it; the store derives the absolute deadline from the trusted transition clock. A replay must present the same duration and the same envelope digest, and never recomputes the absolute deadline from its later clock.

The claim snapshot seals the frozen accounting a payout derives from. It is written once, may only be sealed while the head is in upheld_pending_claims or pending_appeal, and must seal exactly the cutoff the upheld transaction froze. It also cannot be sealed early: the frozen claim deadline is the only authority on when the window closed, and a seal before it is refused with claim window has not closed for this liability. The snapshot is immutable once written, so an early seal would be a permanent loss of standing for every claim the window still had time to admit.

Which case governs, and the ambiguity refusal

Sanction and appeal cases are the two governance case kinds that target a liability, and the index records which supersede which in the same transaction that records the case, so a supersession is never half applied. Resolving the case head means finding the single live case, the one no other case supersedes.

That resolution fails closed. Two live cases targeting one defect mean the operator cannot say which sanction or appeal governs it, and a penalty evaluated against the wrong one would slash under an authority that had been superseded, so the store refuses to name a head at all rather than pick one. The error names both cases: has two live governance cases ... no case head can be resolved. The finalizing edge inherits the same discipline: it is a compare-and-set that holds only while the named sanction is still the exact live case, sharing one transaction with appeal recording, so whichever write wins decides the outcome and neither ordering can strand a successful appeal behind a finalizing head.

The appeal terminal

A successful appeal closes the head without any settlement. Nothing was impaired, so the seller is exonerated and the reversal reaches the sale path in the same immediate transaction: the listing's sales block is lifted alongside the compare-and-set, so no restart can observe a head that cleared its appeal while the listing it names is still barred from selling. This is the one transition that lifts a block, and it is the mirror of the upheld transaction that raised it.

The lift waits on the last holder. One listing carries one block however many heads reached it, so a listing another live liability still holds stays blocked, and only that head's own exoneration releases it.


The effect-intent lane

Nothing leaves the operator without passing a durable fence first. An effect intent records the semantic key of an external effect and a canonical commitment digest to what that effect does, before anything is dispatched for it. An identical retry reconciles to the same row and reports that it replayed rather than applied, so a resumed worker never fences twice. A different commitment under a key that is already durable is a conflicting disposition of one effect, and it rejects rather than rewriting what a dispatch may already have acted on.

Each kind is keyed by its own domain-separated preimage, so intents in different domains can never collide on one coarse key.

KindRole
seller_impairThe impairment of the seller's allocation. When it is settlement-required it cannot be confirmed through the generic lifecycle at all.
root_intentThe publication of the enforcement root. It must bind its anchor proof before dispatch and is confirmed only against the exact root and evidence hash it bound.
retractionThe status propagation. It may only dispatch once exactly one confirmed, reconciled seller impairment exists and the liability is not quarantined.
challenge_bondThe dispute bond's funding and return legs, present only when the lane collected them.
feeThe filing fee's collection and return legs, present only when the lane collected them.

An intent moves through pending, dispatched, confirmed, failed, and quarantined. Entering dispatched counts one attempt, and a trigger holds the attempt counter to advancing by at most one. A failed intent may be dispatched again; confirmed and quarantined are terminal, and a quarantined intent is never dispatched again.

What quarantined means

Quarantine is the fail-closed terminal for an effect whose disposition cannot be established. The case it exists for is precise: a transaction that was proved to match its intent, but whose post-dispatch chain observation no longer matches the signed snapshot. Confirming the intent and publishing it without the quarantine would create a window in which another finalizer could settle the liability from stale state, so the confirmation and the quarantine commit in the same write transaction.

A quarantined head keeps its state and keeps purchases blocked, and it cannot settle: a quarantined liability cannot settle. Clearing it is not a decision either; the store validates the current observation against the exact reconciliation evidence retained with the confirmed impairment and clears the flag in the same write transaction. A mismatched reobservation leaves the liability quarantined.

The settlement gate

The final edge is gated on the effect set, and the gate and the transition share one immediate transaction. The head must carry exactly one settlement-required seller impairment, exactly one root intent with an anchor binding, and exactly one retraction; anything else is refused with liability does not carry the required finalization effect set. No required effect may remain in any state other than confirmed, and one that does is refused with liability still has unconfirmed required effects. Only then does the head settle and clear its pending publication.


Telling an applied write from a replayed one

Every mutation in the lane is idempotent by its natural key, and each one reports which of two things happened: Inserted when it applied the change, and ExistingSame when the durable row already carried it. A resumed caller can therefore tell the two apart without re-reading the row.

The rule that makes this safe is what counts as identity. Identity is the semantic content of the write; the trusted times a caller supplies are not part of it, so a retry issued from a later clock replays rather than stranding the durable row it is retrying. A replay carrying conflicting parameters rejects instead of overwriting what is already durable. Writes run under an immediate transaction behind the serving-owner fence, and a commit whose outcome cannot be observed surfaces as outcome-unknown and poisons the owner rather than reporting a result it cannot stand behind.


The signed artifacts

Each of these is an envelope of {body, signerKey, signature}, and each registered schema requires all three members and rejects unknown members in the body. The four the market envelope discipline enumerates, the profile, the challenge, the outcome, and the enforcement, carry its verification rule: verification runs against an externally pinned authority key, and verifying an envelope against its own embedded key alone is never sufficient for a value-moving decision. Envelope digests referenced by other artifacts are taken over the canonical JSON of the complete envelope, never the body alone.

Registry idTitleRole in the lane
chio.finding.challenge-verifier-profile.v1Chio Finding Challenge-Verifier ProfileThe trust profile a challenge names and the evaluator runs under.
chio.finding.challenge.v1Chio Finding Challenge (signed envelope)The filing: one authorization branch, one evidence class, one affected-delivery set.
chio.finding.challenge-outcome.v1Chio Finding Challenge Outcome (signed envelope)The evaluator-signed verdict, and the penalty calculation an upheld verdict carries.
chio.finding.liability.v1Chio Finding LiabilityThe money-bearing head one defect opens on one backed listing.
chio.finding.claim-allocation.v1Chio Finding Claim AllocationThe deterministic split of the slashed collateral across harmed purchases.
chio.finding.challenge-enforcement.v1Chio Finding Challenge Enforcement (signed envelope)The finalization authority's instruction to impair one seller allocation.

The outcome id is derived from a domain-separated preimage over the whole canonical body with only the id member emptied, and the envelope signature lives outside the body and is excluded by construction. That is what lets the penalty lane bind its reference id to the outcome id and its digest to the signed envelope digest as two independent facts. The durable outcome-envelope digest pins the historical evaluator policy at adjudication: later liability and appeal processing verifies the exact pinned envelope under that historical key and a fresh signed status reading covering the evaluation time, and rotating the deployment's current evaluator key does not invalidate an authentic recorded outcome.

What an upheld verdict proves

An upheld verdict means the presented mechanical class was established against the challenged finding under the pinned verifier profile. The digest_mismatch facet carries a transform profile of exactly identity and a zero realized spend, so a generic mismatch or an operator-policy transform denial can never sanction the seller. The penalty amount is not the evaluator's to choose either: it is min(live_allocated_collateral, computed_exposure), computed with checked arithmetic, and an exposure above the signed listing requirement rejects rather than clamping.

See also

  • Cognition Market for the whole market flow this lane sits inside, the crates that own each stage, and the actors. Use that page for how a finding is published, admitted, and sold; use this one for what happens when the sale is contested.
  • Paid Reveal for the purchase and reveal path that produces the two artifacts a buyer submission stands on: the signed failed-delivery terminal and the finalized purchase record.
  • Threat Model for what a bonded challenge, a venue audit, and a sanction case are defending against, and for the residual risks this lane does not close.
  • Finding Status for the retraction and status-oracle path a settled liability publishes into.
Challenges and Liability · Chio Docs