Add ERC: Preregistered Acceptance Criteria - #2002
richard7463 wants to merge 14 commits into
Conversation
File
|
| @@ -0,0 +1,600 @@ | |||
| --- | |||
| eip: 8411 | |||
There was a problem hiding this comment.
| eip: 8411 | |
| eip: 8412 |
Assigning next sequential EIP/ERC/RIP number.
Numbers are assigned by editors & associates. Please don't attempt to self assign.
Please also update the filename.
| title: Preregistered Acceptance Criteria | ||
| description: Acceptance criteria and typed evidence obligations frozen on-chain before evidence exists, with verdicts itemized against them | ||
| author: Richard (@richard7463) | ||
| discussions-to: https://ethereum-magicians.org/t/pre-erc-discussion-preregistered-acceptance-criteria/29609 |
There was a problem hiding this comment.
| discussions-to: https://ethereum-magicians.org/t/pre-erc-discussion-preregistered-acceptance-criteria/29609 | |
| discussions-to: https://ethereum-magicians.org/t/erc-8412-preregistered-acceptance-criteria/29609 |
Updated Eth Magicians title with assigned number
|
The Reference Implementation section says the registry contract and conformance vectors are the next deliverable before Review is requested. I would like to contribute the vectors, if that is useful and not already in progress. Proposed shape: a JSON fixture set covering E1-E9 and O1-O4, each case naming the invariant it exercises, plus a minimal conforming registry to run them against so the fixtures are executable rather than descriptive. Either landing in One thing worth settling before the vectors are written, because a fixture can only pin behaviour the text has already decided. The expiry boundary is currently specified with two different comparisons:
In the block where Two lines close it: make E7 and E8 test the same comparison so the windows are disjoint by construction, and give (Also raised on the discussions thread, post #5.) Happy to write the vectors either way. I just do not want to encode a boundary the text has not chosen yet. |
|
Independently reread E3/E7/E8/E9 against @renezander030's point above — the gap is real, not just a boundary-comparison nitpick. At This isn't just a same-block tie — tightening the comparison operators ( Worth pinning as its own invariant (something like E10: |
…testation and waiver schemas; add reference implementation and vectors
…line code span, and link the assets path
…ref lint accepts it
|
Recomputed at head
Reading
One question, not a finding: in the mutation run, disabling E10 alone is caught by a single vector ( |
|
The commit 85da18f (as a parent of dc44de1) contains errors. |
|
Hi Richard, I run Judge Protocol, a deterministic evaluator for ERC-8183 jobs on Arc testnet. It looks like a live instance of the computable corner of this ERC, so I wanted to share it as a data point. How it maps today:
Honest limits: 24 verdicts so far, all on my own test jobs, testnet only, and only computable checks, so it covers the recomputable subset your motivation contrasts with, not judged evidence like photos. If useful, I'd be glad to add an ERC-8412 profile: emit the criteria document with our checks as obligations, register its digest, publish the itemized verdict in your packed encoding, and run it against your vectors. Code: https://github.com/vijaygopalbalasa/judge-protocol |
|
Thanks @renezander030, @babyblueviper1 and @vijaygopalbalasa — and apologies for the slow reply. The boundary finding is correct. Working through it, and then trying to make O1–O4 executable, turned up several related gaps. All fixes are in the latest commit. The interface changed, so anything built against the previous text needs the new signatures. 1. Expiry boundary and finality (the 09-13 finding)
2. Attestation front-runningWith one immutable attestation per preregistration and no on-chain verifier binding, anyone could occupy the slot first with an arbitrary verdict. 3. Supersession is now enforceable
4. Symmetric protectionE11: 5. Making O1–O4 executable exposed undefined pieces
6. Reference implementation and vectorsTo pin the text I drafted a reference implementation: the registry, an executable model, a reference off-chain verifier, 43 on-chain cases (E1–E14, one invariant each), 23 off-chain packages (O1–O5) with real digests and EIP-712 signatures, and a generated Foundry suite. A mutation test switches off each invariant in turn and confirms a vector fails; it also replays the old boundary text and catches the overwrite you reported. On E10 as defence in depth (@babyblueviper1)Yes — deliberate, and your reading is right that E7 already covers the resolve-then-attest order on its own. E10 is there so that finality does not depend on the boundary arithmetic staying correct. The two fixes are separately load-bearing on purpose: if someone later relaxes E7 back to Thank you also for re-running the mutants yourself. That the old text is caught by three distinct cases is the most useful thing anyone has told me about this draft. Judge Protocol (@vijaygopalbalasa)This is the composition case the ERC is written for. A deterministic evaluator on ERC-8183 jobs, with criteria written at Please do add a profile. @renezander030, none of this replaces your offer; it is a first draft so the text was decided before anyone encoded it. I would value your review of the vectors, and especially adversarial cases I have not thought of. Happy to credit you in the vectors, and to discuss co-authorship if you would like to keep contributing. |
…attestation ERC-8412 (Preregistered Acceptance Criteria, draft at ethereum/ERCs#2002) freezes a task's criteria and typed evidence obligations on chain before any evidence exists, then records the verdict itemized against them. A Judge job maps onto it one to one: each check is an obligation, the judge's per-check result is the packed outcomes, and the pass rule is the decisionRule, a standard rule wherever one gives the judge's answer for every combination of outcomes. - src/erc8412.js: JCS digests, packed encodings, the criteria document, evidence bundle and attestation for a ruling, and an independent port of the ERC's reference off-chain verifier (O1-O5). - src/erc8412-live.mjs: one ERC-8183 job end to end on Arc testnet: create, preregister, submit, rule and settle, attest. - Live: the ERC's reference registry deployed unchanged at 0x48c3a1812F2dFc762a80dbD5c65e9C7B0BB25ae4; jobs 186777 (Satisfied) and 186778 (NotSatisfied) are in docs/erc8412/, valid under the reference verifier and ours. - Tests: our digests match the reference for all 69 documents in the ERC's 23 packages; our checker reports exactly each package's expected violations; our packages pass the reference verifier; the decision rule matches the judge on every outcome of 400 random criteria. The CC0 vectors and verifier are vendored unchanged under test/fixtures/erc8412.
|
Thanks Richard. The profile is in: https://github.com/vijaygopalbalasa/judge-protocol/blob/master/docs/ERC-8412.md (built against ff9fbc7). How a Judge job maps:
Live on Arc testnet: I deployed your Against your vectors: a separate JS canonicalizer reproduces your digests for all 69 documents in the 23 off-chain packages, and a JS port of Two things came out of doing it:
Limits, plainly: testnet, my own test jobs, and the live judge does not publish an ERC-8412 record for every ruling yet (the two runs go through a script that drives the same engine). If the interface moves again I will follow it. |
|
Following up on the one limit in my last comment: the live judge now attests ERC-8412 records itself, with no script in the loop.
One implementation hazard, since you asked for adversarial cases. A review of our code found that the judge's criteria hashing, which rebuilt objects in JavaScript by assignment, silently dropped a member named Also, the judge's whole-word matching is now pinned to a Unicode 17.0.0 table instead of the runtime's, so a Still testnet only, and both jobs are my own test jobs. |
… in the mutation test - vectors/offchain/valid-proto-member.json: a valid package whose criteria carry an ordinary "__proto__" member. JCS hashes it like any other key. A JavaScript port that copies with Object.assign or key-by-key assignment drops it (checked in Node: 11 keys -> 10; spread keeps it), so it computes a different digest, and two criteria documents can collide. The generator asserts the member changes the digest. Adversarial case suggested on ethereum#2002 by the Judge Protocol implementation. - tools/mutation_test.py: a 'disable E7 and E10 together' mutation that must be caught by attest-after-expired-unresolved. That case already fails only when both are off; the table now pins it (E10 as defence in depth for resolutions). run_all.py: ALL CHECKS PASSED (43 on-chain, 17 mutations all caught, 24 off-chain packages, Foundry suite in sync). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@richard7463 both follow-ups are in a PR against your branch: richard7463#1 (assets only, no spec or contract change).
|
packages/verify was a README describing what should go there. It is now the thing itself: given what a registry says about one preregistration plus the three published documents, decide whether the recorded verdict stands, and by which rule. The rules are ERC-8412 as drafted in ethereum/ERCs#2002, pinned to commit ff9fbc7e. That pin is part of the deliverable — the PR is still open and the encoding can move — so the specification is archived in docs/reference and the 23 conformance vectors are vendored by scripts/fetch-erc8412-vectors.mjs with a manifest of commit and sha256. What it checks: JCS canonicalisation and the three document digests, the 2-bit packing of obligation flags and outcomes, required constraints per evidence type, evidence that predates the criteria, MET obligations with no covering evidence, waivers that are missing or signed by the wrong key, and verdicts that do not follow from the decision rule. What it cannot check it reports as unchecked rather than refuting: a namespaced decision rule, salted commitments, the media itself. The reason this is worth having at all is in the draft's own vector set: 15 of the refuted packages would be accepted by the registry alone. On-chain, a bundle digest is attested; adequacy is not. That gap is the product. Also adds the anchoring layer the draft does not define: a merkle root over a day of record digests, pairing sorted the way OpenZeppelin's MerkleProof does, so a proof made here verifies on chain and vice versa. The daily job that anchors it comes next; the encoding and the proofs are tested now. 388 tests green (38 new), and the CLI walks all 23 vectors: 23/23 match.
Summary
This PR adds a draft ERC for Preregistered Acceptance Criteria.
Existing agent verification standards reach a verdict by recomputation: derive
the artifact again and compare digests. That only works where a verdict is a
function of the artifact. Most of what agents pay for is judged rather than
computed — a generated deliverable, a human review, an on-site inspection.
This ERC freezes acceptance criteria and typed evidence obligations on-chain
before evidence exists, and requires the final attestation to be itemized
against that frozen list (MET / WAIVED / UNMET / NOT_APPLICABLE). A verdict that
declares an outcome satisfied while a required obligation is recorded UNMET is
rejected at the contract level rather than merely flagged; so is a verdict that
records failure while no obligation is unmet.
Scope is deliberately narrow: registry interface, three off-chain document
schemas (criteria, evidence bundle, attestation), a packed itemized-verdict
encoding, and contract-enforced consistency. Evidence formats, payment, and
identity are out of scope, so it composes with existing standards (ERC-8126
verdicts, ERC-8183 settlement, ERC-8004
identity) instead of competing with them.
Discussion
Ethereum Magicians: https://ethereum-magicians.org/t/erc-8412-preregistered-acceptance-criteria/29609
Validation
category, created, requires).
outcome-switching literature (ICMJE registration, COMPare) cited in the
motivation.
assets/erc-8412/:registry contract, executable model, reference off-chain verifier, 43 on-chain
cases (E1–E14) with a generated Foundry suite, 23 off-chain document packages
(O1–O5), and a mutation test confirming every invariant is exercised.
python assets/erc-8412/tools/run_all.pyruns all checks without dependencies.