ERC-8434: Agent Identity (AID)

Assigned ERC-8434 (2026-09-30).

PR: ethereum/ERCs#2044 · Draft: erc-aid.md · Repo / reference implementation: garyyang-finchip/aid-standard

One paragraph

Agent Identity (AID) is an identity for autonomous agents whose anchor is a blockchain address. Every address is a dormant AID. It becomes active when a live agent demonstrably operates behind it: an ERC-8004 agent bound one-to-one to the address, a liveness signal inside a window, and the agent’s own on-chain behaviour. AID adds a deliberately thin on-chain layer and composes existing registries for everything else. Around that layer it fixes a deterministic resolution model: an AID Document of facets, each tagged with a provenance class, a validity window, a digest or commitment, and an access mode, so that anyone holding only an address can derive the agent’s state, assemble its profile, and re-verify every facet on chain without trusting an indexer.

Why

ERC-8004 gives agents a registration and a reputation channel keyed by a token id. It does not say whether the agent is alive, how the registration relates to the address the agent actually transacts from, or how heterogeneous trust signals (feedback, credit scores, audits, on-chain financial behaviour, skill and task history) are assembled into one verifiable, time-bounded picture. Discovery layers (Google’s Agentic Resource Discovery, DNS-based agent records) expose a trust slot that expects a DID-like identifier and answer “where is it”; AID is meant to sit in that slot and answer “is it alive, who is it, how has it behaved” with on-chain re-verifiability.

Four observations drive the design:

  • The address is already the join key. Every event emitted by ERC-8004 registries and by token-bound skill/task contracts carries the acting address. Anchor on the address and an agent’s history needs no extra registration — it is derived.
  • Liveness is a property, not a flag. A registration is a claim made once. AID makes “alive” a four-state machine with a deterministic on-chain part and one explicit off-chain refinement.
  • Trust decays. Credit and behavioural profiles are only meaningful inside a window; unwindowed credit is rejected at the resolver, not merely discouraged.
  • Financial behaviour is sensitive. The default for financial facets is a commitment on chain plus predicate proofs, with plaintext only to authorised parties.

What is on chain (and what is not)

The AID registry stores only anchor-authorised records: binding to exactly one ERC-8004 (identityRegistry, agentId) (enforced one-to-one in both directions; the anchor must be the agent’s agentWallet or owner; direct call or EIP-712 bindWithSig, ERC-1271 for smart accounts), heartbeat / liveness window, document URI + digest, self-declared facet pointers, and retirement (irreversible, releases the binding so a successor can take over the same agent, optional successor pointer). No owner, no upgrade path, two immutable liveness bounds.

States: DORMANT → ACTIVE ⇄ STALE, → RETIRED. state() is a pure function of on-chain data (binding intact, ownerOf/getAgentWallet still pointing at the anchor, lastSeen + window >= now). A resolver adds exactly one downgrade: registration file "active": false → STALE. Wallet drift or token burn degrades to STALE on the next read with no AID transaction — identity cannot be silently transferred.

Everything else is composed, not duplicated:

Content Where it lives
registration file, endpoints, agent wallet ERC-8004 Identity Registry
raw per-interaction feedback, open evaluation terms (tag1) ERC-8004 Reputation Registry
aggregated credit scores, trust levels, audits, profiler outputs, ZK predicates an assertion registry — the draft specifies the minimum it needs (subject = account(chainId, anchor), resolve/check, hash-pinned schemes, ATTESTED/PROVED modes, expiry); the reference registry is the Know-Your-Agent (KYA) Framework draft, whose interface it matches as written
skill / task history derived from token-bound skill and task-tender contract events by role (informative)

Facets and provenance

Each facet in the AID Document carries facetType (namespaced URI, on-chain key keccak256), provenance, issuer, validUntil (mandatory), digest, access, resolver. Provenance is the part I’d most like scrutiny on, because “tamper-proof” means four different things and hiding that distinction is how trust systems get gamed:

Class Produced by Guarantee Reader obligation
SELF the anchor integrity only never present as verified
OBSERVED anyone, from chain events under a hash-pinned algorithm reproducible recompute or spot-check
ATTESTED a third-party issuer issuer accountability filter by trusted issuers
PROVED a prover against a commitment cryptographic verify the proof

Core facet types: aid:core/identity/v1, aid:core/kya/v1, aid:finance/observed/v1 (frequency, volume, direction, counterparty dispersion, leverage, derived intent — default access ZK), aid:behavior/* (open namespace: runtime, skills, industries, protocols, task traits), aid:skills/erc8338/v1, aid:tasks/erc8414/v1, aid:review/erc8004/v1. Access modes PUBLIC | GATED | ZK.

What is in the repo

  • AIDRegistry.sol reference implementation (no dependencies; self-contained EIP-712 / low-s ECDSA / ERC-1271), 22 behavioural tests (binding uniqueness both ways, state transitions, wallet drift, burn, facets, bindWithSig for EOA and 1271 anchors incl. replay/expiry, retirement + successor, end-to-end resolver)
  • JSON Schemas for the AID Document, facet envelope and five core facet content documents
  • Test vectors (facetType keys, JCS digest, EIP-712 Bind digest, account subjectKey, interfaceId 0x72750a54) and resolver fixtures
  • A reference resolver implementing the normative seven-step resolution algorithm, RPC or fixture mode

Sepolia deployment and worked examples (binding the ERC-8004 demo agent used in the KYA thread) are next.

Questions I’d like feedback on

  1. Anchor = address, binding strictly one-to-one. ERC-8004 lets one owner hold many agent tokens; AID says an identity that fans out to several tokens is not the identity of an agent, and owners with several agents should give each its own wallet or ERC-6551 account. Does anyone see a legitimate case for one anchor ↔ many agents that this breaks?
  2. On-chain / off-chain boundary of “alive”. state() ignores the registration file’s active flag (unreadable on chain) and resolvers apply it as the single downgrade. Is one explicit refinement the right amount, or should the flag be mirrored on chain via setMetadata?
  3. Assertion registry as an interface rather than a hard dependency. The draft specifies the minimum (subject encoding, resolve/check, pinned schemes, two modes, expiry) instead of requiring a specific registry ERC. Too loose, or the right shape for something several registries could satisfy?
  4. Mandatory validUntil and rejection of unwindowed credit. Strict by design; is there a facet class where an open-ended validity is legitimately needed?
  5. Naming. No ERC proposal uses “Agent Identity (AID)” as its name or acronym, though “agent identity” appears descriptively in other agent proposals and some agent registries use aid as an identifier prefix. A DNS discovery project uses the same acronym for “Agent Identity & Discovery”; the two layers are complementary and designed to point at each other. Objections?
  6. Registry discovery. One AID registry per chain, deterministic address recommended so a resolver can find it from chainId alone. Should the DID method spec (companion, did:aid:eip155:{chainId}:{address}) carry the per-chain registry list instead?

Context: this is the fourth piece of a set — ERC-8338 (skill supply side), ERC-8414 (task demand side), ERC-8419 (KYA trust assertions), and now AID as the identity the other three hang off. Happy to be told where it overlaps something I’ve missed.

1 Like

@garyyang-finchip, one gap in §7 that matters for any facet built on third-party judgment. ATTESTED records who stands behind a claim, not when it was committed relative to what it grades. An audit or review written after the outcome was known reads exactly like one written before, and for a trust signal that’s the difference between a prediction and a recollection. Validity windows (§8) bound how long a claim counts; they don’t show it existed before the behaviour it describes.

A suggestion, optional and facet-level: committedAt: { anchor, proof }, a commitment of the facet digest to a clock the issuer doesn’t control, such as block inclusion, an RFC 3161 token or a Bitcoin OpenTimestamps proof. The resolver rule: an ATTESTED facet about behaviour in a window may be presented as pre-outcome only if committedAt precedes the end of that window. Otherwise it’s integrity-only for timing, and the resolver says so, the same way §7 already refuses to let SELF read as verified.

A live case for aid:tasks/erc8414/v1: the ERC-8414 companion now in review (task-token-standard#2) settles a tender only with an invinoveritas verdict verified on-chain, BIP-340 in about 12k gas. It’s bound to the kernel’s deliverable hash, the task document (tdHash) and the outcome, and anyone may submit it. The same verdicts are Bitcoin-anchored before their outcomes exist, recomputable at invinoveritas verdict ledger — a public record of being right (and honestly wrong). So a judged task in that facet can point at a judgment that is both on-chain-verifiable and provably pre-outcome, the property the class table can’t express today.

If it’s useful, I’ll draft the field and a vector pair against your reference resolver: anchor before the window’s end → may be read as pre-outcome; anchor after → integrity-only.

The address anchor looks like the right place to put identity, and the ERC-8004 binding looks like the right live relation. The thing I would make explicit is that a binding becoming true again cannot retroactively authorize what happened while it was false.

Suppose A is bound to agent T and is ACTIVE. The owner/wallet relation then moves away from A, so A becomes STALE. While that relation is broken, activity, feedback or validation is recorded against T under another controller. Later the relation returns to A and the current predicate becomes true again.

That should make A ACTIVE again going forward. It should not make the evidence produced during the gap evidence about A.

So I don’t think boundAt by itself closes the history question once authority can leave and later return. What matters is the authority interval. Any ERC-8004-derived fact attributed to an AID through that binding should only count if its observation or issuance belongs to an interval in which (anchor, registry, agentId) was actually valid.

I don’t think that necessarily means AID has to store an epoch itself. A resolver could reconstruct the intervals from authoritative ERC-8004 ownership/wallet history. But the semantics need to be explicit: re-establishing the same relation opens a new authority interval; it does not authorize the gap.

That also seems to settle where source-token binding belongs. ERC-8323 is useful because it keeps immutable source provenance separate from current ownership validity. The provenance can survive every transition. The live predicate tells us whether that relation is valid now. Neither makes evidence from an interval without authority belong to the identity when the relation later becomes true again.

If that interval rule is explicit, the stack closes for me: the address is the identity anchor, the ERC-8004 binding supplies live authority, source-token binding supplies provenance, facets carry evidence, and ERC-8419 carries trust conclusions. Evidence can survive the transition. Authority has to be established again on the other side.

@babyblueviper1 Agreed that this is a real gap. Provenance (§8) says who stands behind a facet and the validity window (§9) says for how long; neither says when the claim existed relative to the behaviour it grades, and for a judgment that is the difference between a prediction and a recollection.

I took it roughly as you describe, with three refinements:

  1. Orthogonal to provenance, not a fifth class. Timing matters for ATTESTED facets, but equally for OBSERVED and PROVED commitments, so it is modelled as an optional envelope member — committedAt: { anchor, proof } — that a resolver verifies and reports separately. A facet without a valid committedAt keeps its provenance and is reported as timing unverified, never as pre-outcome, the same way §8 refuses to let SELF read as verified.
  2. The facet needs an explicit subject window. “Precedes the end of the window” needs the window the claim is about, which is not validUntil (how long the claim counts). So the envelope gains subjectWindow: { from, until } next to committedAt, and the rule is: pre-outcome iff committedAt is verifiably earlier than subjectWindow.until.
  3. Existence is not exclusivity. A timestamp proves those bytes existed at that time. It does not prove the issuer didn’t timestamp several contradictory verdicts and reveal only the one that turned out right. For a facet to be read as pre-outcome I think its commitment has to sit in a public, append-only log that a resolver can enumerate per (issuer, subject, facetType, subjectWindow), so it sees every commitment the issuer made, not just the one presented. Curious whether you see a lighter way to close that.

The rule stays anchor-agnostic: block inclusion, RFC 3161 and OpenTimestamps are all admissible anchor kinds, and the resolver checks the proof for whichever is used.

This is now in #2044 together with the renumbering: subjectWindow and committedAt in the envelope (§6), the timing rule and the exclusivity caveat in §8, and the resolver step in §11. A vector pair against the reference resolver along the lines you suggest — anchor before subjectWindow.until → may be read as pre-outcome; anchor after → timing unverified — would be welcome; I’ll merge it into the assets. The ERC-8414 companion in task-token-standard#2 I’ll review in that repository.

@chugarchugarr Agreed, and it is now normative. The earlier text only said what the binding means now; it did not say what a binding that comes back means for the time it was gone, and a resolver could read it either way.

This is now in the PR text (§3 Authority intervals and Takeover, §11 steps 4 and 7, §12):

  • Authority intervals. An anchor holds authority over a bound agent exactly while the binding predicate holds — ownerOf(agentId) == anchor or getAgentWallet(agentId) == anchor, with the binding record intact. Each time the predicate becomes true again, a new interval opens. Re-establishing the relation never authorizes the gap.
  • Attribution. Evidence attributed to an AID through the binding — anything keyed by agentId: ERC-8004 feedback and validation, and the registration file itself — counts only if its issuance or observation falls inside an authority interval. Evidence outside every interval is still reported, but as not attributable to this AID.
  • No new on-chain state. state() stays the current-time predicate it is today. Resolvers reconstruct intervals from the bound registry’s event history, and ERC-8004 makes that fully observable: ownership changes are Transfer events, every wallet change is a MetadataSet event under the reserved agentWallet key, and a transfer clears the wallet, so a wallet relation can never move away without an event marking the boundary.

Two choices in that revision I’d like checked:

  1. Evidence keyed by the address itself. Skill and task records derived from events where the anchor is the actor are the address’s own acts, so impersonation is not the issue. But during a gap the address is not demonstrably operating as this agent. My inclination is to keep them in the profile, marked as outside an authority interval, rather than drop them — they are still facts about the address. Views welcome.
  2. A stale binding should not hold the agent hostage. In the earlier reference registry a binding whose predicate had failed still blocked the agent’s new controller from binding it until the old anchor unbound or retired. The registry now lets a qualifying new anchor take the binding over once the old predicate is false, emitting Unbound for the old anchor — which also gives intervals a clean on-chain boundary. That is a behavioural change to bind (interface and interfaceId unchanged), covered by regression cases for takeover, refusal while the old predicate still holds, and the A → gap → A round trip.

On ERC-8323: agreed on the principle — immutable source provenance and current ownership validity are different facts and neither should stand in for the other. AID stays agnostic about which provenance mechanism a deployment uses; the interval rule is what keeps either from being read as evidence about the gap.

What is the recovery path if an EOA anchor loses its key before calling unbind or retire?

The ERC-8004 owner could move agentWallet to a new address. But the old AID would become STALE while still occupying the one-to-one binding. Would the new wallet’s bind then revert with AgentAlreadyBound?

Does this leave recovery dependent on the lost key even after control has been restored at the ERC-8004 layer? Should there be a way to release a binding once the old anchor is neither the owner nor the agent wallet?

@garyyang-finchip all three refinements read right, and (2) is the one I’d missed: “precedes what?” needs subjectWindow.until, not validUntil.

On exclusivity, a lighter way than a global log: make committedAt.log the issuer’s own append-only hash chain, declared once, and make the chain head hard to fork.

  • Each commitment the issuer makes is an entry in its log, head_n = H(content_hash_n || head_{n-1}). The facet’s committedAt.log names the log and the entry position.
  • The issuer signs each head and publishes it to several places it doesn’t control (independent relays or witnesses), and periodically anchors a head (OTS, block, RFC 3161).
  • A resolver walks the log up to an anchored head, enumerates every entry for (issuer, subject, facetType, subjectWindow), and checks the presented facet is the only one. A hidden contradictory commitment then requires either omitting an entry, which breaks the chain, or keeping two heads at the same position, which is equivocation that any witness holding the other head can show.

So exclusivity reduces to non-equivocation of one head, which a few independent witnesses give cheaply. The remaining hole is a second, undeclared log, which is closed by binding the log to the anchor: the AID Document names it, so a commitment in any other log isn’t the issuer’s.

This runs in production today. Our verdict ledger chains every entry as sha256(content_hash | prev_head), signs heads to multiple Nostr relays, and OTS-anchors entries. A third party (VLC-1, @MattyIceMatrix) recomputed 230 entries from public data with zero breaks and cross-checks the signed head. Its verdicts are published before their outcome. Entry 264, for example, was anchored in Bitcoin block 969156 on 09-29 and settled against the IETF filing on 10-01.

On the vector pair: the resolver verifies block anchors but leaves ots to a plug-in, so I’ve written one, verifiers/ots.js (Node stdlib, offline). It walks the OpenTimestamps proof to a pinned 80-byte Bitcoin header and returns that block’s time. It verifies the real proof above (block time 1790685635), and every byte flip on the Bitcoin path is refused. The pair itself uses a real facet whose digest I stamped today at 11:34Z. As soon as it confirms in a Bitcoin block (usually within a few hours), I’ll open a PR on aid-standard with three things:

  • the verifier;
  • the before/after twins, pre-outcome and integrity-only (committed after subjectWindow.until);
  • a negative where the proof is over a different digest.

@SergeevDmitry Recovery no longer depends on the lost key. That case is what §3 Takeover now covers: when bind / bindWithSig targets an agent already bound to another anchor, the registry checks the predicate for that anchor; if it no longer holds, the stale binding is released (Unbound for the old anchor) and the caller is bound in the same transaction. In your example the owner moves agentWallet to W2, the old anchor A is neither owner nor wallet, and W2’s bind takes the binding over instead of reverting. The reference registry implements it, with regression cases for takeover, refusal while the old predicate still holds, and the A → gap → A round trip.

Three boundaries worth stating:

  1. If the lost-key anchor is the owner, the predicate still holds through ownerOf and nothing can displace it — but then control has not been restored at the ERC-8004 layer either, since only the owner can transfer the token or move the wallet. AID mirrors ERC-8004 rather than overriding it, which is why §3 recommends the agentWallet, not the owner, as anchor.
  2. After a takeover A is not retired: it ends DORMANT, with its facets and event history retained, and without a successor pointer, because only A’s key could set one. Continuity is visible on chain (Unbound(A) and Bound(W2) in one transaction, plus the ERC-8004 MetadataSet for agentWallet), but it is informational: as with retirement, resolvers must not merge the two histories. I’ll make that explicit for takeover too.
  3. If the key was stolen rather than lost, whoever holds A cannot re-bind after the takeover, because the predicate fails for A; and under the authority-interval rule nothing done to the agent after the wallet moved is attributed to A.
1 Like

@garyyang-finchip the ots verifier and fixture from my last post are up as a PR: https://github.com/garyyang-finchip/aid-standard/pull/1

It plugs into ctx.verifiers.ots. The fixture is a real Bitcoin-anchored proof (block 969451) and checks offline, with the three timing outcomes timingOf defines: pre-outcome; integrity-only because it was committed after subjectWindow.until; and integrity-only because the proof is over other bytes. Six mutated proofs, including an altered merkle root with a recomputed block hash, are all refused (node scripts/ots-timing-check.js, 9/9).

One limit the PR states: committedAtVerified is the block header’s timestamp, which Bitcoin only keeps to within about two hours, so an until closer than that to the block time isn’t safely decided by it. If you’d like the spec to say so, I’ll add a sentence to the committedAt section.

@babyblueviper1 That is a better shape than a global log: it reduces exclusivity to non-equivocation of one head, which a few independent witnesses give cheaply. I’ve added it to §8 as an informative reference commitment-log profile — issuer-owned hash chain, signed heads published to declared witnesses, periodic anchoring, resolver enumerates up to an anchored head — with the normative rule staying anchor- and log-agnostic.

Three details the profile pins down, because without them the hole you closed reopens:

  1. The issuer declares the log, not the subject. A log named in the subject’s AID Document doesn’t stop an issuer from keeping a second one. Every issuer is itself an address — a dormant AID at least — so the natural place is the issuer’s own AID Document (new top-level member commitmentLog) or, for facets issued through an assertion registry, the scheme descriptor pinned by schemeHash. The declaration has to be provably earlier than subjectWindow.until, otherwise an issuer could declare a cleaned log after the fact. A commitment in any log not declared that way is not read as pre-outcome.
  2. Enumerable without disclosure. To enumerate per (issuer, subject, facetType, subjectWindow) the entries need those keys, not just content_hash. Each entry carries a deterministic index tag, H(subject ‖ facetType ‖ subjectWindow), next to the content commitment, so a resolver can check there is exactly one entry per tag up to the anchored head without seeing GATED or ZK content. committedAt.log is now { uri, position }.
  3. Which time is proven. The proven time of a facet is the anchoring time of the first anchored head that includes its entry, not anything the entry states about itself; and the witness set, with how a resolver queries it, is declared together with the log — otherwise non-equivocation can’t actually be checked.

The reference resolver now applies the check when it can read the declared log: pre-outcome is kept only with exactly one matching entry (exclusivity: unique); duplicate, missing or undeclared downgrade to integrity-only; no log access reports unchecked without downgrading. There’s a fixture covering the three outcomes.

The PR on aid-standard is welcome — verifier, twins and the different-digest negative is the right set. I’ll review it there; one thing to document in it is that the pinned header is the verifier’s trust root. The verifier stays an optional plug-in in the repository rather than part of the ERC assets, so the normative text doesn’t depend on any one anchor.

One edge case for the facet model: a facet derived from an oracle outcome that is proposed, then disputed, then superseded by a later answer. Its digest still verifies and its validity window may still be open, so a resolver following the current rules would read it as current.

For Polymarket markets resolved through UMA, the median time to resolution was 3.3 h after a first dispute and 88.5 h after a second (1 Jan to 2 Oct 2026). A validity window shorter than that can expire before the outcome is final, and a longer one can outlive a superseded answer.

Proposal: give outcome-type facets an outcomeState (proposed, disputed, final) and an optional supersedes pointer to the prior facet digest. Resolvers MUST NOT treat a superseded facet as current even inside its window, and SHOULD keep it enumerable for audit. We use this shape in an ERC-8004 outcome profile: GitHub - predgeAI/erc8004-outcome-validator: Predge as an ERC-8004 Validation Registry validator — resolved-outcome-vs-claim: signed outcome attestation mapped to a real validationResponse (responseHash bound to the signed evidence). · GitHub

@babyblueviper1 Merged aid-standard#1 — thanks for the real proof, the six mutations and the trust-root note. Two changes on merge: the verifier and its fixtures live only in the repository (tools/, assets/erc-aid/), not in ercs-pr-package, so the ERC assets stay anchor-agnostic; and the fixture carries the stamped digest without the underlying document or links, so the reference implementation stays issuer-neutral.

On precision: agreed, and it isn’t specific to ots. Every anchor’s clock has a tolerance — 12 seconds (one slot) for an Ethereum block, roughly two hours for a Bitcoin header, the stated accuracy for an RFC 3161 token — so §8 now says a resolver reports pre-outcome only when the proven time plus the anchor’s tolerance is earlier than subjectWindow.until, and integrity-only with the reason otherwise. The reference resolver applies the same table, and there is a within-tolerance negative in the vectors.

@predge-ai Agreed that this is a real gap. A facet’s digest and window say nothing about whether its issuer later replaced it, so a resolver following the current rules reads an overturned answer as current. Assertions issued through an assertion registry are already covered — §5 has resolve/check ignore superseded and revoked assertions — but facets carried in the AID Document itself had no way to say it.

I’ve taken your proposal in two layers:

  1. Supersession is generic, not outcome-specific. Audits get reissued, scores get updated, reviews get corrected. So the envelope gains an optional supersedes (the digest of the facet it replaces) for any facet type, and the resolver rule is yours: a superseded facet MUST NOT be read as current even inside its window; it stays enumerable as history, marked with what superseded it.
  2. Finality is domain-specific. proposed / disputed / final is the shape of an optimistic-oracle flow; another oracle or another domain will have different states. The core envelope only carries finality: provisional | final (absent = final), with the state machine itself defined by the facet type’s content schema. A resolver may list a provisional facet as current but MUST NOT present it as final. That also removes the window dilemma: a provisional answer can carry a short validUntil and be superseded by the final one, instead of the window having to guess how long a dispute will run.

One thing the proposal needs in order to hold: supersession has to be discoverable from the issuer’s side, not the subject’s. The AID Document is maintained by the anchor, and an anchor whose favourable answer was overturned has every reason to keep presenting the old facet and leave the new one out. So a supersession only counts if it comes from the same issuer and appears in that issuer’s declared commitment log (§8) under the same index tag, with supersedes pointing at the earlier entry; a resolver walking the log sees it whether or not the subject presents the replacement. That also refines the exclusivity rule: two entries under one tag are still equivocation, unless the later one explicitly supersedes the earlier, in which case the resolver follows the chain and only the latest unsuperseded entry can be current. A “supersession” by a different issuer isn’t one.

This is now in #2044: supersedes and finality in the envelope (§6), the resolver rule in §11, the supersession-aware exclusivity rule in §8, with vectors for the three cases — a normal supersession chain, a subject presenting only the superseded facet while the issuer’s log shows the replacement, and a cross-issuer supersession being refused.

@garyyang-finchip thanks. Confirmed on aid-standard at 07702ec8: the verifier, the fixtures and the timing check are in place. Keeping them out of ercs-pr-package is the right split. The ERC stays anchor-agnostic and verifiers plug in from the repository. The per-anchor tolerance table also makes the pre-outcome rule hold for any anchor, not only OTS.

A question on the author line. The §8 commitment-log profile and the tolerance rule came out of this thread, and the merged verifier implements them. If you think that is a fair basis, I’d be glad to be listed on 8434 as babyblueviper1 (@babyblueviper1). It’s your call. Either way, I’ll keep the OTS verifier passing the vectors as §8 changes.

Thanks, both layers make sense, and the issuer-side point is the important one: a subject that benefits from the old answer should never be the only path to the supersession. Requiring it in the issuer’s commitment log under the same tag closes that.

One small suggestion for #2044: let a provisional facet name the event that will finalize it (e.g. an oracle request id or a block-bounded window), so a resolver can tell “provisional, still open” from “provisional, issuer went silent”. Happy to add an oracle-outcome vector from a real disputed Polymarket market to your test set if useful.

@garyyang-finchip @predge-ai one gap in the supersession rule, with a fix in aid-standard#2.

Nothing records when a supersession happened. So an issuer can commit a final claim A before the outcome, see it come out wrong, and then append B under the same tag with supersedes = A. The resolver shows B as current and A as plain history, and A’s pre-outcome timing is dropped. Every wrong call can be withdrawn this way after the fact, which leaves a record of only right calls. That defeats the reason to read pre-outcome at all.

§8 already defines what’s needed: a proven time is the first anchored head that includes the entry. The PR applies that rule to the superseding entry too.

  • A superseded facet keeps its own timing.
  • The resolver reports supersessionTiming: before-outcome, not-before-outcome (using the same per-anchor tolerance), or unknown.
  • A final facet that was pre-outcome and was replaced not-before-outcome is marked reversedAfterOutcome.
  • A provisional facet replaced after the outcome is the dispute flow predge-ai described, so it is not flagged. Neither is a facet that was committed late in the first place.

The PR adds §11 step 7 wording, one sentence in §8, a “Late supersession” security note, and a fixture with 6 cases. npm test gives 31/31, the existing fixtures regenerate byte-identically, and 6 mutants each fail the new test.

Why is Agent ID chain-scoped?

@babyblueviper1 Thanks for checking the merge, and for offering to keep the OTS verifier aligned with §8 — the verifier is what makes the profile checkable, and that matters.

I’m keeping the author line of ERC-8434 unchanged, and recording design and implementation contributions separately from it. The next revision of #2044 will acknowledge your contributions to the commitment-log profile, the anchor-tolerance rule and the supersession-timing rule explicitly in the text, and the aid-standard README will credit you as the author of the ots verifier.

For this proposal I’m reserving co-authorship for a shared role in developing and maintaining the specification as a whole, rather than for contributions to particular parts of it, however substantive. That’s a choice about how this document is maintained, not a judgement on the weight of your input. I appreciate the work you’ve put in and would be glad to keep collaborating on that basis.

On the late-supersession gap: agreed, and the fix is right — it goes into the next revision of #2044. From aid-standard#2 I’ll take the resolver change, the fixture and the test; I’ve asked on the PR to drop the edits to ERCS/erc-aid.md and ercs-pr-package/, since the normative text is mine to write and the filing variant is generated, never edited by hand.

@predge-ai Agreed — “provisional, still open” and “provisional, issuer went silent” should not look the same to a relying party. I’d keep the core generic again: the envelope gains an optional finalizeBy (the time by which the issuer commits to publish a final facet or a supersession) and an optional opaque finalizationRef (an oracle request id, a market id — its format belongs to the facet type’s content schema; the core doesn’t parse it). A resolver then reports a provisional facet as open before finalizeBy and overdue after it when the issuer’s log shows no supersession, and leaves what to do with an overdue answer to the relying party, same as timing and attribution. A block-bounded window fits the same field once the facet type maps it to a time.

A vector from a real disputed market would be welcome in aid-standard, on the same terms as the OTS fixture: public on-chain data only, no issuer branding, in the repository rather than the ERC assets.

@wjmelements Because the facts an AID is built from are chain-local.

  • Control of an address is per chain. An EOA key is the same everywhere, but a contract or smart-account anchor at the same address can be undeployed on another chain, or deployed there by a different factory with a different owner; EIP-7702 delegations are per chain as well. “Same address” does not imply “same controller”.
  • The binding and the state are per chain. The ERC-8004 binding is a (registry, agentId) on one chain, and state() is computed from that chain’s ownership, wallet and heartbeat. A contract can’t read another chain’s state, so a chain-agnostic AID would have no on-chain state to gate on.
  • The evidence is per chain. Skill, task and feedback history are events on a specific chain.

So cross-chain identity is opt-in rather than assumed: an AID Document lists the anchor’s AIDs on other chains in alsoKnownAs. A resolver should treat such a link as real only when it is mutual — the other chain’s AID Document lists this one back — since each document can only be set by its own anchor, so a mutual link shows the same controller on both sides. That is the same point-back rule the spec already applies to discovery entries; I’ll make it explicit for alsoKnownAs in the next revision.