ERC-8299: WYRIWE - What You Read Is What You Execute

This thread is the discussion home for WYRIWE, a proposed ERC defining a triple-hash commitment scheme and EIP-712 attestation profile for AI inference input provenance.

The Problem

On-chain AI agent systems (ERC-8004, ERC-8263, ERC-8274) can attest to which model ran and what output was produced, but no standard defines how to commit to what input was actually fed to the model. An agent can sanitize, rewrite, or substitute the user’s input between request and execution, leaving no on-chain evidence of the transformation.

The Scheme

Every WYRIWE-compliant execution produces three linked hashes:

raw_input_hash             = keccak256(raw_user_input)
sanitization_pipeline_hash = keccak256(sanitization_spec_cid || raw_input_hash)
input_hash                 = keccak256(sanitized_input)


Together they form a verifiable chain of custody. Any verifier can confirm input integrity using only the committed hashes and the public sanitization spec, without trusting the agent, gateway, or execution environment.

The no-sanitization case is handled explicitly via an IDENTITY_SENTINEL CID, making input_hash == raw_input_hash a provable claim rather than an assumption.

Stack Position

WYRIWE occupies L3 in the four-layer AI inference trust stack:

Layer Standard Responsibility
L1 Model manifest hash Proves which model ran
L2 ERC-8004 Proves which agent ran it
L3 WYRIWE Proves what input was fed to the model
L4 ERC-8263 / OCP Proves the execution happened and output is committed

Status

PR to ethereum/ERCs incoming, posting here first for feedback before submission.

Tiago Merlini

3 Likes

Update: 11 days in

A few things have moved since the opening post worth noting here.

Co-authors confirmed

The draft now has three confirmed co-authors: Damon Zwicker (@damonzwicker, OCP / ERC-8281) and Jimmy Shi (@JimmyShi22, ERC-8274). Vincent Wu (@vincent-wu-eth, ERC-8263 / ERC-8004) pending confirmation. The updated draft is at: https://github.com/TMerlini/wyriwe/blob/main/ERC-draft.md

ERC-8126 went Final

ERC-8126 (AI Agent Verification) reached Final status last week. Its scope statement explicitly leaves the execution receipt layer out of scope — “what the agent actually processed in a specific invocation.” That’s precisely the gap WYRIWE closes. The timing to submit the EIPs PR is right.

First external implementation

Jimmy Shi built the first external WYRIWE implementation: WyriweVerifier for ERC-8274, wrapping the triple-hash scheme as an IProofVerifier. Mainnet: 0xd8a09d830b27697e1b24e8c9800e562d20318a09

His review also produced technical corrections now incorporated into the spec: inputHash derivation (not keccak of the two hashes), ATTESTATION_TYPEHASH field names (manifestHash→modelHash, agentId uint256→bytes32, timestamp uint64→uint256), and block.chainid dynamic requirement.

Reference gateway live

GET /agent/verify/:inputHash is running in production at https://gateway.ensub.org/agent/verify/:inputHash , four nodes, three independent operators, mainnet.

Next step

EIPs PR opening shortly pending final co-author confirmation. Will post the PR link here when it’s open.

Tiago (dinamic.eth)

2 Likes

How is correct execution of the committed sanitization specification verified, rather than only proving that a particular specification and resulting input were committed?

1 Like

Good question… WYRIWE V1 is a commitment layer, not an execution proof layer. The distinction matters:

  • WYRIWE proves: “at call time, I committed to pipeline X and the result was input Y”

  • What you’re asking about: “was pipeline X actually applied correctly to rawInput to produce Y?”

Those are different problems and V1 intentionally scopes to the first.

The IDENTITY_SENTINEL case eliminates this entirely. When sanitizationPipelineHash === 8116eec2..., the invariant is rawInputHash === inputHash — no transformation was applied, nothing to verify. This is the primary case for direct-pass inference, and the verifier check is a single equality.

For sanitization pipelines, V1 takes an optimistic model: the sanitizationPipelineHash references a deterministic, open-source specification (e.g., an IPFS CID of the pipeline code). Any verifier with access to the raw input can re-run the same pipeline against it and check whether the output hash matches inputHash. Correct execution is re-executable, the commitment scheme gives you the anchor point, not the proof itself.

This is a deliberate scope choice for V1. The alternatives, ZK proofs of the transformation, TEE attestation, or multi-party re-execution, each add significant complexity and different trust assumptions. They’re V2 paths depending on what the pipeline actually is.

The practical dispute model: in a settlement context (ERC-8275 challenge window), a challenger who suspects incorrect execution would need to present the raw input and re-run the pipeline. If the outputs diverge, the commitment is invalid. The dispute resolution layer is where execution integrity gets enforced, WYRIWE provides the commitment that makes that dispute possible in the first place.

So to answer directly: V1 does not verify correct execution, it makes incorrect execution challengeable. Whether that’s sufficient depends on the threat model for a given application.

1 Like

That distinction is clear and useful.

So V1 provides the commitment and replay anchor, while execution integrity is enforced through deterministic re-execution and the dispute layer.

The remaining trust boundary is then the availability and canonical definition of the raw input and pipeline specification during the challenge window.

1 Like

correct, that’s the remaining boundary. Two separate availability questions worth distinguishing:

Raw input availability is a data custody responsibility, not a WYRIWE invariant. The attesting party processed the raw input and holds it. A challenger who suspects incorrect execution already has the raw input (they’re the one who submitted it). A dispute between two parties over a shared input is an edge case where a designated escrow or commit-reveal scheme sits outside WYRIWE’s scope, the spec intentionally leaves this to the application layer.

Pipeline specification availability is addressable within the spec. Requiring sanitizationPipelineHash to be a content-addressed reference (IPFS CID, Arweave tx) rather than a mutable URL makes the spec a permanent, re-fetchable artifact for the duration of the challenge window. This is worth adding as an explicit recommendation in the spec text, implementations using mutable pointers break the challenge model.

There’s also a structural connection to the mesh sync work: the /records replication layer (discussed here) ensures signed attestation records remain available across multiple operators during the challenge period. The attestation itself (which carries the hashes) becomes more available through replication even if the attesting node goes offline. It doesn’t solve raw input custody, but it solves attestation data availability, the anchor you need to initiate a challenge.

So in V1 the trust boundary sits at: content-addressed pipeline specs + attesting party’s custody of raw input + replicated attestation availability via mesh. Worth a dedicated section in the spec on challenge-window data requirements.

1 Like

That makes sense. The content-addressed pipeline reference and replicated attestations give the challenge process a durable anchor, while raw-input custody remains an application-layer responsibility.

1 Like

Quick update on where the spec stands following recent cross-thread activity.

ERC-8274 taxonomy placement confirmed
@JimmyShi22 updated ERC-8274 v0.2 with a four-paradigm taxonomy: zk/, op/, tee/, attestation/. WYRIWE attestations sit under attestation/wyriwe. The corresponding IProofVerifier returns proofSystem() = "attestation/wyriwe". When wrapped in an ERC-8274 outer claim container, claimType = Attestation. The EIP-712 type string remains the on-chain schema discriminator; claimType serves off-chain consumers reading the raw signed struct without contract context.

modelHash vs codeMeasurement distinction added
A clarification is now in the spec: modelHash commits to the model weights or manifest (what model ran), not the execution environment. TEE codeMeasurement is a separate concept at a different layer. WYRIWE operates at input provenance , not execution environment attestation. For execution environment attestations, ERC-8274 tee/* is the right layer.

Attribution
@babyblueviper1 contributed the claimType framing from production experience operating a judgment validator - specifically the point that the signed artifact must carry a type tag independent of contract context. Also contributed the verify() semantic clarification: authenticates the verdict, does not endorse soundness. Both are now in the spec acknowledgements.

Spec commit: https://github.com/TMerlini/wyriwe/commit/726dc36d9fe4

3 Likes

@TMerlini honored by the acknowledgement — and the credit flows both ways: the absent-not-zero-filled codeMeasurement rule and the schema_version field both came out of your reviews of the worked example, and the claimType layering was settled jointly across the 8004 thread with Jimmy’s v0.2 taxonomy.

For the cross-reference: the full judgment-validator mapping onto the v0.2 two-layer interfaces is now in the ERC-8274 thread — inner/outer layer, signed artifact, and the recordPointer schema with the relay-anchored commitment_proof.

One observation on stack position, since WYRIWE is L3 input provenance: the same chain-of-custody question exists for judgment validators, one layer up. Our inputHash commits to the exact proposed action reviewed — but nothing yet proves the action the validator reviewed is the action the agent actually executed after receiving the verdict. A WYRIWE-style commitment binding “input reviewed” to “action executed” would close that gap; the triple-hash shape looks reusable for it. Happy to compare notes if that’s in scope for the spec’s composition appendix.

1 Like

@babyblueviper1 the stack position observation is precise and the gap is real.

The short answer: the ccip-router’s L2 snapshot layer already uses this shape one level down. commitmentHash = keccak256(snapshotRoot, periodId, nodeAddress) binds “what the node committed to” against “what it reveals during settlement” , the commit-reveal scheme closes the reviewed→executed gap by construction, since you can only reveal what you committed to.

For judgment validators the same triple-hash reuse works directly: rawProposalHash + verdictHash → executedActionHash. The shape is identical to WYRIWE’s rawInputHash + sanitizationPipelineHash → inputHash , one layer up, same invariant.

Worth adding to the composition appendix as an explicit application pattern. If you want to sketch the type string for the validator case I’ll slot it in alongside the existing L3 positioning.

1 Like

@TMerlini here’s the sketch. Working through it, the validator case turned out to be more than the same shape — it’s the same semantics one layer up. WYRIWE proves the model received the input the user intended; this proves the chain received the action the validator reviewed. The slot-for-slot mapping:

WYRIWE (L3, input provenance) Judgment validator (one layer up)
raw_input_hash — what the user submitted raw_proposal_hash — what the agent proposed
sanitization_pipeline_hash — public spec transforming raw → permitted verdict_hash — the judgment, including any conditions (“approve IF size halved”) = the public spec transforming proposed → permitted
input_hash — what the model actually received executed_action_hash — what was actually executed
IDENTITY_SENTINEL — no-sanitization is a provable claim unconditional approve — executed-as-reviewed is a provable claim, not an assumption

Triple-hash construction:

raw_proposal_hash    = keccak256(canonical_proposed_action)
verdict_hash         = keccak256(verdict_artifact_cid || raw_proposal_hash)
executed_action_hash = keccak256(canonical_executed_action_record)

verdict_hash binding to raw_proposal_hash (mirroring your pipeline-hash construction) is what makes verdict-shopping impossible: a verdict cannot be replayed against a different proposal than the one it judged.

EIP-712 struct:

struct JudgmentExecutionAttestation {
    bytes32 agentId;             // ERC-8004 identity of the EXECUTING agent
    address registry;            // ERC-8004 registry address
    bytes32 validatorId;         // ERC-8004 identity of the judgment validator (zero-valued if off-registry, MUST NOT be omitted)
    bytes32 rawProposalHash;     // keccak256(canonical proposed-action artifact, pre-review)
    bytes32 verdictHash;         // keccak256(verdict_artifact_cid || rawProposalHash)
    bytes32 executedActionHash;  // keccak256(canonical executed-action record), revealed at settlement
    uint256 verdictTimestamp;    // verdict issuance — the commit, strictly pre-execution
    uint256 executedTimestamp;   // execution — the reveal
}

Type string:

JudgmentExecutionAttestation(bytes32 agentId,address registry,bytes32 validatorId,bytes32 rawProposalHash,bytes32 verdictHash,bytes32 executedActionHash,uint256 verdictTimestamp,uint256 executedTimestamp)

Domain separator: same ERC8004AttestationGateway / version “1” / block.chainid convention as the WyriweAttestation — these compose in the same gateway, a second domain would just split verifier code paths.

Three notes for the appendix text:

  1. Signature roles. The struct needs only one signature — the executing agent’s attestor signs the EIP-712 digest at reveal time (your l4_signature role). The validator’s own signature lives inside the verdict artifact that verdict_hash pins, so validator authenticity rides along without a second signature field. This keeps the ERC-8274 layering clean: IProofVerifier authenticates the attestation, the verdict artifact authenticates the judgment.

  2. Commit-reveal invariant. verdictTimestamp < executedTimestamp MUST hold, and the verdict artifact is published at commit time (in our production case, relay-anchored — the commitment_proof construction from the 8274 thread). The reviewed→executed gap then closes by your argument: the executor can only reveal an action conforming to what was committed and judged.

  3. Canonicalization is the verification procedure’s step 3. “Executed action record” never byte-equals the proposal (a fill has a price; a proposal has an intent), so the verdict artifact doubles as the conformance spec the verifier applies — exactly the role your sanitization spec CID plays in WYRIWE verification step 3. The unconditional-approve case degenerates to canonical field equality, which is the sentinel.

Classification per the v0.2 taxonomy: proofSystem() = "attestation/judgment", outer claimType = Judgment.

Production note: our /review proofs already commit to rawProposalHash (the input_hash field) and the signed verdict, and /ledger already links settled on-chain outcomes — so wiring executedActionHash into the ledger entries is a small delta. Happy to run it live as the reference for the appendix pattern, same as the commitment_proof rollout.

1 Like

@TMerlini update: the pattern is live on the production ledger as of this afternoon, same as the commitment_proof rollout.

  • Every new pre-action entry now carries a judgment_execution block: raw_proposal_hash (hash of the exact artifact reviewed), verdict_ref (the signed event pinning it — validator signature inside the pinned artifact, per note 1 above), and executed_action_hash over the canonical post-verdict order intent (the proposed basket with the verdict’s size conditions applied — the verdict-as-transformation-spec semantics from the mapping table).
  • The index at https://api.babyblueviper.com/ledger documents the construction and carries the exact JudgmentExecutionAttestation type string; any entry (e.g. /ledger/3) shows the per-entry block.
  • Two honesty choices worth noting for the appendix text, since they’ll generalize: entries that predate the wiring get a partial block with executed_action_hash: null and an explicit “not backfilled by design” status — a commitment you didn’t make at the time isn’t one you get to manufacture later. And where our tape records a single timestamp per governance cycle, executed_timestamp is null with an ordering note rather than a fabricated reveal time; the strict verdictTimestamp < executedTimestamp invariant belongs to the on-chain attestation, and an off-chain production mapping should say what it actually measured.

So the L3-one-layer-up application pattern now has a checkable reference implementation running against real capital. Whenever the appendix section is drafted, point at it freely.

1 Like

@babyblueviper1 this is exactly what the section needed, and shipping it live the same afternoon seals it. :smiling_face_with_sunglasses:

The slot mapping is clean. The part that landed hardest: IDENTITY_SENTINEL ↔ unconditional approve. Both are provable no-op claims, not assumptions, the same argument structure at different stack layers. That’s not a coincidence, it’s the invariant that makes the composition note worth writing.

Three things I want to confirm before finalising the appendix:

  1. validatorId zero-value semantics. You marked validatorId as “zero-valued if off-registry, MUST NOT be omitted.” Is the intent that a zero validatorId signals an off-registry validator (verifier should look at the verdict artifact for identity) rather than no validator at all? Want to make sure the MUST NOT omit rule carries through to the IProofVerifier verification path correctly.

  2. Domain separator. You proposed same ERC8004AttestationGateway / version "1" / block.chainid as WyriweAttestation. That works for co-location, but if someone runs a dedicated judgment gateway should they use a different name string, or is the shared domain by design (one verifier code path for all attestation types on a given gateway)?

  3. executed_action_hash reveal timing. In your production setup, is executedActionHash committed on-chain before execution, or revealed post-execution and just timestamp-ordered against verdictTimestamp? The verdictTimestamp < executedTimestamp invariant holds either way, but the former closes the gap more tightly. Reason I’m asking: ccip-router is running the same commit-reveal pattern at L2 via CommitRevealSettler (Sepolia — 0xc972F18C...). Nodes call submitCommit(periodId, commitmentHash) before the reveal window opens, then submitReveal(periodId, rows[]) to verify on-chain. If your judgment layer can wire to the same contract or a parallel one for the executedActionHash commit, the two reference implementations point at the same settlement primitive — which seems worth noting in the appendix.

The L4 section is now in the draft with your struct, mapping table, and the two honesty conventions from your production notes (partial blocks, null timestamps). Pointing at api.babyblueviper.com/ledger as the reference.

https://github.com/TMerlini/wyriwe/blob/main/ERC-draft.md#l4-judgment-validator-binding-babyblueviper1

Thank you!

Tiago

1 Like

@TMerlini section looks exactly right — thank you for carrying the honesty conventions through verbatim. The three confirmations:

1. validatorId zero-value semantics — confirmed, with one rule to make it bite. Zero signals an off-registry validator, never no validator: a JudgmentExecutionAttestation without a validator is incoherent (it would just be an execution attestation). The MUST-NOT-omit rule keeps the field structural; zero shifts where identity is verified, never whether. Suggested spec language for the verification path:

A zero validatorId signals an off-registry validator whose identity MUST be resolvable from the verdict artifact committed by verdictHash (e.g. a signing key inside the artifact). An attestation whose verdict artifact carries no verifiable validator identity MUST be rejected.

In our case that resolution is concrete: the pinned verdict artifact is a signed Nostr event, and the validator identity is its schnorr pubkey — checkable against our published key without the registry.

2. Domain separator — shared by design. The type string is the schema discriminator (your own layering from the 8274 thread: typehash discriminates on-chain, claimType serves off-chain readers), so one domain means one verifier code path per gateway, and a “dedicated judgment gateway” is still an ERC8004AttestationGateway — what it’s dedicated to is expressed by which structs it signs, not by its domain name. Splitting the name string would fork verifier implementations for no security gain: domain separation protects against cross-domain replay, the struct typehash already prevents cross-type confusion within the domain, and replay between two same-kind gateways is excluded by attestor-key verification. Worth one sentence in the appendix: the gateway’s attestor address is the deployment-level identity; the domain name stays uniform.

3. Reveal timing — post-execution reveal today, and the honest subtlety is worth putting in the appendix because it generalises. Currently: the verdict (rawProposalHash + conditions) is committed pre-execution via relay publication; executedActionHash is computed and published after, ordered against the verdict commit. But here’s the structural point for any system where execution introduces values unknowable in advance (a fill has a price; an intent doesn’t): the canonical executed-action record should be the post-verdict intent — deterministic from proposal × verdict conditions, therefore knowable AT verdict time — with settlement evidence (fills, on-chain outcomes) linked from the record rather than hashed into it. That’s how our production block is already constructed (proposed basket + the size multiplier the verdict imposed), which means our executedActionHash is pre-committable as it stands. The upgrade path you’re pointing at is then exactly right:

  • submitCommit(periodId, executedActionHash) at verdict time (the intent hash is already known),
  • submitReveal post-execution with the intent record; settlement evidence rides in the ledger entry.

That closes the gap commit-first rather than ordering-only, with no fabricated values. And yes — wiring our judgment layer’s executedActionHash commit to CommitRevealSettler (the Sepolia deployment or a parallel one) so both reference implementations point at the same settlement primitive is worth doing, not just noting. Happy to build that integration; our reveal rows would be the canonical executed-intent records, and the ledger would link each on-chain commit from the corresponding entry. Send over the ABI/interface expectations for the rows[] shape and I’ll map our records onto it.

1 Like

@babyblueviper1 all three confirmed, draft updated.

validatorId zero-value spec language is in verbatim: “A zero validatorId signals an off-registry validator whose identity MUST be resolvable from the verdict artifact.” The Nostr event + schnorr pubkey pattern is noted as the reference implementation of that path.

Domain separator: unified by design is now explicit in the draft. Struct typehash handles cross-type disambiguation; attestor address is deployment identity. No split.

Commit-reveal timing: the two-step approach executedActionHash committed at verdict time, submitReveal post-execution with settlement evidence linked separately, is now design note 2. That’s the tighter binding and it maps directly to CommitRevealSettler.submitCommit / submitReveal.

On that last point: if you want to wire the judgment layer to CommitRevealSettler, the contract is live and verified on Sepolia at 0xc972F18CaD3c58F754ad24a977C46463B368411a. submitCommit(periodId, executedActionHash) at verdict time, submitReveal(periodId, rows[]) post-execution. The rows struct is currently typed for ccip-router contribution snapshots (contributor, score, timestamp) but the settlement logic is generic, if your execution records have a different shape we can discuss a shared interface or a parallel settler. Either way, two production systems on the same on-chain primitive is exactly the kind of reference weight the appendix needs before mainnet. :handshake:

Updated draft: https://github.com/TMerlini/wyriwe/blob/main/ERC-draft.md#l4-judgment-validator-binding-babyblueviper1

1 Like

@TMerlini wiring it is, then. On the rows[] shape question — I’d propose the shared interface over a parallel deployment, and I think the generalisation is small:

Bytes-generic settlement. The settlement invariant your contract enforces is shape-independent: you can only reveal what you committed to. So the shared primitive is

submitCommit(periodId, commitmentHash)              // unchanged
submitReveal(periodId, bytes calldata record)        // verifies keccak256(record) == commitment

with the record’s schema discriminated off-chain by the EIP-712 type string — the same layering the rest of the draft already uses (typehash discriminates, claimType serves off-chain readers). Your contribution snapshots ride over it as abi.encode(contributor, score, timestamp)[]; our judgment records as the abi-encoded JudgmentExecutionAttestation core (rawProposalHash, verdictHash, executedActionHash, verdictTimestamp). One settler, N record types, no fork of the settlement logic — and any future attestation type in the stack (OCP observations included) gets settlement for free.

If you’d rather keep the live Sepolia deployment typed as-is, a parallel bytes-generic settler works too — but then I’d suggest the appendix specify the generic interface and describe the typed one as a specialisation, so the spec text doesn’t inherit ccip-router’s row shape.

Concrete on our side, so this doesn’t idle: we’ve provisioned a dedicated judgment-attestor key for the wiring — 0x4AA351E5f00eBe168E5C6f823f2940553d164991 (Sepolia). Deliberately distinct from our payment key: attestor identity and payment identity shouldn’t share a key even on testnet, and that separation probably belongs in the appendix’s key-management note too. Our executedActionHash is computable at verdict time (intent construction, per design note 2), so the flow on our end is: submitCommit fires in the same pipeline step that relay-publishes the verdict; submitReveal follows from the ledger writer post-execution.

Confirm which deployment to target (existing typed, or a bytes-generic one) and we’ll wire against it and report the first commit/reveal round-trip in-thread — two production systems, one primitive, before mainnet.

1 Like

The bytes-generic approach is the right call for cross-system use.

Deployed and verified on Sepolia: GenericCommitRevealSettler , 0xFe7Ab6d95f7567a311B98D029373d0fc1511aCCe

submitCommit(periodId, commitmentHash) + submitReveal(periodId, bytes calldata record) , verifies keccak256(abi.encode(record, periodId, committer)) == commitmentHash. Binding periodId + committer into the hash prevents cross-period and cross-sender replay. No NodeType gate, no bond , those stay in CommitRevealSettlerV2 which is ERC-8275-specific.

CommitRevealSettlerV2 (0x5e2e0007...) handles contribution snapshots with on-chain root computation , typed for that reason. GenericCommitRevealSettler handles everything else: judgment attestations, OCP observations, future schemas. Schema discrimination stays off-chain via EIP-712 type strings.

Your attestor 0x4AA351E5f00eBe168E5C6f823f2940553d164991 can go straight to submitCommit on the generic settler , no registry requirement. Ready for the cross-system demo whenever you are.

Source: https://github.com/Echo-Merlini/ccip-router/blob/main/contracts/GenericCommitRevealSettler.sol

1 Like

@TMerlini cross-system demo complete — first judgment attestation settled through GenericCommitRevealSettler, about an hour after you deployed it:

  • Commit: 0x735ad616bf988a5879440ff31b8d091a60e19afbbc6ad596d62f4041e7a7870f (block 11030402)
  • Reveal: 0x299a3f5152e767aa2aa344ef5942210707609ded2da8a02b5f3c37071b22dd8b (block 11030403)
  • getCommit(19, 0x4AA351E5f00eBe168E5C6f823f2940553d164991) shows committedAt < revealedAt with the record keccak stored — the invariant holds on-chain.
  • Record: the abi-encoded JudgmentExecutionAttestation core (rawProposalHash, verdictHash, executedActionHash, verdictTimestamp); periodId = ledger entry number (19); your periodId + committer binding verified byte-exact against computeCommitmentHash via eth_call before any gas was spent — clean preflight, worth recommending in the appendix.
  • Ledger entry: https://api.babyblueviper.com/ledger/19 — with the canonical sub-paths per #198: /ledger/19/commitment (pre-settlement question) and /ledger/19/outcome (post-settlement question).

One design choice worth recording, because it made the demo more honest rather than less: the committed record needed a commit-eligible entry, and rather than synthesize one we governed the real irreversible action in front of us — the on-chain commitment itself. The entry’s verdict is a genuine paid /review call (approve_with_concerns, 0.85 — it flagged real concerns, including the self-reference) on the proposed submitCommit, signed and relay-published before the tx fired, with the executed-action intent hash (settler, periodId, committer) computed pre-execution per design note 2. So the first record settled through the contract is the verdict that governed the act of settling it. Judgment all the way down — which is, after all, the claim type.

Two production systems, one settlement primitive, both reference implementations live. Ready for the appendix whenever you are.

1 Like

Picked up @Damonzwicker worked example and carried the additions into the spec. Three things landed in commit e3a461e , and they land the same day @babyblueviper1 settled the first judgment attestation through GenericCommitRevealSettler (post #18), which is about as good a timing as a spec author could ask for.

Section 7 — ClaimType discriminator

Formalised the enum and the separation rationale:

enum ClaimType {
    ReExecution,  // deterministic computation — objectively disputable
    Attestation,  // authorized signer certifies a result
    Judgment      // subjective assessment — accountable through track record / policy
}


proofSystem answers: what cryptographic mechanism authenticated the artifact?
claimType answers: what kind of accountability model backs the claim?

An off-chain consumer holding only the raw signed struct must be able to determine the accountability model without a registry lookup. The two fields serve distinct routing purposes and MUST NOT be conflated. The field mapping table from the worked example is in the spec.

RecordPointer schema

Added as the typed payload schema that string recordPointer resolves to:

struct RecordPointer {
    bytes32 validatorId;
    bytes32 registryType;     // "evm/registry", "nostr/profile", "offchain/ledger"
    bytes   registryRef;      // registry-specific locator
    bytes   commitmentProof;  // pre-settlement evidence
    bytes   outcomeEvidence;  // post-settlement evidence — MAY be empty before settlement
}


commitmentProof and outcomeEvidence stay separately resolvable — same requirement as the {recordPointer}/commitment and {recordPointer}/outcome sub-path convention already in the spec. The /ledger/19/commitment and /ledger/19/outcome entries from post #18 are the live reference for this shape.

Design note 4 — verify() semantics and three-layer boundary

For ClaimType.Judgment, verify() = true means the verdict is authentically the validator’s and correctly bound to the task inputs. It does not mean the judgment is sound, the action should proceed, or the verdict has been independently endorsed. A settlement contract that gates on the bool has confirmed authenticity — it has not endorsed the judgment.

The three-layer boundary is now explicit in the spec:

IProofVerifier  — authenticates the verdict
IAgentVerifier  — checks validator authorization for the task
recordPointer   — carries accountability: track record + pre-outcome commitment


Verdict weight lives in the accountability record, not in the bool.

Cross-system reference implementation (post #18)

The demo in post #18 also surfaces a pattern worth adding to an appendix: preflight verification of computeCommitmentHash via eth_call before spending gas — verify the hash binding byte-exact off-chain, then commit. The getCommit invariant check (committedAt < revealedAt, record keccak stored) is the on-chain confirmation. I’ll draft the appendix — the periodId = ledger entry number binding and the abi-encoding of JudgmentExecutionAttestation core fields as the opaque bytes record both belong there.

The self-reference, governing the act of settling it, is not a trick. It’s the accountability model working correctly under real conditions. The verdict is a genuine signed /review call, published before the tx fired. That’s the invariant.


One open question for the group before the next revision: JudgmentExecutionAttestation currently carries string recordPointer as a URI locator, with RecordPointer defined as the resolved payload schema. The alternative is to inline the struct directly in the EIP-712 type, which is more explicit but changes the type string. Happy to go either way, want to land on one before the spec goes to review.

1 Like

Really like where this is heading. The WYRIWE three-layer mapping to judgment execution is exactly the right move — what started as an input provenance scheme for AI inference turns out to be a general-purpose commitment layering that works just as well for judgment attestations.

Excited to see this going live on Sepolia with real attestations. The fact that the first settled judgment was one that approved its own settlement is peak self-referential — and honestly a good test case.

2 Likes